comp.lang.ada
 help / color / mirror / Atom feed
* weird block on Get from basic protected bounded buffer with 2 tasks
@ 2017-12-12 19:08 George Shapovalov
  2017-12-12 20:18 ` Jeffrey R. Carter
  2017-12-12 21:16 ` Dmitry A. Kazakov
  0 siblings, 2 replies; 6+ messages in thread
From: George Shapovalov @ 2017-12-12 19:08 UTC (permalink / raw)


Hi guys,

Sorry if this is a trivial issue, but I can't seem to find any (RM) rules regulating this or mentions of similar issues on Google..

This is a very basic protected bounded buffer in Ada, pretty much the text-book example. (This is a part of a bigger thing, but I simplified code down to bare minimum, where it reproduced the behavior). Don't mind the modular type for counters please, I tried using a range (just as in GNAT.Bounded_buffer) or going without Counter. The indices are not a culprit here, all ways to count them behave identically..

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 := 10;
    type Index is mod maxItems;
    maxCount : constant Index := Index(maxItems - 1);
    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 Count < maxCount and Buffer.Get'Count = 0 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 Count > 0 is
        begin
            X := buf(First);
            First := First + 1;
            Count := Count - 1;
            Put_Line("Get X="&X'Img & "; First="&First'Img&", Last="&Last'Img&", Count="&Count'Img);
        end;
    end;

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

    task Getter;
    task body Getter is
        X : Integer;
    begin
        Put_Line("Getter started");
        loop
            Put_Line("getting 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;
    abort Getter;
end test_buffer;


The output it gives me looks like this:
$ ./test_buffer 
Getter started
getting X..
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
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

Sometimes it manages to read one or two items before blocking (thenn more are pushed in correspondingly):
$ ./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
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
Getter started
getting X..
Put X= 7; First= 0, Last= 7, Count= 7
Put X= 8; First= 0, Last= 8, Count= 8
Get X= 0; First= 1, Last= 9, Count= 8
,  got X= 0
getting X..
Get X= 1; First= 2, Last= 9, Count= 7
,  got X= 1
getting X..
Put X= 9; First= 2, Last= 9, Count= 7
Put X= 10; First= 2, Last= 0, Count= 8
Get X= 2; First= 3, Last= 1, Count= 8
Put X= 11; First= 3, Last= 1, Count= 8
^C

I was suspecting issues with the barrier (thus that 'Count check in there just for the heck of it - I thought might be prioritizing Gets would make it behave, but its exactly the same without using 'Count in when). However I cannot seem to find any rule that would stall the barrier on Get (2nd task is external to the protected object, so no, its not the no reevaluation on internal calls of protected object). Oh, and if I move comments around, to execute Gets in the loop in the main part (whether I comment out or leave Getter running in parallel), then it seems to work Ok..

So, what gives? Am I missing something here or is this a bug?

The used platform: Gentoo Linux, gnat-gpl-2017, gcc-6.3.0

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: weird block on Get from basic protected bounded buffer with 2 tasks
  2017-12-12 19:08 weird block on Get from basic protected bounded buffer with 2 tasks George Shapovalov
@ 2017-12-12 20:18 ` Jeffrey R. Carter
  2017-12-12 20:55   ` George Shapovalov
  2017-12-12 21:16 ` Dmitry A. Kazakov
  1 sibling, 1 reply; 6+ messages in thread
From: Jeffrey R. Carter @ 2017-12-12 20:18 UTC (permalink / raw)


On 12/12/2017 08:08 PM, George Shapovalov wrote:
> 
>      abort Getter;

Getter isn't blocking; it's been aborted.

There's a deadlock: if Get is called 1st, it blocks because Count = 0. If Put is 
then called, it blocks because Get'Count > 0. Nothing can then change Count 
unless the call to Get is aborted.

If I change the abort statement to "null;" and remove the check on Get'Count in 
Put's barrier, it works fine for me.

-- 
Jeff Carter
"Since I strongly believe that overpopulation is by
far the greatest problem in the world, this [Soylent
Green] would be my only message movie."
Charleton Heston
123


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: weird block on Get from basic protected bounded buffer with 2 tasks
  2017-12-12 20:18 ` Jeffrey R. Carter
@ 2017-12-12 20:55   ` George Shapovalov
  2017-12-12 21:01     ` George Shapovalov
  0 siblings, 1 reply; 6+ messages in thread
From: George Shapovalov @ 2017-12-12 20:55 UTC (permalink / raw)


Hm, somewhat embarassing. Totally forgot about that abort which I put there after loop so I don't have to press ctrl-C when it finishes :). But of course it does not work without the loop!
Thanks!

Well, that means that there is some other issue in my original (and bigger) code.. But well, at least the bounded buffer works as its supposed to. At least a little sanity restored :).

