115 lines
2.7 KiB
C
115 lines
2.7 KiB
C
|
#include "devices/intq.h"
|
|||
|
#include <debug.h>
|
|||
|
#include "threads/thread.h"
|
|||
|
|
|||
|
static int next (int pos);
|
|||
|
static void wait (struct intq *q, struct thread **waiter);
|
|||
|
static void signal (struct intq *q, struct thread **waiter);
|
|||
|
|
|||
|
/* Initializes interrupt queue Q. */
|
|||
|
void
|
|||
|
intq_init (struct intq *q)
|
|||
|
{
|
|||
|
lock_init (&q->lock);
|
|||
|
q->not_full = q->not_empty = NULL;
|
|||
|
q->head = q->tail = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* Returns true if Q is empty, false otherwise. */
|
|||
|
bool
|
|||
|
intq_empty (const struct intq *q)
|
|||
|
{
|
|||
|
ASSERT (intr_get_level () == INTR_OFF);
|
|||
|
return q->head == q->tail;
|
|||
|
}
|
|||
|
|
|||
|
/* Returns true if Q is full, false otherwise. */
|
|||
|
bool
|
|||
|
intq_full (const struct intq *q)
|
|||
|
{
|
|||
|
ASSERT (intr_get_level () == INTR_OFF);
|
|||
|
return next (q->head) == q->tail;
|
|||
|
}
|
|||
|
|
|||
|
/* Removes a byte from Q and returns it.
|
|||
|
If Q is empty, sleeps until a byte is added.
|
|||
|
When called from an interrupt handler, Q must not be empty. */
|
|||
|
uint8_t
|
|||
|
intq_getc (struct intq *q)
|
|||
|
{
|
|||
|
uint8_t byte;
|
|||
|
|
|||
|
ASSERT (intr_get_level () == INTR_OFF);
|
|||
|
while (intq_empty (q))
|
|||
|
{
|
|||
|
ASSERT (!intr_context ());
|
|||
|
lock_acquire (&q->lock);
|
|||
|
wait (q, &q->not_empty);
|
|||
|
lock_release (&q->lock);
|
|||
|
}
|
|||
|
|
|||
|
byte = q->buf[q->tail];
|
|||
|
q->tail = next (q->tail);
|
|||
|
signal (q, &q->not_full);
|
|||
|
return byte;
|
|||
|
}
|
|||
|
|
|||
|
/* Adds BYTE to the end of Q.
|
|||
|
If Q is full, sleeps until a byte is removed.
|
|||
|
When called from an interrupt handler, Q must not be full. */
|
|||
|
void
|
|||
|
intq_putc (struct intq *q, uint8_t byte)
|
|||
|
{
|
|||
|
ASSERT (intr_get_level () == INTR_OFF);
|
|||
|
while (intq_full (q))
|
|||
|
{
|
|||
|
ASSERT (!intr_context ());
|
|||
|
lock_acquire (&q->lock);
|
|||
|
wait (q, &q->not_full);
|
|||
|
lock_release (&q->lock);
|
|||
|
}
|
|||
|
|
|||
|
q->buf[q->head] = byte;
|
|||
|
q->head = next (q->head);
|
|||
|
signal (q, &q->not_empty);
|
|||
|
}
|
|||
|
|
|||
|
/* Returns the position after POS within an intq. */
|
|||
|
static int
|
|||
|
next (int pos)
|
|||
|
{
|
|||
|
return (pos + 1) % INTQ_BUFSIZE;
|
|||
|
}
|
|||
|
|
|||
|
/* WAITER must be the address of Q's not_empty or not_full
|
|||
|
member. Waits until the given condition is true. */
|
|||
|
static void
|
|||
|
wait (struct intq *q UNUSED, struct thread **waiter)
|
|||
|
{
|
|||
|
ASSERT (!intr_context ());
|
|||
|
ASSERT (intr_get_level () == INTR_OFF);
|
|||
|
ASSERT ((waiter == &q->not_empty && intq_empty (q))
|
|||
|
|| (waiter == &q->not_full && intq_full (q)));
|
|||
|
|
|||
|
*waiter = thread_current ();
|
|||
|
thread_block ();
|
|||
|
}
|
|||
|
|
|||
|
/* WAITER must be the address of Q's not_empty or not_full
|
|||
|
member, and the associated condition must be true. If a
|
|||
|
thread is waiting for the condition, wakes it up and resets
|
|||
|
the waiting thread. */
|
|||
|
static void
|
|||
|
signal (struct intq *q UNUSED, struct thread **waiter)
|
|||
|
{
|
|||
|
ASSERT (intr_get_level () == INTR_OFF);
|
|||
|
ASSERT ((waiter == &q->not_empty && !intq_empty (q))
|
|||
|
|| (waiter == &q->not_full && !intq_full (q)));
|
|||
|
|
|||
|
if (*waiter != NULL)
|
|||
|
{
|
|||
|
thread_unblock (*waiter);
|
|||
|
*waiter = NULL;
|
|||
|
}
|
|||
|
}
|