使用访问类型在受保护对象中调用过程出现问题

时间:2019-02-15 12:56:10

标签: ada

考虑附加的代码,该代码显示对同一过程的三个不同的调用。它编译良好,但挂起了执行时间。我怀疑某种锁,但是我不明白为什么。

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;
procedure Main is

   type A_Proc is access protected procedure (B: in out Integer);

   protected Obj is
      procedure Inc (B: in out Integer);
      procedure Test (B: in out Integer);
   end Obj;
   protected body Obj is
      procedure Inc (B: in out Integer) is
      begin
         B:=B+1;
      end Inc;
      procedure Test (B: in out Integer) is
         Proc : A_Proc:=Inc'Access;
      begin
         Proc.all (B);
      end Test;
   end Obj;

   B : Integer:=1;

   Proc : A_Proc:=Obj.Inc'Access;
begin
   Put_Line(B'Image);
   Obj.Inc (B);
   Put_Line(B'Image);
   Proc.all (B);
   Put_Line(B'Image);
   Obj.Test (B);
   Put_Line(B'Image);
   Put_Line("The End");
end Main;

2 个答案:

答案 0 :(得分:3)

ARM 9.5.1(3)中,我们找到

  

为了在受保护的子程序上执行调用,如果该调用是内部调用(请参见9.5),则该子程序的主体将像普通子程序调用一样被执行。如果调用是外部调用,则子程序的主体将作为对目标受保护对象的新受保护动作的一部分执行

ARM 9.5(2,3)中的

  

当名称或前缀表示受保护的子程序条目时,名称或前缀将确定目标对象,如下所示:

     
      
  • 如果它是表示操作的声明(或主体)的direct_name或扩展名,则隐式指定目标对象为任务或受保护单元的当前实例,并立即将其围起来;使用这种名称的呼叫被定义为<内部呼叫
  •   

但是在(5)中,

  
      
  • 如果名称或前缀是对子保护访问值的取消引用(隐式或显式),则目标对象由最初产生访问值的Access attribute_reference的前缀确定;使用这种名称的呼叫被定义为外部呼叫
  •   

因此,我担心ARM会明确警告您尝试执行的操作; Obj在进入Obj.Test时被锁定,并且通过Proc进行的外部呼叫尝试再次获得该锁定。参见DeeDee’s answer

答案 1 :(得分:3)

作为西蒙·赖特(Simon Wright)答案的附录,我认为ARM 9.5.1 (15)

  

在受保护的操作期间,调用可能会阻塞的操作是有界的错误。以下内容被定义为可能阻止的操作:

     

[...]

     
      
  • 对受保护子程序的外部调用(或外部重新排队),其目标对象与受保护动作的目标对象相同;
  •   

ARM 9.5.1 (17)

  

如果检测到边界错误,则会引发Program_Error。如果未检测到,则边界错误可能会导致在同一目标对象上发生死锁或(嵌套)受保护的操作。

也适用。如果是这样,则对受保护的子程序可能进行外部调用会导致死锁,但可能也会导致程序继续运行(或引发Program_Error) )。

我在Windows和Linux(Debian)上的GNAT CE 2018上执行了该程序。 Windows上的程序运行到最后,但是在打印3后在Linux上挂起。


要详细说明以下评论:您可以使用配置实用程序Detect_Blocking对这些潜在阻塞的调用进行Ada运行时检查(请参见ARM H.5)。

如果使用GPRbuild,则可以通过将pragma Detect_Blocking;放入文件(通常名为gnat.adc)中来启用检测,并通过添加{{1 }}属性赋予编译器软件包(另请参见herehere):

Local_Configuration_Pragmas