Permalink
Browse files

Add the rest of the echo multitasking example, now without leaking ta…

…sks!
  • Loading branch information...
1 parent 740677b commit 889546daabe446716d7ec3920b437cfe02999595 @rtyler committed Mar 9, 2013
Showing with 175 additions and 67 deletions.
  1. +1 −0 .gitignore
  2. +1 −1 Makefile
  3. +86 −0 echomultitask-worker.adb
  4. +36 −0 echomultitask-worker.ads
  5. +0 −66 echomultitask.adb
  6. +3 −0 echomultitask.ads
  7. +48 −0 echomultitask_main.adb
View
1 .gitignore
@@ -10,3 +10,4 @@ vectors
runtests
/build
/obj
+echomultitask
View
2 Makefile
@@ -24,7 +24,7 @@ vectors:
$(GNATMAKE) vectors.adb
echomultitask:
- $(GNATMAKE) echomultitask.adb
+ $(GNATMAKE) echomultitask_main.adb -o echomultitask
clean:
rm -f *.o *.ali
View
86 echomultitask-worker.adb
@@ -0,0 +1,86 @@
+private with Ada.Streams,
+ Ada.Strings.Hash,
+ Ada.Text_IO,
+ GNAT.Sockets;
+
+package body EchoMultitask.Worker is
+ use Ada.Text_IO,
+ GNAT.Sockets;
+
+ -- We need a Hash function to make sure our Hashed_Maps.Map container can
+ -- proeprly create the hash map. This function will just rely on the
+ -- Ada.Strings.Hash function and pass in the string representation of the
+ -- Task_Id
+ function Hash (Key : Ada.Task_Identification.Task_Id) return Ada.Containers.Hash_Type is
+ begin
+
+ return Ada.Strings.Hash (Ada.Task_Identification.Image (Key));
+
+ end Hash;
+
+
+ task body Worker is
+ use Ada.Streams;
+
+ Client_Sock : Socket_Type;
+ begin
+ accept Serve (Sock : Socket_Type) do
+ Client_Sock := Sock;
+ end Serve;
+
+ declare
+ Channel : Stream_Access := Stream (Client_Sock);
+ Data : Ada.Streams.Stream_Element_Array (1 .. 1);
+ Offset : Ada.Streams.Stream_Element_Count;
+ begin
+ while true loop
+ Ada.Streams.Read (Channel.All, Data, Offset);
+ exit when Offset = 0;
+ Put (Character'Val (Data (1)));
+ end loop;
+ Put_Line (".. closing connection");
+ Close_Socket (Client_Sock);
+ end;
+ end Worker;
+
+
+ protected body Coordinator is
+
+ procedure Last_Wish (C : Ada.Task_Termination.Cause_Of_Termination;
+ T : Ada.Task_Identification.Task_Id;
+ X : Ada.Exceptions.Exception_Occurrence) is
+ W : Worker_Ptr := Tasks.Element (T);
+ begin
+
+ -- First, let's make sure we remove the task object from our Tasks
+ -- map
+ Tasks.Delete (Key => T);
+ -- Then we deallocate it
+ Free_Worker (W);
+ Put_Line ("Task (" & Ada.Task_Identification.Image (T) & ") deallocated");
+
+ end Last_Wish;
+
+ procedure Track (Ptr : in Worker_Ptr) is
+ -- THe Task_Id for a task can be found in the Identity attribute,
+ -- but since we're receiving a Worker_Ptr type, we first need to
+ -- dereference it into a Worker again
+ Key : constant Ada.Task_Identification.Task_Id := Ptr.all'Identity;
+ begin
+
+ Put_Line ("Adding task (" & Ada.Task_Identification.Image (Key) & ") to Coordinator.Tasks");
+
+ -- Add our Worker pointer into our hash map to hold onto it for
+ -- later
+ Tasks.Insert (Key => Key,
+ New_Item => Ptr);
+
+ -- We need to set a task termination handler (introduced in Ada
+ -- 2005) in order to get called when the Worker (W) terminates
+ Ada.Task_Termination.Set_Specific_Handler (Key, Last_Wish'Access);
+
+ end Track;
+
+ end Coordinator;
+
+end EchoMultitask.Worker;
View
36 echomultitask-worker.ads
@@ -0,0 +1,36 @@
+with Ada.Containers,
+ Ada.Containers.Indefinite_Hashed_Maps,
+ Ada.Exceptions,
+ Ada.Task_Identification,
+ Ada.Task_Termination,
+ Ada.Unchecked_Deallocation,
+ GNAT.Sockets;
+
+package EchoMultitask.Worker is
+
+ task type Worker is
+ entry Serve (Sock : GNAT.Sockets.Socket_Type);
+ end Worker;
+
+ --- Declare a pointer type for pointers to Worker objects
+ type Worker_Ptr is access all Worker;
+
+
+ -- Procedure to properly deallocate a heap-allocated Worker task
+ procedure Free_Worker is new Ada.Unchecked_Deallocation (Object => Worker,
+ Name => Worker_Ptr);
+
+ function Hash (Key : Ada.Task_Identification.Task_Id) return Ada.Containers.Hash_Type;
+
+ package Worker_Containers is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => Ada.Task_Identification.Task_id,
+ Element_Type => Worker_Ptr,
+ Hash => Hash,
+ Equivalent_Keys => Ada.Task_Identification."=");
+
+ protected Coordinator is
+ procedure Track (Ptr : in Worker_Ptr);
+ private
+ Tasks : Worker_Containers.Map;
+ end Coordinator;
+
+end EchoMultitask.Worker;
View
66 echomultitask.adb
@@ -1,66 +0,0 @@
-with Ada.Text_IO,
- Ada.Task_Identification,
- Ada.Streams,
- GNAT.Sockets;
-
-procedure EchoMultitask is
-
- task type Connection_Worker is
- entry Serve (Sock : GNAT.Sockets.Socket_Type);
- end Connection_Worker;
-
- ServerSock : GNAT.Sockets.Socket_Type;
- ServerAddr : GNAT.Sockets.Sock_Addr_Type;
-
- task body Connection_Worker is
- use Ada.Streams,
- Ada.Text_IO;
-
- Client_Sock : GNAT.Sockets.Socket_Type;
- begin
- accept Serve (Sock : GNAT.Sockets.Socket_Type) do
- Client_Sock := Sock;
- end Serve;
-
- declare
- Channel : GNAT.Sockets.Stream_Access := GNAT.Sockets.Stream (Client_Sock);
- Data : Ada.Streams.Stream_Element_Array (1 .. 1);
- Offset : Ada.Streams.Stream_Element_Count;
- begin
- while true loop
- Ada.Streams.Read (Channel.All, Data, Offset);
- exit when Offset = 0;
- Put (Character'Val (Data (1)));
- end loop;
- Put_Line (".. closing connection");
- GNAT.Sockets.Close_Socket (Client_Sock);
- end;
- end Connection_Worker;
-
- use Ada.Text_IO;
-begin
- ServerAddr.Addr := GNAT.Sockets.Inet_Addr ("0.0.0.0");
- ServerAddr.Port := GNAT.Sockets.Port_Type (2046);
- GNAT.Sockets.Create_Socket (ServerSock);
-
- GNAT.Sockets.Set_Socket_Option (ServerSock, GNAT.Sockets.Socket_Level, (GNAT.Sockets.Reuse_Address, True));
- GNAT.Sockets.Bind_Socket (ServerSock, ServerAddr);
- GNAT.Sockets.Listen_Socket (ServerSock);
-
- Put_Line ("Listening on port 2046");
-
- -- Keep the daemon running forever for now
- loop
- Put (".");
- declare
- ClientSock : GNAT.Sockets.Socket_Type;
- Worker : access Connection_Worker := new Connection_Worker;
- begin
- GNAT.Sockets.Accept_Socket (ServerSock, ClientSock, ServerAddr);
- Put_Line ("accepted connection");
- Worker.all.Serve (ClientSock);
- end;
- end loop;
-
-end EchoMultitask;
-
View
3 echomultitask.ads
@@ -0,0 +1,3 @@
+
+package EchoMultitask is
+end EchoMultitask;
View
48 echomultitask_main.adb
@@ -0,0 +1,48 @@
+with Ada.Containers,
+ Ada.Containers.Indefinite_Hashed_Maps,
+ Ada.Exceptions,
+ Ada.Text_IO,
+ Ada.Streams,
+ Ada.Strings.Hash,
+ Ada.Unchecked_Deallocation,
+ GNAT.Sockets;
+
+
+with EchoMultitask.Worker;
+
+
+procedure EchoMultitask_Main is
+ use Ada.Text_IO,
+ GNAT.Sockets,
+ EchoMultitask.Worker;
+
+ ServerSock : Socket_Type;
+ ServerAddr : Sock_Addr_Type;
+begin
+ ServerAddr.Addr := Inet_Addr ("0.0.0.0");
+ ServerAddr.Port := Port_Type (2046);
+ Create_Socket (ServerSock);
+
+ Set_Socket_Option (ServerSock, Socket_Level, (Reuse_Address, True));
+ Bind_Socket (ServerSock, ServerAddr);
+ Listen_Socket (ServerSock);
+
+ Put_Line ("Listening on port 2046");
+
+ -- Keep the daemon running forever for now
+ loop
+ Put_Line ("Waiting for a connection..");
+ declare
+ ClientSock : Socket_Type;
+ W : Worker_Ptr := new Worker;
+ begin
+ Accept_Socket (ServerSock, ClientSock, ServerAddr);
+ Put_Line ("accepted connection");
+ -- Dereference the pointer and call Server() on the Worker object
+ W.all.Serve (ClientSock);
+ Coordinator.Track (W);
+ end;
+ end loop;
+
+end EchoMultitask_Main;
+

0 comments on commit 889546d

Please sign in to comment.