Overall, the projects were well done. Everybody wrote an essentially
correct queue implementation and semaphore implementation. The real
differences turned out to be in how the minithreads operations were
implemented: the best grades went to groups which dealt with the NT
kernel thread effectively, reused code
(e.g. minithread_fork can call
minithread_create), and had a clear and efficient
mechanism for cleaning up threads. The best thread-cleanup schemes
involved a "cleanup" thread waiting on a semaphore and being signalled
by terminating threads, or a check for threads on the cleanup queue
whenever a thread came out of a
minithread_yield(). Terminating the application once once
all the application-created minithreads have terminated also earned a
bonus to the grade.
Common problems included:
minithread_create to make a TCB for the
NT kernel thread: this is unnecessary, since it creates a stack for
the NT thread as well as the TCB. When a context switch occurs "away
from" the NT thread (e.g. it calls minithread_yield), the
TCB stack top pointer is overwritten with the pointer to the top of
the NT thread's stack. As a result, the stack allocated by the
minithread_create is lost (a memory leak). A
"stripped-down" version of minithread_create, with no
stack creation, avoided this problem.
minithread_switch directly to start the first minithread,
rather than minithread_yield or
minithread_stop to transfer control from the NT kernel
thread).
Ben Atkin