1

This is a very basic protected bounded buffer in Ada, exactly as it is presented everywhere, even in text books. (This is a part of a bigger thing, but I simplified code down to bare minimum, where it reproduced the behavior). It seems to work just fine if I have 1 task feeding it and the "main" body is reading from it. But it blocks on the 1st Get if I use 2 tasks - Putter and Getter, just as below.

with Ada.Text_IO;use Ada.Text_IO;
procedure test_buffer is

    maxItems : constant Positive := 10;
    type Index is mod maxItems;
    maxCount : constant Index := 9;
    type ItemArray is array(Index) of Integer;

    protected Buffer is
        entry Put(X : in  Integer);
        entry Get(X : out Integer);
    private
        First, Last, Count : Index := 0;
        buf : ItemArray;
    end;

    protected body Buffer is
        entry Put(X : in  Integer) when Last - First < maxCount is
        begin
            Put_Line("Put X="&X'Img & "; First="&First'Img&", Last="&Last'Img&", Count="&Count'Img);
            buf(Last) := X;
            Last := Last + 1;
            Count := Count + 1;
        end;
        --
        entry Get(X : out Integer) when Last - First > 0 is
        begin
            Put_Line("Get X="&X'Img & "; First="&First'Img&", Last="&Last'Img&", Count="&Count'Img);
            X := buf(First);
            First := First + 1;
            Count := Count - 1;
        end;
    end;

    task Putter;

    task body Putter is
    begin
        Put_Line("Putter started");
        for i in 0 ..25 loop
            Buffer.Put(i);
        end loop;
    end;

    task Getter;
    task body Getter is
        X : Integer;
    begin
        Put_Line("Getter started");
        loop
            Put_Line("requesting X..");
            Buffer.Get(X);
            Put_Line("got X="&X'Img);
        end loop;
    end;

--     X : Integer;
begin
--     loop
--         Buffer.Get(X);
--         Put_Line("got X="&X'Img);
--     end loop;
    Null;
end test_buffer;

This spits out the following output:

$ ./test_buffer 
Putter started
Put X= 0; First= 0, Last= 0, Count= 0
Put X= 1; First= 0, Last= 1, Count= 1
Put X= 2; First= 0, Last= 2, Count= 2
Put X= 3; First= 0, Last= 3, Count= 3
Getter started
Put X= 4; First= 0, Last= 4, Count= 4
Put X= 5; First= 0, Last= 5, Count= 5
Put X= 6; First= 0, Last= 6, Count= 6
Put X= 7; First= 0, Last= 7, Count= 7
Put X= 8; First= 0, Last= 8, Count= 8
^C

As you can see, Get is never getting executed, likely due to the barrier not being re-evaluated. However if I uncomment the main body and comment out the task (or even simply uncomment the main - then I get concurrent reads), it proceeds just fine with all reads and writes..

I cannot seem to find any rule that would stall the barrier on Get (2nd task is external, so no, its not the no reevaluation on internal calls of protected object).

Am I missing something obvious here or is this a bug?

  • 2
    Works for me (admittedly, that doesn't prove absence of a bug) ... gcc-6 (FSF) in Debian Stretch. What's your OS and compiler version? Here, both tasks started before the first Put, which may make a difference. –  Dec 12 '17 at 17:59
  • Works for me too (GNAT 4.9.2 on 64 bit Debian/Jessie). But you should be aware that the I/O calls are "potentially blocking" and thus illegal inside protected operations. – Jacob Sparre Andersen Dec 12 '17 at 18:06
  • No, doesn;t work here still, on another computer either. And it gets even weirder. Looking at how this is so standard, I instantiated GNAT.Bounded_Buffers (which has pretty much exact same code in its body) and it hangs even sooner, with both Insert and Remove (Put and Get in my code) hanging right off.. Like this: – George Shapovalov Dec 12 '17 at 18:12
  • Err, apparently it submits on Enter, sorry for broken comment. Anyway, here is sample output: – George Shapovalov Dec 12 '17 at 18:13
  • $ ./test_buffer Getter started requesting X.. Putter started got X= 0 ^C The systems (both) nrun Gentoo Linux, updated within month or so. Ada in question: gnat-gpl-2017 (gcc-6.3.0). The original (mine) code at least runs until Puts are blocked by buffer size and even occasionnally do 1-2 reads. So, really looks more like a race condition in gnat realtime.. – George Shapovalov Dec 12 '17 at 18:16

1 Answers1

4

The ARM section 9.5.1 states During a protected action, it is a bounded error to invoke an operation that is potentially blocking. and further on it clarifies this with the statement Certain language-defined subprograms are potentially blocking. In particular, the subprograms of the language-defined input-output packages that manipulate files (implicitly or explicitly) are potentially blocking. The Put_Line procedure calls in your protected entries are potentially blocking and should be removed from the entries.

Jim Rogers
  • 4,822
  • 1
  • 11
  • 24
  • Well, they were not there to begin with (when it was blocking). I added them to trace what's going on.. But this issue has been resolved - first it was an abort (and not block) issue in some other example (not here, this one was with Null), but still blocking in the "big project", where I suspected Interfaces and class-wide calls. But there it is now also resolved too, although in a quite mysterious way - it just started working.. (I just run a diff, and there no essential changes) But maybe indeed Text_IO is blocking on Tuesdays but not other days of week :) . Thanks for all the input anyway! – George Shapovalov Dec 13 '17 at 09:08