嵌套 "then abort" 奇怪的行为

Nested "then abort" weird behaviour

嵌套 "then abort" 结构在 Ada 中合法吗?如果是,我该如何正确使用它们?我有这个代码:

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is

  task TestTask is
  end TestTask;

  task body TestTask is
  begin
    select
      delay 2.0;
      Put_Line("C"); -- never executed
    then abort
      select
        delay 5.0;
      then abort
        Put_Line("A");
        delay 4.0;
      end select;
      loop
        Put_Line("B");
        delay 10.0;
      end loop;
    end select;
  end TestTask;

begin
  null;
end Main;

我预计此代码应在 2 秒后退出。但是相反,它连续打印 "B",甚至没有延迟(它忽略 delay 10.0)。看起来代码的行为是这样的:

  1. 执行Put_Line("A")并等待2秒
  2. 从内部退出 "then abort"
  3. 执行循环忽略delay 10.0

如果我们插入 delay 1.0 而不是 delay 4.0(然后在循环内发生中止),程序将正常运行。我认为这很危险,因为 "abort then" 可以在库函数内部,例如:

procedure Main is

  ----- It's function from library -----

  procedure Foo is
  begin
    select
      delay 5.0;
    then abort
      Put_Line("A");
      delay 4.0;
    end select;
  end;

  ---------------------------------------

  task TestTask is
  end TestTask;

  task body TestTask is
  begin
    select
      delay 2.0;
      Put_Line("C"); -- never executed
    then abort
      Foo;
      loop
        Put_Line("B");
        delay 10.0;
      end loop;
    end select;
  end TestTask;

begin
  null;
end Main;

谁能解释为什么这个程序会以这种奇怪的方式运行?

A Comparison of the Asynchronous Transfer of Control Features in Ada and the Real-Time Specification、"The asynchronous select statement deals with nested ATCs correctly. For example, if the delay for an outer triggering statement expires while an inner delay is pending, the inner delay will be canceled and an ATC will be promulgated out of the inner abortable part…"

中所述

下面的变体按预期打印 ABBBBC。外部 触发语句 指定五秒超时;嵌套的 触发语句 指定三秒超时。由于后者的 可中止部分 仅消耗其第二预算的一秒,因此随后的 loop 可以在外部超时之前打印四个 B。将外部 delay 更改为 1.0 以重现示例中的效果。

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is

   task TestTask;

   task body TestTask is
   begin
      select
         delay 5.0;
         Put("C");
      then abort
         select
            delay 3.0;
         then abort
            Put("A");
            delay 1.0;
         end select;
         loop
            Put("B");
            delay 1.0;
         end loop;
      end select;
   end TestTask;

begin
   null;
end Main;