/*
* srr.c
*Naomi Aro
*0424556
*March 14 2008
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#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;
}