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 for
Note, 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.