4 #include "devices/serial.h"
5 #include "devices/vga.h"
6 #include "threads/init.h"
7 #include "threads/interrupt.h"
8 #include "threads/synch.h"
10 static void vprintf_helper (char, void *);
11 static void putchar_have_lock (uint8_t c);
14 Both the vga and serial layers do their own locking, so it's
15 safe to call them at any time.
16 But this lock is useful to prevent simultaneous printf() calls
17 from mixing their output, which looks confusing. */
18 static struct lock console_lock;
20 /* True in ordinary circumstances: we want to use the console
21 lock to avoid mixing output between threads, as explained
24 False in early boot before the point that locks are functional
25 or the console lock has been initialized, or after a kernel
26 panics. In the former case, taking the lock would cause an
27 assertion failure, which in turn would cause a panic, turning
28 it into the latter case. In the latter case, if it is a buggy
29 lock_acquire() implementation that caused the panic, we'll
30 likely just recurse. */
31 static bool use_console_lock;
33 /* It's possible, if you add enough debug output to Pintos, to
34 try to recursively grab console_lock from a single thread. As
35 a real example, I added a printf() call to palloc_free().
36 Here's a real backtrace that resulted:
40 printf() - palloc() tries to grab the lock again
42 schedule_tail() - another thread dying as we switch threads
45 intr_handler() - timer interrupt
50 sys_write() - one process writing to the console
54 This kind of thing is very difficult to debug, so we avoid the
55 problem by simulating a recursive lock with a depth
57 static int console_lock_depth;
59 /* Number of characters written to console. */
60 static int64_t write_cnt;
62 /* Enable console locking. */
66 lock_init (&console_lock);
67 use_console_lock = true;
70 /* Notifies the console that a kernel panic is underway,
71 which warns it to avoid trying to take the console lock from
76 use_console_lock = false;
79 /* Prints console statistics. */
81 console_print_stats (void)
83 printf ("Console: %lld characters output\n", write_cnt);
86 /* Acquires the console lock. */
88 acquire_console (void)
90 if (!intr_context () && use_console_lock)
92 if (lock_held_by_current_thread (&console_lock))
95 lock_acquire (&console_lock);
99 /* Releases the console lock. */
101 release_console (void)
103 if (!intr_context () && use_console_lock)
105 if (console_lock_depth > 0)
106 console_lock_depth--;
108 lock_release (&console_lock);
112 /* Returns true if the current thread has the console lock,
115 console_locked_by_current_thread (void)
117 return (intr_context ()
119 || lock_held_by_current_thread (&console_lock));
122 /* The standard vprintf() function,
123 which is like printf() but uses a va_list.
124 Writes its output to both vga display and serial port. */
126 vprintf (const char *format, va_list args)
131 __vprintf (format, args, vprintf_helper, &char_cnt);
137 /* Writes string S to the console, followed by a new-line
144 putchar_have_lock (*s++);
145 putchar_have_lock ('\n');
151 /* Writes the N characters in BUFFER to the console. */
153 putbuf (const char *buffer, size_t n)
157 putchar_have_lock (*buffer++);
161 /* Writes C to the vga display and serial port. */
166 putchar_have_lock (c);
172 /* Helper function for vprintf(). */
174 vprintf_helper (char c, void *char_cnt_)
176 int *char_cnt = char_cnt_;
178 putchar_have_lock (c);
181 /* Writes C to the vga display and serial port.
182 The caller has already acquired the console lock if
185 putchar_have_lock (uint8_t c)
187 ASSERT (console_locked_by_current_thread ());