Move serial interrupt queue into new file intq.c.
[pintos-anon] / src / devices / intq.c
1 #include "devices/intq.h"
2 #include <debug.h>
3 #include "threads/thread.h"
4
5 static int next (int pos);
6 static bool owned_by_current_thread (const struct intq *);
7 static void wait (struct intq *q, struct thread **waiter);
8 static void signal (struct intq *q, struct thread **waiter);
9
10 /* Initializes interrupt queue Q, naming it NAME (for debugging
11    purposes). */
12 void
13 intq_init (struct intq *q, const char *name) 
14 {
15   lock_init (&q->lock, name);
16   q->not_full = q->not_empty = NULL;
17   q->head = q->tail = 0;
18 }
19
20 /* Locks out other threads from Q (with Q's lock) and interrupt
21    handlers (by disabling interrupts). */
22 void
23 intq_lock (struct intq *q) 
24 {
25   ASSERT (!intr_context ());
26   ASSERT (!owned_by_current_thread (q));
27
28   lock_acquire (&q->lock);
29   q->old_level = intr_disable ();
30 }
31
32 /* Unlocks Q. */
33 void
34 intq_unlock (struct intq *q) 
35 {
36   ASSERT (!intr_context ());
37   ASSERT (owned_by_current_thread (q));
38
39   lock_release (&q->lock);
40   intr_set_level (q->old_level);
41 }
42
43 /* Returns true if Q is empty, false otherwise. */
44 bool
45 intq_empty (const struct intq *q) 
46 {
47   ASSERT (intr_get_level () == INTR_OFF);
48   return q->head == q->tail;
49 }
50
51 /* Returns true if Q is full, false otherwise. */
52 bool
53 intq_full (const struct intq *q) 
54 {
55   ASSERT (intr_get_level () == INTR_OFF);
56   return next (q->head) == q->tail;
57 }
58
59 /* Removes a byte from Q and returns it.
60    Q must not be empty if called from an interrupt handler.
61    Otherwise, if Q is empty, first waits until a byte is added.
62    Either Q must be locked or we must be in an interrupt
63    handler. */   
64 uint8_t
65 intq_getc (struct intq *q) 
66 {
67   uint8_t byte;
68   
69   ASSERT (owned_by_current_thread (q));
70   while (intq_empty (q))
71     wait (q, &q->not_empty);
72
73   byte = q->buf[q->tail];
74   q->tail = next (q->tail);
75   signal (q, &q->not_full);
76   return byte;
77 }
78
79 /* Adds BYTE to the end of Q.
80    Q must not be full if called from an interrupt handler.
81    Otherwise, if Q is full, first waits until a byte is removed.
82    Either Q must be locked or we must be in an interrupt
83    handler. */   
84 void
85 intq_putc (struct intq *q, uint8_t byte) 
86 {
87   ASSERT (owned_by_current_thread (q));
88   while (intq_full (q))
89     wait (q, &q->not_full);
90
91   q->buf[q->head] = byte;
92   q->head = next (q->head);
93   signal (q, &q->not_empty);
94 }
95 \f
96 /* Returns the position after POS within an intq. */
97 static int
98 next (int pos) 
99 {
100   return (pos + 1) % INTQ_BUFSIZE;
101 }
102
103 /* Returns true if Q is "owned by" the current thread; that is,
104    if Q is locked by the current thread or if we're in an
105    external interrupt handler. */
106 static bool
107 owned_by_current_thread (const struct intq *q) 
108 {
109   return (intr_context ()
110           || (lock_held_by_current_thread (&q->lock)
111               && intr_get_level () == INTR_OFF));
112 }
113
114 /* WAITER must be the address of Q's not_empty or not_full
115    member.  Waits until the given condition is true. */
116 static void
117 wait (struct intq *q, struct thread **waiter) 
118 {
119   ASSERT (!intr_context ());
120   ASSERT (owned_by_current_thread (q));
121   ASSERT ((waiter == &q->not_empty && intq_empty (q))
122           || (waiter == &q->not_full && intq_full (q)));
123
124   *waiter = thread_current ();
125   thread_block ();
126 }
127
128 /* WAITER must be the address of Q's not_empty or not_full
129    member, and the associated condition must be true.  If a
130    thread is waiting for the condition, wakes it up and resets
131    the waiting thread. */
132 static void
133 signal (struct intq *q, struct thread **waiter) 
134 {
135   ASSERT (owned_by_current_thread (q));
136   ASSERT ((waiter == &q->not_empty && !intq_empty (q))
137           || (waiter == &q->not_full && !intq_full (q)));
138
139   if (*waiter != NULL) 
140     {
141       thread_unblock (*waiter);
142       *waiter = NULL;
143     }
144 }