Home | Contact | Pricing | News | Partners | Mailing List | Site Map
Gnat Pro. Powerful tools. Frontline Support. Ada expertise.

Gem #20: Using pragma Shared_Passive for data persistence

Author: Pascal Obry, EDF R&D

Abstract: Ada Gem #20 — Using pragma Shared_Passive for data persistence.

« Previous Gem | Next Gem » | Gems Menu

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;
   begin
      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;
      private
         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.

application/x-ada-source
1.9Kb
 

Posted by Posted in Development Log, Ada / Ada 2005, Devt log - Gem of the Week

Have your own idea for a Gem?

If you have an idea for a Gem you would like to contribute please feel free to contact us at: gems@adacore.com

Discussion

5 responses to “Gem #20: Using pragma Shared_Passive for data persistence”


  1. Christoph Grein said:

    Hey, that’s a really nice gem, actually. Two remarks, though.

    The storing in a file is, I reckon, a GNAT implementation detail (at least the file name). This should be pointed out. AARM E.1(11.a)

    Where in the RM is it specified that the initialisation of a shared passive variable is only performed the very first time? I searched the whole RM, but without avail.


  2. Paul F. Pearson said:

    In what format is the data stored? I presume that it’s essentially a stream output (e.g. ‘Output)? If so, it might be interesting if the ‘Output could be overridden (assuming the Ada.Streams is Pure - which I haven’t looked at).

    Thanks for a cool gem!


  3. Pascal Obry said:

    Christoph,

    Thanks for the kind words. The GNAT implementation should
    indeed be specified clearly.

    Pascal.


  4. Pascal Obry said:

    Paul,

    Yes you are right the output is just a stream. And it is
    indeed possible to specify the streamed format. We could
    link together this gem with the previous one on stream
    using an XML format.

    For example the following store implementation will
    outout counter as an XML like stream:

    with Ada.Streams;
    package Store is
    pragma Shared_Passive;

    use Ada.Streams;

    type My_Int is new Integer;

    procedure Write_Int
    (S : access Root_Stream_Type’Class; V : in My_Int);
    for My_Int’Write use Write_Int;

    Counter : My_Int := 0;
    end Store;

    package body Store is
    procedure Write_Int
    (S : access Root_Stream_Type’Class; V : in My_Int) is
    begin
    String’Write
    (S, “” & My_Int’Image (V) & “”);
    end Write_Int;
    end Store;

    The ‘Read implementation is left to the reader as
    an exercice.

    Pascal.


  5. Paul F. Pearson said:

    Pascal,

    Thanks. I’d not read about Annex E before reading this. Now my head hurts. :-)

Leave a Reply