Channel Synchronization in Ada

with Ada.Text_IO;
with Ada.Calendar;

procedure Channel_Synchronization is
   package IO renames Ada.Text_IO;
   package Calendar renames Ada.Calendar;

   protected type Done_Channel is
      entry Wait;
      procedure Signal;
   private
      Done : Boolean := False;
   end Done_Channel;

   protected body Done_Channel is
      entry Wait when Done is
      begin
         null;
      end Wait;

      procedure Signal is
      begin
         Done := True;
      end Signal;
   end Done_Channel;

   task Worker is
      entry Start(Channel : in out Done_Channel);
   end Worker;

   task body Worker is
      Channel : access Done_Channel;
   begin
      accept Start(Channel : in out Done_Channel) do
         Worker.Channel := Channel'Access;
      end Start;

      IO.Put("working...");
      delay 1.0;
      IO.Put_Line("done");

      Channel.Signal;
   end Worker;

   Done : aliased Done_Channel;
begin
   Worker.Start(Done);
   Done.Wait;
end Channel_Synchronization;

This Ada program demonstrates channel synchronization, which is similar to the concept in the original example. Here’s an explanation of the code:

  1. We define a protected type Done_Channel that acts as our synchronization mechanism. It has an entry Wait and a procedure Signal, mimicking the behavior of a channel.

  2. The Worker task represents our worker “goroutine”. It takes a Done_Channel as a parameter when started.

  3. In the Worker task, we simulate work by printing “working…”, waiting for a second, and then printing “done”. After this, it signals completion using the Done_Channel.

  4. In the main procedure, we create a Done_Channel, start the Worker task, and then wait for it to complete using Done.Wait.

To run this program:

$ gnatmake channel_synchronization.adb
$ ./channel_synchronization
working...done

If you removed the Done.Wait; line from this program, the program would exit before the Worker even started, similar to the behavior described in the original example.

Ada’s tasking model provides a built-in synchronization mechanism, which we’ve used here to replicate the behavior of Go’s channels. While the syntax and specifics differ, the core concept of synchronizing concurrent operations remains the same.