Search code examples
embeddedreal-timesemaphoreadaproducer-consumer

Semaphore in Ada


This is an assignment and I have been asked to implement a Semaphore in Ada as the description below.

However I have implemented the Semaphore.adb and called this Semaphore in the producerconsumer_sem.adb which I created.

I get some output which is the following.

  1. I'm not sure if my initialization of semaphore is correct S: CountingSemaphore(1,1);.

  2. I don't know where I call the S.wait and S.Signal now i randomly called the S.wait before Producer put item in the buffer X := I; and the S.Signal after the X := I;. Is this the correct way?

Producer-Consumer Problem The program producerconsumer.adb implements a non-reliable implemen- tation of the producer-consumer problem, where data is likely be lost. In the following, you will use three different communication mechanisms to achieve a reliable implementation of the producer-consumer problem.

Semaphore

The Ada language does not directly provide library functions for a semaphore. However, semaphores can be implemented by means of a protected object. Create a package specification Semaphore in the file Semaphores.ads and the corresponding package body in the file Semaphores.adb that implements a counting semaphore. Skeletons for the package are available on the course page.

Use the semaphore package for a reliable implementation of the producer- consumer problem. Modify the file producerconsumer.adb and save the final code as producerconsumer_sem.adb. In order to use the semaphore package it shall be installed in the same directory as producerconsumer_sem.adb. It can then be accessed by

with Semaphores; use Semaphores;

The Output:

OutPut: 1 1 1 2 2 3 4 4 5 6 6 7 7 8 9 9 9 10 11 11 11 12 12 13 13 13 14 15 15 16 16 17 18 18 18 19 20 20 21 21 22 22 23 24 24 24 24 25 25 26 27 27 28 29 29 30 30 31 31 32 32 33 33 33 34 35 35 35 36 36 37 37 37 38 38 38 39 40 40 40

The package

package Semaphores is
   protected type CountingSemaphore(Max: Natural; Initial: Natural)  is
      entry Wait;
      entry Signal;
   private
      Count : Natural := Initial;
      MaxCount : Natural := Max;
   end CountingSemaphore;
end Semaphores;

The Semaphore I implemented semaphores.adb.

package body Semaphores is
   protected body CountingSemaphore is
   entry Wait when Count > 0 is
    begin
    Count := Count - 1;

    end Wait;
      entry Signal when Count < MaxCount is
    begin
    Count := Count + 1;

    end Signal;
   end CountingSemaphore;
end Semaphores;

The producerconsumer_sem.adb

with Ada.Text_IO;
use Ada.Text_IO;

with Ada.Real_Time;
use Ada.Real_Time;

with Ada.Numerics.Discrete_Random;

with Semaphores;
use Semaphores;

procedure ProducerConsumer_sem is

   X : Integer; -- Shared Variable
   N : constant Integer := 40; -- Number of produced and comsumed variables

   S: CountingSemaphore(1,1);
   --S1: CountingSemaphore(1,1);

   pragma Volatile(X); -- For a volatile object all reads and updates of
                       -- the object as a whole are performed directly
                       -- to memory (Ada Reference Manual, C.6)

   --Random Delays
   subtype Delay_Interval is Integer range 50..250;
   package Random_Delay is new Ada.Numerics.Discrete_Random
   (Delay_Interval);
   use Random_Delay;
   G : Generator;

   task Producer;

   task Consumer;

   task body Producer is
      Next : Time;
   begin
      Next := Clock;
      for I in 1..N loop
         -- Write to X
         S.Wait;
         X := I;
         S.Signal;
         --Next 'Release' in 50..250ms
         Next := Next + Milliseconds(Random(G));
         Put_Line(Integer'Image(X));
         delay until Next;
      end loop;
   end;

   task body Consumer is
      Next : Time;
   begin
      Next := Clock;
      for I in 1..N loop
         -- Read from X
         S.Wait;
         Put_Line(Integer'Image(X));
         S.Signal;
         Next := Next + Milliseconds(Random(G));
         delay until Next;
      end loop;
   end;

begin -- main task


   null;
end ProducerConsumer_sem;

Solution

  • On macOS, with FSF GCC 7.1.0 and GNAT GPL 2017, I changed your Put_Lines to Puts and got pretty-much the answer you state in the question.

    The question says to create Semaphore.ads, .adb. This will work on Windows, and may work on macOS, but won’t work on Linux, because of GNAT’s file naming convention (see the end of this; it’s a good idea to get into the habit of using lower-case file names).

    If you want to ensure that only one task has access to X at a time, I don’t think there’s much wrong with your Wait, Signal calls, though when I put a delay 0.1 at the beginning of Producer, the first value output was 151619216 (because X isn’t initialized). However! if the point is to communicate one update to X at a time (as implied by the names producer/consumer), you should

    • initialize the semaphore with a count of 0 (and a max of 1). This makes it a binary semaphore.
    • in Consumer, Wait only (i.e. remove the Signal)
    • in Producer, Signal only (i.e. remove the Wait). Also, remove the Put to avoid confusion!