Ada:一系列任务

时间:2019-03-13 19:35:35

标签: arrays concurrency task ada

在SPARK中将索引任务链接到相应的索引受保护类型的好方法是什么?

有关详细信息,请考虑以下设置:

subtype Thread_Range is Natural range 1..n;
protected type P is ... end P;
p: array(Thread_Range) of P;

对于每个p(i),我想要一个任务t(i)来监视p(i),并在准备就绪时对其进行处理。我可以在Ada上轻松完成这项工作,但是SPARK w / Ravenscar的要求更高。我尝试了两种方法,这些方法在运行它们时似乎可以正常工作:

  1. 赋予T一个Integer判别力,然后为每个T(i);实例化一个i,但是对于不是很大的i来说,这会增加负担。 / li>
task type T(which: Integer);
t1: T(1);
t2: T(2);
...
  1. is_not_monitored添加set_monitor函数和P过程。创建一个判别任务 的数组。 t(i)开始时,它会分配自己来监视第一个p(j),它发现尚未分配监视器。
task type T;
task body T is
  which: Integer;
  available: Boolean;
begin
  for i in Thread_Range loop
    available := p(i).is_not_monitored;
    if available then
      p(i).set_monitor;
      which := i;
    end if;
  end loop;
  -- what the task does with p(i) follows
end T;
t: array(Thread_Range) of T;

我更喜欢第二个,但不是很多。无论如何,SPARK“证明”都对潜在的数据竞争抱怨不已,我可以理解为什么(尽管我不确定这实际上是由于这个原因)。

提出问题。

2 个答案:

答案 0 :(得分:1)

这不会使gnatprove窒息。

我认为与选项2的主要区别在于Claim检查是否可以提出索赔,如果可以,则在一个受保护的呼叫中执行索赔。

但是我还不太清楚如何证明Claim中的循环T在声明Ps (J)的情况下退出。我尝试在循环后放一个断言,但无法证明。

protected type P is
   procedure Claim (Succeeded : out Boolean);
private
   Claimed : Boolean := False;
end P;

subtype Thread_Range is Integer range 1 .. 2;

Ps : array (Thread_Range) of P;

Ts : array (Thread_Range) of T;

task body T is
   Which : Integer;
begin
Claim:
   for J in Thread_Range loop
      declare
         Claimed : Boolean;
      begin
         Ps (J).Claim (Succeeded => Claimed);
         if Claimed then
            Which := J;
            exit Claim;
         end if;
      end;
   end loop Claim;

   loop  -- having a loop keeps gnatprove quiet
      delay until Ada.Real_Time.Time_Last;
   end loop;
end T;

protected body P is
   procedure Claim (Succeeded : out Boolean) is
   begin
      if not Claimed then
         Claimed := True;
         Succeeded := True;
      else
         Succeeded := False;
      end if;
   end Claim;
end P;

与John进行带外讨论之后,我们发现可以证明这种后置条件:

  procedure Claim (Succeeded : out Boolean)
  with
    Post =>
      (Is_Claimed'Old or (Succeeded and Is_Claimed))
      or
      (not Succeeded and Is_Claimed);

请注意,它不是P’Old.Is_Claimed,主要是因为’Old需要副本,并且P受限制(因为它是受保护的类型)。

我们还发现了一些替代配方,例如,在GPL 2017中但在CE 2018中没有:

      (Is_Claimed
       and
       (Is_Claimed'Old xor Succeeded)

答案 1 :(得分:1)

我不是专家,但是似乎您无法证明SPARK任务实例和受保护对象实例之间存在一对一关系,除非您从任务实例中明确引用了该受保护对象实例。尤其是为了使SPARK证明只有一个任务会在受保护对象的进入时排队;下面的代码中的Wait条目)。因此(尽管这可能并非您真正要寻找的),但我只能通过使用可实例化多个的通用包来解决连接任务和受保护对象并同时具有监视器功能的问题次。这在GNAT CE 2018中得到了证明:

generic
package Generic_Worker with SPARK_Mode is   

   task T;

   protected P is
      entry Wait;
      procedure Trigger;
   private
      Triggered : Boolean := False;
   end P;

end Generic_Worker;

连身:

package body Generic_Worker with SPARK_Mode is

   task body T is      
   begin
      loop   --  Ravenscar: Tasks must not terminate.         
         P.Wait;
      end loop;
   end T;

   protected body P is

      entry Wait when Triggered is
      begin
         Triggered := False; 
         --  Do some work.
      end Wait;

      procedure Trigger is
      begin
         Triggered := True;
      end Trigger;

   end P;

end Generic_Worker;

和实例:

with Generic_Worker;

pragma Elaborate_All (Generic_Worker);

package Workers with SPARK_Mode is

   package Worker_0 is new Generic_Worker;
   package Worker_1 is new Generic_Worker;
   package Worker_2 is new Generic_Worker;
   package Worker_3 is new Generic_Worker;
   package Worker_4 is new Generic_Worker;

end Workers;