/* * Implementation of the mailbox. * Implementation notes: * * The mailbox is protected with a lock to make sure that only * one process is within the queue at any time. * * It also uses condition variables to signal that more space or * more messages are available. * In other words, this code can be seen as an example of implementing a * producer-consumer problem with a monitor and condition variables. * * Note that this implementation only allows keys from 0 to 4 * (key >= 0 and key < MAX_Q). * * The buffer is a circular array. */ #include "common.h" #include "thread.h" #include "mbox.h" #include "util.h" mbox_t Q[MAX_MBOX]; /* * Returns the number of bytes available in the queue * Note: Mailboxes with count=0 messages should have head=tail, which * means that we return BUFFER_SIZE bytes. */ static int space_available(mbox_t * q) { if ((q->tail == q->head) && (q->count != 0)) { /* Message in the queue, but no space */ return 0; } if (q->tail > q->head) { /* Head has wrapped around */ return q->tail - q->head; } /* Head has a higher index than tail */ return q->tail + BUFFER_SIZE - q->head; } /* Initialize mailbox system, called by kernel on startup */ void mbox_init(void) { mbox_t *box; int i; for(i = 0; i < MAX_MBOX; i++){ box = &Q[i]; box->used = 0; lock_init(&box->l); condition_init(&box->moreSpace); condition_init(&box->moreData); box->count = 0; box->head = 0; box->tail = 0; } } /* * Open a mailbox with the key 'key'. Returns a mailbox handle which * must be used to identify this mailbox in the following functions * (parameter q). */ int mbox_open(int key) { mbox_t *box; if (key < 0 || key >= MAX_MBOX) return -1; box = &Q[key]; lock_acquire(&box->l); if (!box->used) { box->count = 0; box->head = 0; box->tail = 0; } box->used++; lock_release(&box->l); return key; } /* Close the mailbox with handle q */ int mbox_close(int q) { mbox_t *box; if (q < 0 || q >= MAX_MBOX) return -1; box = &Q[q]; lock_acquire(&box->l); /* We could assert here that box->used > 0 */ box->used--; lock_release(&box->l); return 1; } /* * Get number of messages (count) and number of bytes available in the * mailbox buffer (space). Note that the buffer is also used for * storing the message headers, which means that a message will take * MSG_T_HEADER + m->size bytes in the buffer. (MSG_T_HEADER = * sizeof(msg_t header)) */ int mbox_stat(int q, int *count, int *space) { } /* Fetch a message from queue 'q' and store it in 'm' */ int mbox_recv(int q, msg_t * m) { mbox_t *box = &Q[q]; int sz; int i; lock_acquire(&box->l); while (box->count < 1) condition_wait(&box->l, &box->moreData); /* the size field can also be fragmented, so assemble it into m */ for (i = 0; i < MSG_T_HEADER_SIZE; i++) ((char *)m)[i] = box->buffer[(box->tail + i) % BUFFER_SIZE]; sz = MSG_SIZE(m); for(; i < sz; i++) ((char *)m)[i] = box->buffer[(box->tail + i) % BUFFER_SIZE]; box->tail = (box->tail + sz) % BUFFER_SIZE; /* notfiy everyone so that a message will be sent if *any* process has a message that fits into the buffer */ condition_broadcast(&box->moreSpace); lock_release(&box->l); return sz; /*whatever - not a single caller checks the return value*/ } /* Insert 'm' into the mailbox 'q' */ int mbox_send(int q, msg_t * m) { mbox_t *box = &Q[q]; int sz = MSG_SIZE(m); int i; lock_acquire(&box->l); while (space_available(box) < sz) condition_wait(&box->l, &box->moreSpace); for (i = 0; i < sz; i++) box->buffer[(box->head + i) % BUFFER_SIZE] = ((char *)m)[i]; box->head = (box->head + sz) % BUFFER_SIZE; condition_signal(&box->moreData); lock_release(&box->l); return sz; /*whatever - not a single caller checks the return value*/ }