Thanks again,
George


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: weird block on Get from basic protected bounded buffer with 2 tasks
  2017-12-12 20:55   ` George Shapovalov
@ 2017-12-12 21:01     ` George Shapovalov
  0 siblings, 0 replies; 6+ messages in thread
From: George Shapovalov @ 2017-12-12 21:01 UTC (permalink / raw)


On Tuesday, December 12, 2017 at 9:55:25 PM UTC+1, George Shapovalov wrote:
> Hm, somewhat embarassing. Totally forgot about that abort which I put there after loop so I don't have to press ctrl-C when it finishes :).

(err - original loop was counted - the same number of entries, so it did finish - just clarifying to make sense of my last post :). That was I am not even sure how many versions ago..) 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: weird block on Get from basic protected bounded buffer with 2 tasks
  2017-12-12 19:08 weird block on Get from basic protected bounded buffer with 2 tasks George Shapovalov
  2017-12-12 20:18 ` Jeffrey R. Carter
@ 2017-12-12 21:16 ` Dmitry A. Kazakov
  2017-12-13  9:25   ` George Shapovalov
  1 sibling, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2017-12-12 21:16 UTC (permalink / raw)


On 2017-12-12 20:08, George Shapovalov wrote:

> I thought might be prioritizing Gets would make it behave

Then the barrier must be

    Count = 0 or else (Count < maxCount and then Get'Count = 0)

i.e. empty or else not full and no pending Get.

BTW, prioritizing is a bad idea. If you enforce entry order you also 
increase possibility of context switches upon bulk buffer updates. If 
one task makes several Puts and another does several Gets over an empty 
buffer you may end up switching these tasks for each Put/Get pair. Which 
defeats the very purpose of buffering. That is for a single core. For 
multiple cores prioritizing would have no sense at all.

> So, what gives? Am I missing something here or is this a bug?
> 
> The used platform: Gentoo Linux, gnat-gpl-2017, gcc-6.3.0

On some older platforms tasking must be kicked on, if I correctly 
remember. E.g. by beginning the main with

    delay 0.0;

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: weird block on Get from basic protected bounded buffer with 2 tasks
  2017-12-12 21:16 ` Dmitry A. Kazakov
@ 2017-12-13  9:25   ` George Shapovalov
  0 siblings, 0 replies; 6+ messages in thread
From: George Shapovalov @ 2017-12-13  9:25 UTC (permalink / raw)


On Tuesday, December 12, 2017 at 10:16:07 PM UTC+1, Dmitry A. Kazakov wrote:
> On 2017-12-12 20:08, George Shapovalov wrote:
> Then the barrier must be
>     Count = 0 or else (Count < maxCount and then Get'Count = 0)
> BTW, prioritizing is a bad idea. If you enforce entry order you also 

Thank you for the hint on the barriers. Although yes, this was not the intention, I added that 'Count dependency to see if it helps to resolve that "block". The original implementation was plain as in textbook..

Now, I am "happy" to report that the issue got "resolved" - all quoted, because it simply "just started working". The bigger program, not this example. There were no aborts in it and I even tried to run the LogBuffer.getEvent from the main loop.. 
The bigger code was using synchronized interface with a printEvent(logBuf_Int'Class) procedure and then implementing actual buffer as a protected type (overriding Get and Put). That class-wide printEvent was essentially calling Buffer.Get and then Print(record) (plain type, basic IO - the base idea was to decouple logging and IO, so this was just a "smart wrapper"). Like this:
procedure PrintEvent(LI : in out EventLogger_Interface'Class) is
    ev : Event_Rec;
begin
    LI.GetEvent(ev); -- was not returning from this before
    Put_Line("report point");  -- was never called here earlier
    PrintEvent(ev); -- regular procedure on basic record type calling IO
end;

Weird thing, it was blocking with Buffer.printEvent (class-wide wrapper) in main cycle. But then when I replaced it with two explicit calls (Get then Print) it unblocked and started working as expected.. And then I prepared an update of that simplified code, to include this, but it kept working in the sample code even with Buffer.printItem.. So, I went back and changed code back to Buffer.printItem in the main cycle - and it kept working now with the "big code" too! And I just run a diff against last commit that was blocking - and there are absolutely no changes aside few cosmetics!
So, yeah, I am glad of course it works now, but it really seems like "blocking on Tuesdays" issue right now.. 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-12-13  9:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-12 19:08 weird block on Get from basic protected bounded buffer with 2 tasks George Shapovalov
2017-12-12 20:18 ` Jeffrey R. Carter
2017-12-12 20:55   ` George Shapovalov
2017-12-12 21:01     ` George Shapovalov
2017-12-12 21:16 ` Dmitry A. Kazakov
2017-12-13  9:25   ` George Shapovalov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox