A tour of the runtime

This section contains a brief introduction to the internal structure of the runtime system. This introduction is neither comprehensive nor tutorial; it is merely intended as a stepping stone for the courageous.

The runtime itself implements the garbage collector, Modula-3 startup code and a few miscellaneous functions. The runtime exists in the libm3/src/runtime directory.

The allocator and garbage collector are based on Joel Bartlett's ``mostly copying collector''. Since that paper, we've made a few modifications to support a growing heap and to use extra information that the Modula-3 compiler generates.

On some platforms (e.g. DS3100 and ALPHA_OSF) exceptions are implemented by PC tables and a stack walker. On all other platforms, exceptions are implemented with setjmp and longjmp. The jump buffers and scope descriptors are chained together to form a stack. The ThreadF interface holds the head of the chain. There is a distinct chain for each thread. When an exception is raised, the chain is searched. If a handler for the exception is found, the exception is allowed to unwind the stack, otherwise a runtime error is signaled. To unwind the stack, a longjmp is done to the first handler on the stack. It does whatever cleanup is necessary and passes control on up the stack to the next handler until the exception is actually handled.

Reference types are represented at runtime by a ``typecell''. Due to separate compilation, opaque types and revelations, it is not possible to fully initialize typecells at compile time. Typecell initialization is finished at link time. A typecell contains a type's typecode, a pointer to its parent typecell, the size of the types referent and method list if any, the type's brand, the number of open array dimensions, the type's fingerprint, and procedures to initialize the typecell, initialize new instances of the type, print instances of the type and trace the type for garbage collection.

At the beginning of the execution of the program, all global variables are initialized, and the main bodies of the modules are invoked. The table that lists the module to be initialized is generated by the linker part of the driver.

Other parts of the runtime, such as threads, are actually implemented in the base library.

On Unix, Thread switching is implemented with setjmp and longjmp. A timer interrupt (signal) is used to cause preemptive thread switching. The global variable ThreadPosix.self points to the currently running thread. The integer RT0u.inCritical is used by the runtime to prevent thread switching during garbage collection and other ``atomic'' runtime operations.

On Windows/NT, Modula-3 threads are implemented as Win32 kernel threads.