Task Safe Use of Streams
A stream is just as safe in a concurrent design as any other shared
resource. That is, unless it is properly protected, it is not at all safe.
The same may be said of shared memory, or shared files.
There are two classical solutions to this problem. The first is to
elminate the shared resource, and have a single, unshared resource
for each task. The second is to protect the shared resource by having
only one task at a time access the resource.
Classical database engines control concurrent access to the
database by forcing all tasks to access the database through a
single database engine. The same sort of thing can be achieved
in Ada in a couple of ways. One way is to have all tasks send
messages to a single controlling task through a rendezvous. This
would be the typical Ada 83 approach. The more modern, and
more efficient approach in Ada 95 is to have encapsulate the stream
in a protected object. All access will be correctly and effectively
controlled by the protected object mechanism.
When you use a protected object the "read" and "write" procedures
or entries must have a parameter of a class-wide type. The
class-wide type parameter will allow the protected object to
read and write a wide range of otherwise heterogeneous data items
to and from the stream. The stream may simply be an instance of
Ada.Streams.Stream_Io, or it may be a custom-made stream of
your own choosing. The implementation will be irrelevant to the
tasks accessing the protected object. It will also be irrelevant to the
protected object itself.
Following is a modest example of what I mean about
encapsulating a stream in a protected object.
The example uses Ada.Streams.Stream_Io for simplicity. You can
reasonably substitute your own stream implementation as needed.
Note that the file name for the log file is passed as a discriminant to
the protected type.
I believe this solution solves the interleaving problem. The read and
write operations will be atomic. The write operation will not complete
until all 'write operations for the tagged type complete. The key is
that the task calling the protected write and read operations is
ignorant of the use of a stream. No stream operations occur outside
the protection of the protected type.
--------------------------------------------------------------------------
-- Simple Example of using streams in a protected object for access by
-- multiple tasks.
--------------------------------------------------------------------------
package Type_Hierarchy is
type Base_Type is tagged private;
-- Add dispatching operations to the Base_type
type First_Child is new Base_Type with private;
-- Add or override operations for First_Child
private
type Base_Type is tagged record
Date : String(1..10);
Time : String(1..8);
end record;
type First_Child is new Base_type with record
Voltage : Float;
end record;
end Type_Hierarchy;
with Type_Hierarchy;
use Type_Hierarchy;
with Ada.Steams.Stream_Io;
package Multi_Task_Logging is
protected type Log_Stream (Log_Name : String) is
Procedure Write(Item : in Base_Type'Class);
Entry Read(Item : out Base_Type'Class);
private
File : Ada.Streams.Stream_Io.File_Type;
Log_Access : Ada.Streams.Stream_Io.Stream_Access :=
Ada.Streams.Stream_Io.Stream(File => File);
end Log_Stream;
end Multi_Tasking_Logging;
package body Multi_Tasking_Logging is
protected body Log_Stream is
Procedure Write(Item : in Base_Type'Class) is
begin
if not Ada.Streams.Stream_Io.Is_Open(File) then
Ada.Streams.Stream_Io.Open(File => File,
Name => Log_Name,
Mode =>
Ada.Streams.Stream_Io.Append_File);
else
Ada.Streams.Stream_Io.Reset(File => File,
Mode =>
Ada.Streams.Stream_Io.Append_File);
end if;
Base_Type'Class'Write(Stream => Log_Access, Item => Item);
end Write;
Entry Read(Item : out Base_Type'Class) when not
Ada.Streams.Stream_Io.End_of_File(File) is
begin
if not Ada.Streams.Stream_Io.is_Open(File) then
Ada.Streams.Stream_Io.Open(File => File,
Name => Log_Name,
Mode =>
Ada.Streams.Stream_Io.In_File);
else
Ada.Streams.Stream_Io.Reset(File => File,
Mode =>
Ada.Streams.Stream_Io.In_File);
end if;
Base_Type'Class'Read( Stream => Log_Access, Item => Item);
end Read;
end Log_Stream;
End Multi_Tasking_Logging;
Contributed by: James S. Rogers
Contributed on: December 30, 1998
License: Public Domain
Back