Parallel processing (Ada)从具有2个任务的基本受保护有界缓冲区获取时出现奇怪的阻塞

Parallel processing (Ada)从具有2个任务的基本受保护有界缓冲区获取时出现奇怪的阻塞,parallel-processing,ada,protected,Parallel Processing,Ada,Protected,在Ada中,这是一个非常基本的受保护的有界缓冲区,就像它在任何地方,甚至在教科书中所显示的一样。(这是一个更大的问题的一部分,但我将代码简化到了最低限度,它再现了行为)。 如果我有一个任务给它喂食,而“主体”正在阅读它,那么它似乎工作得很好。但是如果我使用两个任务——推杆和吸气,它会在第一个Get上阻塞,如下所示 with Ada.Text_IO;use Ada.Text_IO; procedure test_buffer is maxItems : constant Positive

在Ada中,这是一个非常基本的受保护的有界缓冲区,就像它在任何地方,甚至在教科书中所显示的一样。(这是一个更大的问题的一部分,但我将代码简化到了最低限度,它再现了行为)。 如果我有一个任务给它喂食,而“主体”正在阅读它,那么它似乎工作得很好。但是如果我使用两个任务——推杆和吸气,它会在第一个Get上阻塞,如下所示

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;
正如您所看到的,Get永远不会被执行,这可能是因为没有重新评估屏障。但是,如果我取消对主体的注释并注释掉任务(或者甚至只是取消对主体的注释,那么我将获得并发读取),那么所有读取和写入操作都将正常进行

我似乎找不到任何规则会在Get上拖延障碍(第二个任务是外部的,所以不,它不是对受保护对象的内部调用的no重新评估)


我是否遗漏了一些明显的东西,或者这是一个bug?

ARM第9.5.1节指出,在受保护的操作期间,调用可能阻塞的操作是一个有界错误。进一步,它用某些语言定义的子程序可能阻塞的语句阐明了这一点。特别是,语言定义的输入输出包的子程序(隐式或显式)可能会阻塞文件。
受保护项中的Put_Line过程调用可能会阻塞,应将其从项中删除。

ARM第9.5.1节指出,在受保护操作期间,调用可能阻塞的操作是有界错误。进一步,它用某些语言定义的子程序可能阻塞的语句阐明了这一点。特别是,语言定义的输入输出包的子程序(隐式或显式)可能会阻塞文件。
受保护条目中的Put_Line过程调用可能会阻塞,应该从条目中删除。

对我来说很有效(无可否认,这并不能证明没有bug)。。。Debian Stretch中的gcc-6(FSF)。你的操作系统和编译器版本是什么?在这里,这两个任务都是在第一次Put之前开始的,这可能会有所不同。对我来说也适用(64位Debian/Jessie上的GNAT 4.9.2)。但是您应该知道,I/O调用是“潜在的阻塞”,因此在受保护的操作中是非法的;我不能在这里工作,也不能在另一台计算机上工作。更奇怪的是。看看这是多么标准,我实例化了GNAT.Bounded_Buffers(它的主体中有几乎完全相同的代码),它挂起得更快,插入和删除(在我的代码中放置和获取)都挂起了。。像这样:呃,很明显,它是在回车时提交的,很抱歉评论被打断了。无论如何,下面是示例输出:$./test\u缓冲区Getter开始请求X。。Putter启动了X=0^C系统(两个)nrun Gentoo Linux,大约在一个月内更新。相关Ada:gnat-gpl-2017(gcc-6.3.0)。原始(我的)代码至少会一直运行,直到PUT被缓冲区大小阻塞,甚至偶尔进行1-2次读取。所以,在gnat realtime中看起来更像是一种竞争条件。对我来说很有效(不可否认,这并不能证明没有bug)。。。Debian Stretch中的gcc-6(FSF)。你的操作系统和编译器版本是什么?在这里,这两个任务都是在第一次Put之前开始的,这可能会有所不同。对我来说也适用(64位Debian/Jessie上的GNAT 4.9.2)。但是您应该知道,I/O调用是“潜在的阻塞”,因此在受保护的操作中是非法的;我不能在这里工作,也不能在另一台计算机上工作。更奇怪的是。看看这是多么标准,我实例化了GNAT.Bounded_Buffers(它的主体中有几乎完全相同的代码),它挂起得更快,插入和删除(在我的代码中放置和获取)都挂起了。。像这样:呃,很明显,它是在回车时提交的,很抱歉评论被打断了。无论如何,下面是示例输出:$./test\u缓冲区Getter开始请求X。。Putter启动了X=0^C系统(两个)nrun Gentoo Linux,大约在一个月内更新。相关Ada:gnat-gpl-2017(gcc-6.3.0)。原始(我的)代码至少会一直运行,直到PUT被缓冲区大小阻塞,甚至偶尔进行1-2次读取。所以,看起来更像是gnat realtime中的一个竞赛条件……好吧,他们一开始就不在那里(当它阻塞时)。我添加它们是为了追踪发生了什么。。但是这个问题已经解决了——首先,在其他一些例子中,它是一个中止(而不是阻塞)问题(这里不是,这个是Null),但在“大项目”中仍然阻塞,我怀疑是接口和类范围的调用。但是现在它也解决了,尽管以一种相当神秘的方式——它刚刚开始工作。。(我只是运行了一个diff,没有任何必要的更改),但可能确实是在周二阻止了Text_IO,而不是一周中的其他几天:)。无论如何,感谢所有的输入!好吧,他们一开始不在那里(当它阻塞时)。我添加它们是为了追踪发生了什么。。但是这个问题已经解决了——首先,在其他一些例子中,它是一个中止(而不是阻塞)问题(这里不是,这个是Null),但在“大项目”中仍然阻塞,我怀疑是接口和类范围的调用。但是现在它也解决了,尽管以一种相当神秘的方式——它刚刚开始工作。。(我只是运行了一个diff,没有任何必要的更改),但可能确实是在周二阻止了Text_IO,而不是一周中的其他几天:)。无论如何,感谢所有的输入!
$ ./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