Gem #20: Using pragma Shared_Passive for data persistence

by Pascal Obry —EDF R&D

Let's get started…

Data persistence can be achieved in many ways starting as simple as using hand-written code to store and load data into some text files to something as complex as mapping the data into a relational or object database for example.

In some cases we just want to store the content of a set of variables. Ada provides such support using the Annex-E shared passive pragma. Let's write a simple counter that will increment each time the application is run:

   package Store is
      pragma Shared_Passive;
      Counter : Natural := 0;
   end Store;

And yes that's all! The variable's current value is read at elaboration time and written during program finalization. Shared passive unit state is saved on disk using one file for each top-level declaration (variables, protected objects). The filename is composed of the unit name and the declaration name separated by a dot. The above counter variable state will be saved into the file named "store.counter" for example.

So the main is as simple as:

   with Ada.Text_IO;
   with Store;

   procedure Main is
      use Ada.Text_IO;
      Put_Line ("Counter : " & Natural'Image (Store.Counter));
      Store.Counter := Store.Counter + 1;
   end Main;

Each time this program is run it will increment Counter by one. There is nothing to save or load explicitly.

In the context of concurrent programming it may be necessary to add proper synchronization. This can be easily done by using a protected object on the shared passive package.

   package Store is
      pragma Shared_Passive;

      protected Shared is
         function Counter return Natural;
         procedure Increment;
         C : Natural := 0;
      end Shared;
   end Store;

Note that the set of objects that can be declared is restricted as a shared passive unit can only depend on pure or other shared passive units. So, for example, it is not possible to declare an Unbounded_String nor any Ada.Containers in a shared passive unit.

Yet it is possible to declare complex objects like records or arrays in a shared passive partition and have them automatically saved. Let's take for example the following complex matrix:

   package Store is
      pragma Shared_Passive;

      type Complex is record
         X, Y : Float;
      end record;

      type Matrix is array
         (Positive range <>, Positive range <>) of Complex;

      M : Matrix (1 .. 3, 1 .. 3);

   end Store;

In spite of the limitations, this cheap persistence support can be quite handy in some circumstances.

Related Source Code

Ada Gems example files are distributed by AdaCore and may be used or modified for any purpose without restrictions.