/*
* 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)
{
mbox_t *box = &Q[q];
lock_acquire(&box->l);
*count = box->count;
*space = space_available(box);
lock_release(&box->l);
return 1; /*whatever - not a single caller checks the return value*/
}
/* 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;
box->count--;
/* 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;
box->count++;
condition_signal(&box->moreData);
lock_release(&box->l);
return sz; /*whatever - not a single caller checks the return value*/
}