X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flib%2Fkernel%2Fconsole.c;h=865b13b2cd810b0805acead8ec9f4e3e038f609a;hb=615bf3b3d2a8573ed6fb9ddc0055745e163ac999;hp=803b46b393dca80c9773e165bdcf8715c2a67de5;hpb=9ab60037e59bb84c22d5648be7c0655fe1be820e;p=pintos-anon diff --git a/src/lib/kernel/console.c b/src/lib/kernel/console.c index 803b46b..865b13b 100644 --- a/src/lib/kernel/console.c +++ b/src/lib/kernel/console.c @@ -16,10 +16,81 @@ static void putchar_unlocked (uint8_t c); from mixing their output, which looks confusing. */ static struct lock console_lock; +/* It's possible, if you add enough debug output to Pintos, to + try to recursively grab console_lock from a single thread. As + a real example, I added a printf() call to palloc_free(). + Here's a real backtrace that resulted: + + lock_console() + vprintf() + printf() - palloc() tries to grab the lock again + palloc_free() + schedule_tail() - another thread dying as we switch threads + schedule() + thread_yield() + intr_handler() - timer interrupt + intr_set_level() + serial_putc() + putchar_unlocked() + putbuf() + sys_write() - one process writing to the console + syscall_handler() + intr_handler() + + This kind of thing is very difficult to debug, so we avoid the + problem by simulating a recursive lock with a depth + counter. */ +static int console_lock_depth; + +/* Number of characters written to console. */ +static int64_t write_cnt; + +/* Initializes the console. */ void console_init (void) { - lock_init (&console_lock, "console"); + lock_init (&console_lock); +} + +/* Prints console statistics. */ +void +console_print_stats (void) +{ + printf ("Console: %lld characters output\n", write_cnt); +} + +/* Acquires the console lock. */ +static void +acquire_console (void) +{ + if (!intr_context ()) + { + if (lock_held_by_current_thread (&console_lock)) + console_lock_depth++; + else + lock_acquire (&console_lock); + } +} + +/* Releases the console lock. */ +static void +release_console (void) +{ + if (!intr_context ()) + { + if (console_lock_depth > 0) + console_lock_depth--; + else + lock_release (&console_lock); + } +} + +/* Returns true if the current thread has the console lock, + false otherwise. */ +static bool +console_locked_by_current_thread (void) +{ + return intr_context () || lock_held_by_current_thread (&console_lock); } /* The standard vprintf() function, @@ -30,13 +101,9 @@ vprintf (const char *format, va_list args) { int char_cnt = 0; - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); __vprintf (format, args, vprintf_helper, &char_cnt); - - if (!intr_context ()) - lock_release (&console_lock); + release_console (); return char_cnt; } @@ -46,31 +113,33 @@ vprintf (const char *format, va_list args) int puts (const char *s) { - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); while (*s != '\0') putchar_unlocked (*s++); putchar_unlocked ('\n'); - - if (!intr_context ()) - lock_release (&console_lock); + release_console (); return 0; } +/* Writes the N characters in BUFFER to the console. */ +void +putbuf (const char *buffer, size_t n) +{ + acquire_console (); + while (n-- > 0) + putchar_unlocked (*buffer++); + release_console (); +} + /* Writes C to the vga display and serial port. */ int putchar (int c) { - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); putchar_unlocked (c); - - if (!intr_context ()) - lock_release (&console_lock); - + release_console (); + return c; } @@ -89,6 +158,8 @@ vprintf_helper (char c, void *char_cnt_) static void putchar_unlocked (uint8_t c) { + ASSERT (console_locked_by_current_thread ()); + write_cnt++; serial_putc (c); vga_putc (c); }