Part A: Here is pseudocode for the touch method of an object, so that the touch methods of all objects, taken together, will perform the marking phase of a mark-sweep garbage collector.
touch: myself.mark(true) for each field f of this object when f is a reference to another object if not f.marked f.touch endif end when end forNote, in most cases, the mark method will not have any iteration, but rather, will consist of explicit statements, for each field that is an object reference, as follows:
touch: myself.mark(true) if not fieldone.marked fieldone.touch endif if not fieldtwo.marked fieldtwo.touch endif . . .
Part B: Here is pseudocode for a sweep function that can be called on receipt of a notice of a heap-empty exception.
sweep: o = first repeat if o.marked o.mark(false) else o.done endif oo = o o = o.next until oo = last
Part C: The major problems that remains unsolved by the definitions of this hypothetical programming environment and by the answers to part A and B given above are:
First, we leave it to the user to call touch on the roots of the data structure and then call sweep in response to a heap-empty exception and then retry the allocation.
Second, we've done nothing to help identify the roots of the data structure. A reasonable definition would be: All global object references plus all object references in local variables of any stack frame are roots. Writing user-level code to find and mark these is difficult, particularly when there is deeply nested recursion.
Third, of course, we said nothing about the underlying heap manager.
It would be impossible. All deadlock models require the ability to construct a wait-for graph. We simply have no way to construct this graph because, when a thread waits on a thread-semaphore, we have no idea what other thread is expected to signal that semaphore. The fact that we are dealing with an or-model, as opposed to an and-model or a single-resource model is irrelevant.
The UNIX kernel is a large object containing file syste, process scheduler, and many other system resources. The kernels of modern operating systems, on the other hand, are small; typically, they contain only minimal scheduling, memory management, process management and interprocess communications mechanisms, leaving things like file systems and accounting entirely outside the kernel.