NEXT UP previous
Next: Creating Channels

Rendezvous

Two new data structures are used in the implementation of the rendezvous mechanism. The first (struct channel) is provided on a per message channel basis. The second (struct message) is used on a per blocked message basis. These two structures, along with the declaration of struct context and some global variables, etc., are all supplied in a separate header file, called <def.h>, which should be #included at the top of the program. The full contents of <def.h> are as follows:

	#include <string.h>

	struct context		   		      /* One structure for each thread */
	{
		int ebp;     		   /* Base pointer (stack frame pointer) store */
		char *stack;               /* Pointer to memory block for thread stack */
		struct context *next;             /* Round robin circular list pointer */
		struct context *prev;             /* Round robin circular list pointer */


		struct channel         /* One structure for each communication channel */
		{
			int number;                                  /* Channel number */
			int sr_flag;    /* 0=no queue, 1=send queued, 2=receive queued */
			struct channel *link;	       /* Link to next channel in list */
			struct message *message_list;	      /* Head of message queue */
			struct message *message_tail;	      /* Tail of message queue */
		};

		struct message          /* One structure for each pending send/receive */
		{
			int size;                          /* Size of message in bytes */
			char *addr;                     /* Pointer to start of message */
			struct message *link;         /* Link to next message in queue */
			struct context *thread;  /* Which thread blocks on this struct */
		};
		
		static struct context main_thread;       /* Storage for main() details */
		static struct context *current;          /* Currently executing thread */
		static int thread_count = 0;	      /* Number of threads to schedule */
		static struct channel *channel_list = 0;       /* List of all channels */

		static int switch_context(struct context *, struct context *); 
		static int exit_thread(void);
		static int rendezvous(struct channel *, char *, int, int);

As each channel is created, a struct channel is allocated and added to a linked list of all the channels within the current process. Whenever an unmatched send() or receive() is performed on a channel, a struct message is allocated and entered on to the end of a message queue, hanging off the channel structure. Whenever a thread is blocked in a rendezvous awaiting a communication partner, the thread structure is unlinked from the scheduling loop and linked instead to its message structure. Figure 1 illustrates how the various structures are interconnected when send() and receive() operations are pending.

The diagram shows the existence of three communication channels in this example, whose data structures are linked together, with channel_list as the pointer to the head of the list. A send() operation has been performed on channel 1 and a message structure has been attached to the channel structure. The context structure for the thread which performed the send() has been removed from the scheduling loop and attached to the message structure. This ensures that the thread cannot be run again until a matching receive() operation is performed on the same channel, at which time the context structure will be easy to locate and re-connect to the scheduling loop.

The diagram also shows that a second send() operation has also been performed by another thread on the same channel and that it, too, is queued on the channel awaiting the second receive() operation. Communication channel 2 has been created but there are no pending messages queued. Channel 3 shows that a receive() has been performed and that a send() on the channel is awaited.


NEXT UP previous
Next: Creating Channels