Ada 2020 (40日目) - その他のprotectedの変更

ここまで拾えていなかった変更点を駆け足で追いかけています。

アスペクト

entry の本体側にも構文上アスペクトが付けられるようになりました。

ただし現時点では entry の本体側に付けられるアスペクトは規格上定義されていません。 実装依存のアスペクトを付けるための場所となります。

protected P1 is
   entry E1;
end P1;

protected P1 is

   entry E1 with IMPLEMENTATION_DEFINED_ASPECT is
   begin
      null;
   end E1;

end P1;

CPUアスペクト

CPU アスペクトは task を実行するためのCPUを固定するためのものです。 System.Multiprocessors.CPU の範囲内(有効範囲は実装依存)の値を指定します。

その CPU アスペクトが protected にも付けられるようになりました。

protected P2 with CPU => 1 is
   procedure E2;
end P2;
-- P2のbodyは省略

一見MutexにCPUを割り当てるような話であり意味不明ですが、その protected への呼び出しは特定CPUから行わなければならないという意味になります。 指定と異なるCPUから呼び出された場合は Program_Error になります。

これによりロックが要らなくなる等のいいことがあるらしいです。

またこの指定によりデッドロックが回避できるケースがあります。

例えば、次のように相互に呼び出し合っているふたつの protected があるとします。

protected P3 is
   procedure Enter;
   procedure Sync;
end P3;

protected P4 is
   procedure Enter;
   procedure Sync;
end P4;

protected body P3 is

   procedure Enter is
   begin
      P4.Sync;
   end Enter;

   procedure Sync is
   begin
      null;
   end Sync;

end P3;

protected body P4 is

   procedure Enter is
   begin
      P3.Sync;
   end Enter;

   procedure Sync is
   begin
      null;
   end Sync;

end P4;

P3.EnterP4.Enter が別々のスレッドから同時に呼ばれますと、それぞれロックがかかった状態で更にお互いのロックを獲得しようとしますからデッドロックします。 しかし P3.EnterP4.Enter が同時に呼ばれなければデッドロックしません。

CPU アスペクトでこれらの protected を同じCPUに固定することで別々のスレッドから同時に呼ばれない保証になります。

protected P3 with CPU => 1 is
   procedure Enter;
   procedure Sync;
end P3;

protected P4 with CPU => 1 is
   procedure Enter;
   procedure Sync;
end P4;

遅延初期化の要求

オブジェクトの初期化に関係するルールの中には遅延初期化の要求(require late initialization)というものがあります。 簡単に書きますと自分自身への参照を必要とする要素は遅延初期化を要求していると見做されて後回しになります。

declare
   type T;
   function Get_Early (Object : T) return Integer;
   type T is
      record
         Late : Integer := Get_Early (T);
         Early : Integer := 1;
      end record;

   function Get_Early (Object : T) return Integer is (Object.Early);

   Object_1 : T;
   Object_2 : T := (Early => 2, others => <>);

ここで要素 LateEarly の初期化順を考えます。 ソースコードの登場順では Late の方が先ですが Late のデフォルト初期化の式では関数経由で Early を参照しています。 ですので Early を先に初期化して欲しいわけです。

Late は与えられた式の中で自分自身を参照していますので遅延初期化を要求していると見做され望ましい順番で初期化されます。

このルールが protected にも適用されるようになりました。

protected P5 is
private
   function Get_Early return Integer;
   Late : Integer := Get_Early;
   Early : Integer := 1;
end P5;

protected body P5 is

   function Get_Early return Integer is
   begin
      return Early;
   end Get_Early;

end P5;

この Get_Early の内部呼び出しではオブジェクト名から . で続けていませんが P5 自身を参照しています。 そのため遅延初期化を要求していると見做されるようになりました。

関連AI

  • AI12-0169-1 Aspect specifications for entry bodies

  • AI12-0192-1 “requires late initialization” and protected types

  • AI12-0194-1 Language-defined aspects and entry bodies

  • AI12-0281-1 CPU Affinity for Protected Objects

  • AI12-0323-1 Implementation Advice for the CPU aspect for protected types