Ада: Массив задач - PullRequest
       27

Ада: Массив задач

1 голос
/ 13 марта 2019

Каков хороший способ связать индексированную задачу с соответствующим индексированным защищенным типом в SPARK?

В качестве примера рассмотрим следующую настройку:

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

Для каждого p(i) Iхотел бы задачу t(i), которая отслеживает p(i) и, когда она готова, обрабатывает ее.Я могу сделать это довольно легко в Ada, но SPARK w / Ravenscar более требователен.Я пробовал два подхода, которые, кажется, работают нормально, когда я запускаю их:

  1. Дайте T дискриминант Integer, а затем создайте T(i); для каждого i, но это растетобременительный с не очень-большим i.
task type T(which: Integer);
t1: T(1);
t2: T(2);
...
Добавьте функцию 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 «Prove» ворчит о потенциальных гонках данных, и я понимаю, почему (хотя я не уверен, что это на самом деле из-за этого).

Отсюда вопрос.

Ответы [ 2 ]

1 голос
/ 17 марта 2019

Я не эксперт в этом, но кажется, что вы не можете показать 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;
1 голос
/ 14 марта 2019

Это не приводит к тому, что gnatprove захлебнется.

И я думаю, что основное отличие от вашего варианта 2 заключается в том, что Claim проверяет, возможна ли заявка, и, если да, выполняет заявку в одной защищеннойcall.

Но я не совсем понимаю, как доказать, что цикл 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;

После внеплановых обсуждений с Джоном мы обнаружили, что это постусловие может быть доказано:

  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)
...