Q: What is the difference between disabling interrupts and the locks we are supposed to implement in Lab 3. A: In the kernel, interrupt disabling is used for mutual exclusion when the critical section is short. When the critical section is long (e.g., when performing IO while accessing some shared data), it is better to use blocking locks, so that interrupts are not disabled for a long time (which can lead to other issues, such as losing timer or network interrupts). In this lab, you are supposed to implement blocking locks. When a thread tries to acquire such a lock, and the lock has previously been acquired by another thread, then this thread should be put to sleep until the lock holder releases the lock. You need to use interrupt disabling to implement these locks. Note that when using blocking lock, interrupts are NOT disabled within the critical section protected by a blocking lock. At the user level, threads cannot disable interrupts. Instead, the operating system provides blocking locks so that user-level code can implement mutual exclusion. -------------------------------------------------------------------------------- Q: I understand that cv_wait runs unlock and sleep atomically. also i know that cv_wait = unlock + sleep + lock so does it run lock atomically together with the other two as well? A: You need to run "unlock + sleep" atomically (with interrupts disabled). lock can be called with interrupts disabled or enabled, and it does not matter. The reason is that after the call to sleep, other threads can run and acquire the lock, and so the interrupt state does not matter. -------------------------------------------------------------------------------- Q: What does preemption mean? A: Without preemption, a thread (e.g., the thread code in test_thread.c) controls thread switching because it needs to invoke thread_yield() to cause thread switching. With preemption, a thread no longer control thread switching because the interrupt handler may invoke thread_yield() to cause thread switching at any time (or after any instruction). -------------------------------------------------------------------------------- Q: When running the test_wait program, it appears to exit early, and I don't see the "wait test done" message. A: Your threads library is designed to run as long as there is at least one runnable threads in the system. If there is no runnable thread, the program will exit. Where is this implemented? In this case, then it is likely that you are not waking up threads correctly, and so eventually there are no runnable threads in the system, and your program exits early. -------------------------------------------------------------------------------- Q: How long is the lab 3 tester supposed to take? A: It should take roughly 5-6 minutes to run. -------------------------------------------------------------------------------- Q: When destroying the wait queue, should I wake up the threads in the wait queue, or kill them? A: Our tester ensures that no threads are waiting in a wait queue that is being destroyed, so you can check for that. Similarly, our tester ensures that no thread is holding a lock when the lock is being destroyed, so you can test for that. -------------------------------------------------------------------------------- Q: How long is test_preemptive supposed to run? A: For about 60 seconds. -------------------------------------------------------------------------------- Q: How long is test_wakeup supposed to run? A: For about 6 seconds. -------------------------------------------------------------------------------- Q: For cv, the documentation says that I need to associate a wait queue with the cv, but I have already associated a wait queue with the lock passed to cv_wait. Why do we need two wait queues? Is a thread supposed to be in two wait queues? A: If a thread acquires a lock, it is no longer in the lock's wait queue. If the condition is not met after acquiring the lock, then it should be waiting in cv's wait queue until the condition is met. In either case, the thread will only be in the lock's wait queue (lock not acquired) OR in the cv's wait queue (lock acquired, condition not met, give up the lock, and wait for condition to be met). -------------------------------------------------------------------------------- Q: what does spin() really do? Does it trigger the interrupt handler? A: If you look at the code in interrupt.c, spin() loops in while(1) for a certain period of time. This time is based on gettimeofday system call, which returns the real time of the clock, and so it doesn't depend on other threads having run in between. Also, it doesn't actually trigger the handler, but by waiting long enough, a signal will eventually occur at some point while you are spinning. -------------------------------------------------------------------------------- Q: Can I call thread_yield within thread_sleep? A: They do slightly different things, but could have a common function that is called by both thread_yield and thread_sleep to do the common work. -------------------------------------------------------------------------------- Q: Are __sync_bool_compare_and_swap(), __sync_fetch_and_add() in test_thread.c atomic operations? A: Yes. See https://gcc.gnu.org/onlinedocs/gcc-4.2.3/gcc/Atomic-Builtins.html for their definitions and for other atomic operations provided by gcc. Note that gcc produces architecture-specific code for these operations, based on the specific atomic instructions supported by each architecture. We are using these atomic operations in the testing code for locking and synchronization because you have not yet implemented these operations (locking, condition variables) in your threads library (and also because we can't depend on them being correct!). --------------------------------------------------------------------------------