What is Ada.Containers.Synchronized_Queue_Interfaces good for?

I have been looking at Ada.Containers.Synchronized_Queue_Interfaces and how it is used in Ada.Containers.Bounded_Synchronized_Queues and I wonder why they are two different packages.

There is no way to extend the interface or provide a different implementation. All the advantages of using an interface seem to be unavailable because are linked inseparably. There is no way to provide a different implementation.

What is the point. Just reusing 15 lines of code so you don’t need to repeat them in the 5 queue packages?

Background: I’m woking on a Queue with an “finished” / “end of queue” status and it seems the only way to do is to implement it from scratch.

Protected types and task types are non-tagged. It was a design flaw of Ada 95 when tagged types were introduced first. It is still here, because it seems more important to introduce square brackets and executable code in declaration… :disappointed:

I do not know what you are trying to do, but making a queue synchronized is a bad idea anyway. For a protected object protected action might be too heavyweight. For a task the overhead is too big.

Depending on the queue type it is better to have it tagged and use locking or no locking according to the use case. For example, 1-1 FIFO needs no locking at all. Otherwise, it is usually some sort of mutex/event combination you could attach to the queue to perform interlocking/signalling,

Synchronized_Queue_Interfaces is a thread-safe interface for a queue that operates on a generic type, that you feed into a another package type to make an unbounded or bounded queue. I can’t elaborate personally more on the design, other than there’s an explanationale in the Ada 2012 Rationale.

Regarding “How useful is this?” I use it in my septum project to track remaining files to load and analyze as they’re found by the directory walker. It’s a piece of code that in Ada seems to work perfectly with almost no effort compared to implementing the same thing in the other languages to which I’ve ported the project.

3 Likes

Can you expound on this a bit more. I was able to extend the interface separately with my own custom type (I’ll give a stub example below). I know extending interfaces with protected types is not intuitive due to how they handle the self/this variable and also because of interchangeability of entry/procedure.

I’m hoping maybe it’s just a typo somewhere for you.

In the general case, having a synchronized queue opens you up the world of task and synchronized features of Ada. Like you can have a queue and try to put an item on it but also have a timeout in case it stays full too long.

    v : Custom_Queues.My_Queue;
begin
    select
        v.Enqueue(10);
        Put_Line("Enqueued");
    or delay 3.0;
        Put_Line("Timed out");
    end select;

One reason to have the two packages separated is so that you can define multiple different protected object implementations with different default priorities and still use them all with the same interface. Don’t know how useful that is in practice

That said, I agree with you that it seems really odd to split them. I’d have to go back and look at the ARG meeting minutes or AI discussion on it to see the exact reasoning.

Stub example:

with Ada.Text_IO; 
    use Ada.Text_IO;
with Ada.Containers.Synchronized_Queue_Interfaces; 
    use Ada.Containers;

procedure jdoodle is

    package My_Queue_Interfaces is new Synchronized_Queue_Interfaces(Integer);
    
    package Custom_Queues is
    
        type Data_Array is array(Positive range <>) of Integer;
    
        protected type My_Queue is new My_Queue_Interfaces.Queue with
            overriding entry Enqueue(New_Item  : in     Integer);
            overriding entry Dequeue(Element   :    out Integer);
            overriding function Current_Use return Count_Type;
            overriding function Peak_Use return Count_Type;
        private
            Dummy : Integer := 0;
        end My_Queue;
    
    end Custom_Queues;
    
    package body Custom_Queues is
    
        protected body My_Queue is
        
            entry Enqueue(New_Item : in Integer) when True is
            begin
                null;
            end Enqueue;
            
            entry Dequeue(Element : out Integer) when True is
            begin
                null;
            end Dequeue;
            
            function Current_Use return Count_Type is (0);
            
            function Peak_Use return Count_Type is (0);
            
        end My_Queue;
        
    end Custom_Queues;
    
begin
    null;
end jdoodle;

Isn’t Synchronized_Queue_Interface also necessary for Ada.Containers.Unbounded_Priority_Queues? That would see a reason to separate it from Bounded_Synchronized_Queues, or am I off base? (I often am! :grin:)

Yep yep. I think the OP was wondering why they chose to break out the interface separately rather than just making the different queues without interfaces involved at all. I was guessing it had to do with being able to develop code that could use any of those different types of queues mixed together (including your own custom implementations). But that is only a guess on my end.

I don’t know if any answers are in here, but here is the discussion on it when they were developing it for the language: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0159-1.txt?rev=1.20

The discussion starts pretty far down. You can search the page for the first instance of
****************************************************************
to find where the discussion starts. It was all done over email there.

1 Like

Synchronized_Queue_Interfaces is good for two things:

  1. Making the standard queue packages more complex, harder to understand, and more difficult to use
  2. Serving as a cautionary example against the use of programming by extension