/* * srr.c *Naomi Aro *0424556 *March 14 2008 */ #include #include #include #include #include #include "srr.h" enum state {IDLE, ACTIVE}; enum mail {UNREAD, READ}; enum reply {REPLIED, NOTREPLIED}; typedef struct mailbox mbox; typedef struct message mail; struct mailbox{ pthread_t tid; //thread ID of owner pthread_cond_t changed; enum state send_state; enum state receive_state; mbox *next; }; struct message{ pthread_t from; pthread_t to; char *sbuff; int sblen; char *rbuff; int rblen; enum mail mail_state; enum reply reply_state; mail *next; }; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static mbox *mailboxes = NULL; static mail *messages = NULL; /* * *get_mailbox *finds the mailbox belonging to pthread thread. *If the thread does not yet have a mailbox one is created and added to the front of the mailbox list. * */ mbox *get_mbox( pthread_t thread ){ mbox *m; for( m=mailboxes; m!=NULL; m=m->next ){ if( pthread_equal( thread, m->tid )){ return m; } } m = (mbox *)malloc( sizeof(mbox) ); m->tid = thread; m->send_state = IDLE; m->receive_state = IDLE; pthread_cond_init( &(m->changed), NULL); if( mailboxes == NULL ){ m->next = NULL; mailboxes = m; } else{ m->next = mailboxes; mailboxes = m; } return m; } /* * msg_send * * Send the message msg to the receiver. The sender is blocked until the * receiver replies with its own message, which is placed in the reply buffer. * */ void msg_send( pthread_t receiver, char *msg, int msglen, char *reply, int *rep len ){ mbox *from, *to; mail *letter; mail *m, *prev, *next, *garbage; int rlen = *replen; pthread_mutex_lock( &lock ); from = get_mbox( pthread_self() ); to = get_mbox( receiver ); /* *If the list of messages is empty you can stick the first letter *at the beginning of the linked list of mails. *Otherwise the most recently received mail will be stuck at the end *of the linked list of mails. */ letter = (mail *)malloc(sizeof(mail)); letter->sbuff = (char *)malloc( msglen ); letter->sbuff = memcpy(letter->sbuff, msg, msglen); letter->sblen = msglen; from->send_state = ACTIVE; letter->mail_state = UNREAD; letter->reply_state = NOTREPLIED; to->receive_state = ACTIVE; letter->from = pthread_self(); letter->to = receiver; if( messages == NULL ){ messages = letter; } else{ m=messages; while(1){ if(m->next == NULL){ m->next= letter; break; } m = m->next; } } //a message has been sent, if a receive is waiting it must be signalled. pthread_cond_signal( &(to->changed)); //a thread has sent its message and it must block itself until it receiv es a reply. while( from->send_state == ACTIVE ) pthread_cond_wait( &(from->changed), &lock ); reply = memcpy(reply, letter->rbuff, rlen); //free the space of the letter as it has served its purpose. m = messages; prev = messages; while(1){ if(m->reply_state == REPLIED){ garbage = m; next = m->next; prev->next = next; free(garbage); break; } prev = m; m = m->next; } pthread_mutex_unlock( &lock ); return; } /* * msg_receive * * Receive a message from any sender thread. Receiver's thread is blocked * until a message is available. The sender's thread ID is returned so * that msg_reply can later be called (sender is blocked awaiting reply). * The reply will not be received until msg_reply is called. */ pthread_t msg_receive( char *sbuffer, int *maxlen ){ mbox *to; mail *letter; mail *m; pthread_t sid; int len = *maxlen; pthread_mutex_lock( &lock ); to = get_mbox( pthread_self()); //waits for a message to arrive so it can receive. while( to->receive_state == IDLE ) pthread_cond_wait( &(to->changed), &lock ); //once there is at least one message, checks if it has already been rece ived. m=messages; while(m->next != NULL ){ if(m->mail_state == UNREAD){ //the message is ment for this thread. if(pthread_equal(m->to, pthread_self())){ break; } } else{ m=m->next; } } letter = m; sid = letter->from; letter->mail_state = READ; sbuffer = memcpy(sbuffer, letter->sbuff, len ); //checks to see if there are anymore messages for this thread, if not se t receive_state to IDLE. m = letter; while(1){ if(m->next == NULL){ to->receive_state = IDLE; break; } m=m->next; if(pthread_equal(m->to, pthread_self())){ break; } } pthread_mutex_unlock( &lock ); return sid; } /* * msg_reply * * Copy the reply into the sender's reply buffer and restart the * sender. * If no message was received from the designated * sender thread, via prior msg_receive, the results are undefined. */ void msg_reply( pthread_t sender, char *msg, int len ){ mbox *to, *from; mail *letter; printf( "thread %x in msg_reply replying to %x\n",(unsigned int)pthread_ self(), (unsigned int)sender); pthread_mutex_lock( &lock ); from = get_mbox( sender ); to = get_mbox( pthread_self() ); letter = messages; while(1){ printf("message sender: %x\n",(unsigned int)letter->from); if(letter->next == NULL){ break; } letter = letter->next; } letter = messages; //reply to msg sent by sender. while(1){ printf("checking for message from sender\n"); if(letter->reply_state == REPLIED){ if(letter->next != NULL){ letter= letter->next; } } else{ if(pthread_equal(sender, letter->from)){ break; } letter = letter->next; } } printf( "thread %x in msg_reply replying to %x FOUND LETTER\n",(unsigned int)pthread_self(), (unsigned int)sender); letter->rbuff = (char*)malloc(len); letter->rbuff = memcpy(letter->rbuff, msg, len); letter->rblen = len; letter->reply_state = REPLIED; from->send_state = IDLE; //wake up sender thread after the message has been replied to. pthread_cond_signal( &(from->changed) ); pthread_mutex_unlock( &lock ); return; }