The Great Kernel is a particular multi-stack implementation of the Willie Kernel that runs on the Windows 2000 operating system. The first major intended application is the Voyager device-control system by Videoconferencing Systems, Inc.
The Willie Kernel is a low-level software module that manages the sequencing and interaction of other software. The Willie Kernel imposes a strict hierarchy on the flow of code in a system, precluding one code module from indirectly calling itself through other code modules which have been invoked: for instance, if module A invoke module B, then B cannot invoke module A. In return for this restriction, the Willie Kernel precludes resource deadlock, reëntrant data-clobbering, and certain other classical software bugs. The Willie kernel does provide a mechanism for a low-level code module to notify a higher-level code module using parameterless event signals.
The original Willie Kernel, called here the "Omega kernel", was made for use in the Omega video-conferencing system made by Videoconferencing Systems, Inc. (VSI). The Omega kernel operated on a single thread of control using a single stack under the Windows for Workgroups 3.11 operating environment from Microsoft Corporation.
Omega's chief purpose was the control of devices used in audiovisual systems; therefore it demanded concurrency in the processing of its code. The concurrency was realised by state-driven code. A few block-till-complete operations were provided in a few code modules. Due to the nature of the kernel, only the upper-level code modules would be blocked, while the lower-level code modules could continue their critical operation. The temporal duration of these block-till-complete operations was usually less than 200 milliseconds, but certain operations using a manufacturer-provided library call, could stall the systems for several seconds.
A new version of the Willie Kernel has been implemented using
the multi-threaded capabilities of Windows 2000 release
candidate 2 from Microsoft Corporation. This code, called here
the "Great Kernel" should also work under the current commercial
releases from Microsoft Corporation of Windows NT, Windows
98, and Windows 95, but it has not yet been tested in those
environments. Furthermore, the code is designed so that
dependencies on Windows is minimal, and it is anticipated that
the Great Kernel be made available in other environments. The
require underlying environment for Willie includes a threads
package with semaphores. The Willie Application program may
make use of the operating system's other services such as file
input and output, but a Willie encapsulting code-section wrapper
may be necessary if the file system is not sufficiently
reéntrant: e.g., if the standard C file functions don't provide
a distinct errno
value for each thread.
Development of a Willie Subkernel is anticipated as well, which
is a threads package completely devoid of any other operating
system.
Since the new Great Kernel uses multiple stacks rather than only a single stack as used by the Omega Kernel, delay and wait-for-event operations are permissible within a single thread of control without hampering the concurrent execution of other code modules.
The Willie Kernel (both the Omega kernel and the Great kernel) uses the patent-pendingtechnology described in the patent application Method for Enforcing a Hierarchical Invocations Structure in Real-time Asynchronous Software Applications by Richard Chapman Mays, assigned to Acis, Inc. A full copy of this patent application is available at http://gb.espacenet.com/ by requesting a copy of patent application "WO9600939".
The Great Kernel was written to follow the algortithms in Mr. Mays' patent application as closely as practical. The Great Kernel was written to provide support for C++ and W++ programs (W++ is an extension of C++ with features added especially in support of the Great Kernel); whereas the patent referred to generic code, not especially written with a particular environment in mind. Consequentially, the procedural interface of the Great Kernel differs from the exact names used in the patent application, although the complete functionality of the patent is supported.
Additionally, the Great Kernel has extensions to the base Willie Kernel. These extensions add support for the W++ language, initialisation & debugging not described in the patent, and some miscellaneous useful features.
This document describes the Great Kernel's particular implementation of the technology described in the patent application, but it does not attempt to describe the technology itself.
The Great Kernel is distributed in four files: willie.dll, willie.hxx, wk_dbug.hxx, and willie.lib. The willie.dll file contains the actual software code and is the only file which needs to be distribued with an application based on the Great Kernel.
The other files are only needed for developing software that uses the great kernel. The main header file which needs to be included by C++ and W++ applications using the kernel is willie.hxx. An auxiliary header file, wk_dbug.hxx, defines a few implementation-specific structures and functions needed only for debugging and statistical analysis of the Great Kernel. The willie.lib file is used for defining entry points into the willie.dll dynamically linked library.
An event is the occurrence of some incident of interest. In
the Great Kernel, an event is represented by a data object of
type event
.
Outside of the scope of context (to be discussed in just a moment), an event is a rather simple object. An event can be signalled to indicate that the associated incident of interest occurred. For anything useful to happen as a result of an event being signalled, there must be a context that is expecting the event.
The event handle, as described in the patent application,
for any given event may be found by using the
_K_Event_Handle
function described in the patent.
This handle may be passed around, even to other systems, to
allow code outside the scope of the event definition to signal
the event.
Before making any calls to _K_Event_Handle
or
_K_Signal_Event
, the programmer must first make a
call to the function wk_set_sys_id
to declare the
system- and network-identification components of the event
handle.
In the Great Kernel, system-specific event handles can be
assigned to events, but, since the Great Kernel does not have
built-in network facilities, an event on another system cannot
be directly signalled using the _K_Signal_Event
function; the programmer must be responsible for the network
communications to notify the remote system (identified in the
event handle) that the event needs to be signalled.
Here is a sample usage of event handles:
// set up system identification
static const K_Event_Handle my_sys_id =
{
"don't care", //local_id value ignored
"willie.acisinc.com", //sys_id
"DNS", //sys_id_type
"ARPA" //net_id
};
wk_set_sys_id ( & my_sys_id );
// declare an event
event my_event;
// declare and determine handle for the event
K_Event_Handle handle_for_my_event =
_K_Event_Handle ( &my_event );
// signal the event using its handle
_K_Signal_Event ( handle_for_my_event );
In the Great Kernel, each event has a numeric event
identifier (event ID) assigned to it. Event identifiers are of
the integral type wk_eventid_t
. Unlike the massive
event handles, event identifiers may efficiently passed around
from one section of code to another. Event identifiers may be
passed around so that they may be signalled by other code
sections which may be outside the scope of the
event
data item itself.
Event Identifiers are not a part of the patent technology proper, but they have proved of great importance and use in both the Omega Kernel and the Great Kernel implementations of the Willie kernel.
The value zero (0) indicates an invalid event identifier; real events are never given an event identifier of zero (0).
The .id() member function of the event class returns the event identifier for an event. For example,
event my_event;
wk_eventid_t event_id_for_my_event = my_event.id();
wk_signal_event ( event_id_for_my_event );
In the Great Kernel version of the Willie Kernel, event identifiers are a 32-bit signed integer. They are always in the range of [257,231-1]; other values are reserved for possible special significance to be assigned in the future. The low 20 bits (bits 0-19) of the event identifier form the event index into an array of the internal WK_Event_Data elements which keep track of data for each event. The index 0 is expressly invalid; it points to a dummy uncaptured WK_Event_Data element. Bits 20-31 of the event identifier form a sequence number. The sequence number for all events is initialised to some arbitrary value based on the time the system starts. To minimise reuse of event identifiers, the sequence number is incremented each time an event is assigned to a given slot: for instance, suppose there is an event with id 0x12300005 (stored in index 5, with sequence 0x123) which is deleted. When the next event is created, the system decides to reuse index 5, but the sequence number will be incremented to 0x124, making the new event's id 0x12400005.
In the Omega kernel, event identifiers were merely 16-bit indices with no sequence numbers. However, they were numbered starting with 100, reserving lower numbers for event enumerations within particular contexts (a concept not directly applicable to the Great Kernel). However, to enable code written for the Omega kernel to work with the Great Kernel, the Great Kernel skips sequence number 0x000 for events assigned to lower-numbered slots (1-256). For instance, in our previous example, assume the original event number were 0x7FF00005. In this case, the 11-bit sequence number will roll over to 0x000, but since 0x00000005 is within the range [1,127], the sequence number is incremented again to 0x001, making the new event identifier 0x00100005.
Contexts are data objects. Each context is the instance of a C++ class virtually derived from the base class type WK_Context. In W++, the derivation from WK_Context is implicit rather than explicit.
Code within any particular context only executes on one thread at a time. If contexts A1 and A2 both want to call context B at the same time, the kernel allows only one of the invocations to take place at a time: the other context is blocked until the first one returns from the context.
This syncrhonisation is performed by using a context Marker. In C++, this must be done manually by the programmer; whereas, in W++, the Markers are generated automatically.
Contexts allow the handling of events.
event
classEvent objects are of the type event, defined in the willie.hxx file. Here is an edited excerpt showing the definition of the event class. Irrelevant sections (e.g., private sections) have been omitted.
class event
{
public:
event();
~event();
void Signal(); // signal this event
wk_eventid_t id() const; // get event ID
// the following member functions are only valid
// from within the scope of a context
wk_counter_t Counter() const; // get counter
wk_counter_t Reset(); // set counter to 0
bool Link(event&); // link another event
void Raise(); // raise this event
bool Await(wk_timeout_t);// wait for event
void Await(); // wait indefinitely
bool Check(); // check for event
};
Signal the event. For event my_event
,
my_event.Signal()
is the same as
wk_signal_event(my_event.id())
Return the event identifier (event ID) for the specified event. Note that this does not return the event handle
Return the current context's counter for the event. Initially, the counter is zero. The counter is increased by one each time the event is signalled or raised and caught by this context. The counter is decreased by one each time the event handler is processed or someone successfully waits for the event.
Reset the current context's counter for the event back to zero. The value of the counter before it was reset is returned.
Link the event to another specified base event. Whenever the base event is signalled, this linked event is also signalled. Multiple events may link to another base event, and all of the linked events are triggered when the base event is signalled.
Signal the event, but only allow a context in the current calling scope to capture the event.
Wait for the event to occur, up to the specified timeout.
The timeout is specified in ticks. A tick is a unit of time
equal to one second or less. The number of ticks in a given
number of seconds can be determined by the function
wk_timeout_ticks
.
The return value from this form of Await is true if the event
occurred within timeout ticks (or it had already occurred before
Await was called); the return value is false if the event did
not occur within timeout ticks. The event handler for this
event, if defined, will have run before returning. Event
handlers for other events may be run while waiting, even if the
awaited event had occurred before Await was called.
Wait indefinitely for the event to occur. The event handler for this event, if defined, will have run before returning. Event handlers for other events may be run while waiting, even if the awaited event had occurred before Await was called.
Check to see whether or not the event have already occurred; if so, run its handler (if applicable). Event handlers for other events will not be run. This routine does not wait for the event to occur.
All contexts are virtually derived from the WK_Context class. Here is the edited definition of the WK_Context class from the willie.hxx:
class WK_DLLPORT WK_Context
{
public:
WK_Context();
WK_Context
(
const char * class_name,
wk_level_t level = WK_LEVEL_MAX
);
virtual ~WK_Context();
typedef void (*WK_Handler)( void * wk_this );
typedef void (*WK_Indexed_Handler)( void * wk_this, size_t index );
wk_contextid_t id() const; // return context id
enum { MAX_EVENTS_IN_WAIT = 64 };
wk_level_t Get_Level() const;
const char * Get_Name() const
{
// returns class name of outermost class
// in the form of typeid(*this).name()
return class_name;
}
protected:
virtual void Start();
// Inserted at the begin of a context constructor
// ~CtorNotify() will call Constructed() if 'name'
// be the outermost context
class WK_DLLPORT Ctor_Marker
{
public:
Ctor_Marker
(
WK_Context * ctx, // context being constructed
const char * name // class name of context
);
Ctor_Marker
(
WK_Context * ctx,
const char * name,
const char * filename, // __FILE__ macro of constructor
int line // __LINE__ macro of constructor
);
~Ctor_Marker();
};
void Associate
(
event &r my_event,
WK_Handler my_handler,
void * my_this,
bool uncaught = false
);
// The preprocessor (or user) uses Associate to
// associate events with the Handler member function
void Associate_Array
(
event * my_event_array,
WK_Indexed_Handler my_handler,
size_t num_elements,
void * my_this,
bool uncaught = false
);
// This version of Associate is used internally by
// Associate_Array but may be useful by programmers
// in special cases to handle multiple different events
// by the same handler, using the index to discriminate
// among the various events handled.
// Capture is used for noticing events waited
// for yet which have no event handler, or for
// turning on the capturing of an event for
// which an event handler was defined yet not
// captured.
void Capture
(
event &r e
);
void Capture
(
wk_eventid_t e
);
// Notice is the general-purpose routine that can
// handle Capture as Event, Capture, and
// Seize has defined in the patent.
void Notice
(
wk_eventid_t e, // event id to notice
WK_Handler h, // handler if not indexed
WK_Indexed_Handler hi, // handler if indexed
size_t my_index, // don't care if hi == NULL
void * my_this, // this ptr of captor
WK_Notice_Option flag, //
wk_eventid_t alias_event // alias event id or 0
);
void Uncapture
(
event &r e
);
void Uncapture
(
wk_eventid_t e
);
void Raise
(
event &r e
);
bool Raise
(
wk_eventid_t e
);
void Signal
(
wk_eventid_t e
);
int Await
(
wk_timeout_t max_wait, // maximum wait (ticks)
size_t num_events, // number of events in list
event * event1, // first event
... // additional events
);
int Await
(
wk_timeout_t max_wait,
size_t num_events,
wk_eventid_t events[],
bool exclusive
);
int Await
(
wk_timeout_t max_wait, // maximum wait (ticks)
size_t num_events, // number of events in list
wk_eventid_t event1, // first event
... // additional events
);
int Block
(
wk_timeout_t max_wait,
size_t num_events,
wk_eventid_t event1,
...
);
// Block is like Await except that it permits the event
// handler of any captured event to run, rather than just the
// events being waited for.
static
void Sleep
(
wk_timeout_t duration
);
wk_level_t Set_Level
(
wk_level_t new_level
);
class WK_DLLPORT Marker
{
public:
Marker( const WK_Context * new_context );
Marker
(
const WK_Context * new_context,
const char * filename, // __FILE__
unsigned int line // __LINE__
);
~Marker();
};
};
General Note: when there are two forms of a function in which one takes an event reference or event pointer and the other takes an event identifier, it may be assumed that the two functions are equivalent. Usually, the form with the identifier is provided to ensure compatibility with programs originally written for the Omega kernel.
There are two constructors for the WK_Context class. The constructor with no parameters is permitted for ease in declaring certain classes, but this constructor precludes defining a Start module for the context (q.v.), and the context's level must be defined, anyway, before the context can do anything useful.
An object of type WK_Context
class is not
usually allocated overtly; normal contexts are actually objects
of a class that virtually inherits from type
WK_Context
. Each context definition should have a
constructor that has a call to the WK_Context constructor in its
initialisation list. In the case of multiple inheritance, since
all context classes inherit virtually from WK_Context, the
constructor is only actually called once, called using the
parameters in the actual object's constructor.
For instance, suppose we have a context C
derived from a base context B
and we declare an
object of type C
. We would have the following C++
code:
class B : public virtual WK_Context{
B(wk_level_t my_level): WK_Context("B", my_level)
{ /*...*/ };
};
class C : public virtual WK_Context, public B {
C(wk_level_t my_level): WK_Context("C", my_level), B(my_level)
{ /* ... */ };
}
void some_fucntion() {
C * my_object = new C ( 123 );
}
When C
is created, the WK_Context
constructor is called using the class_name of
"C"
. Although the constructor of B
is
called from within the constructor of C
, the call
to the WK_Context
constructor with
class_name "B"
is not made since the
WK_Context
component of the class has already been
called.
One might think it unnecessary to pass a valid
level argument to B
's constructor from
C
's constructor since that argument is not actually
used in this example. However, the constructor of
B
may want to use the level argument for
something else besides just passing it on to the
WK_Context
constructor, or it may make a Set_Level
call that would
override the originally specified level value with the
false
value given to it.
Returns the kernel-assigned context identifier (context ID) for this context. The Great Kernel assigns each context a non-zero identifier similar to the event identifier given to each event. The context ID 0 is never valid. All context identifiers are unique for any given time. Although the kernel makes some effort to avoid re-using the same context identifier, it is still possible for a context identifier might be reused at some later time.
Returns the level assigned to the context.
Returns the name of the outermost class for this context. The text string returned by this function is deleted when the context is deleted; therefore, the results to this routine should not be stored permanently.
Does initialisation needed for this particular context. In
the base WK_Context
class, this function does not
do anything. However, derived contexts (i.e., classes
derived from WK_Context
) perform any needed
start-up function here. This code is executed at the defined
level for the context in contrast to the constructor whose code
runs at the level of its creator. In C++ a derived class should
call the Start
methods of its
base classes before doing of its own code. In W++, the
Start
modules are automatically.
Define the event handler which is invoked when the specified
event occurs. my_event is the event with which the
function is being associated; my_handler is the
routine invoked when the event is caught, my_this is
the this pointer that the event handler routine uses to have
access to the context, and uncaught is a flag
indicating whether the event should be captured at this moment
or the event should just have its handler defined without
actually capturing the event at this point. The default is for
the event to be caught: i.e., uncaught's
default value is false
.
Much effort has been exerted to determine the optimal definition of parameters and their types for this routine. The trouble stems from the fact that the this pointer for a context and its virutally inherited base are not the same. Furthermore, C++ implementations do not deal with pointers to member functions the way that would be best suited for the kernel.
It is necessary that my_handler be a static member
function (or possibly a normal old function) that takes a single
argument of type void*
. This single argument is
passed the my_this value when the event handler is
activated by the kernel.
In W++, the Associate
functions are called
automatically for events defined with handlers. For example,
here is a sample of W++ code and its C++ equivalent:
// W++ code
context A
{
public:
handled event e() {
do_something();
}
};
// equivalent C++ code
class A : public virtual WK_Context
{
public:
A() : WK_Context( "A", WK_LEVEL_MAX )
{
WK_Context::Associate ( e, wk_stub_e, this );
};
event e;
private:
void wk_stub_e ( void * my_this )
{ (static_cast<A*>(my_this))->wk_real_e(); }
void wk_real_e () {
do_something();
};
}
Associates an array of events with an indexed handler. The indexed handler is like a normal event handler except that the index of the event signalled is passed as the second parameter to the event. Unfortunately, in the initial version of W++, there is no way to transparently define a handler for an array of events as there is for a single event.
Associate_Array Example:
class A : public virtual WK_Context
{
public:
event e[ 20 ];
A() : WK_Context( "A", 123 )
{
WK_Context::Associate_Array(e,wk_stub_e,20,this);
};
private:
void wk_stub_e ( void * my_this, size_t index )
{ (static_cast<A*>(my_this))->wk_real_e( index ); }
void wk_real_e ( size_t index) {
// e[index] was signalled!
do_something( index );
};
}
Start capturing the event e. This call is
automatic in W++ for events declared with the
captured
keyword, or for
non-uncaptured
events in private
or
protected
sections of the context definition.
Notice the event e. This is an all-encompassing
routine which can perform the function of Capture
,
Associate
, Associate_Array
, and also
the Seize and Capture_As_Event functions of the patent
application. e is the event to notice. h
is the normal (non-indexed) handler for the event or
NULL
if none. hi is the indexed handler
for the event or NULL
if none. Both h
and hi cannot be set for the same event.
my_index is the index number to be passed to the
indexed handler; my_index should be 0
when hi is NULL
. flag is
WK_CAPTURE
to capture the event;
WK_SEIZE
to seize the event; or
WK_ASSOCIATE_ONLY
if the event is not being
captured or seized right now but only having its associate event
handler defined. alias_event is normally
0
, but if the programmer desire to define an alias
event as described in the patent application, this value is the
event identifier of the alias event.
Quit capturing or seizing the event e. Any defined association with a handler is discarded.
Raise the event e; i.e., signal the event, allowing only those contexts which are in the current calling chain to capture or seize the event.
Signal the event.
Wait for one of the specified events to occur. This function
may have additional arguments which are additional events.
max_wait is the maximum amount of time in ticks to wait
for one of the events. A tick is a unit of time less than or
equal to one second; the number of ticks in one second is
returned by the global function call
wk_timeout_ticks(1)
;
for the first version of the Great Kernel, one tick is one
millisecond. num_events is the number of events
being awaited; it must be one or more. event1 is the
first event awaited; additional events may be specified after
event1; the total number of events specified should
be num_events.
This Await
function is not the same as the
Wait-for operation described in the patent application. The
Await
function allows any of the context's event
handlers not already in progress to execute while waiting, but
the Wait-for operation in the patent application only permits
the execution of event handlers of awaited events. The Wait-for
operation of the patent is implemented using the similar
Block
member function.
Wait for one of the specified events to occur. This function
may have additional arguments which are additional events.
max_wait is the maximum amount of time in ticks to
wait for one of the events. A tick is a unit of time less than
or equal to one second; the number of ticks in one second is
returned by the global function call
wk_timeout_ticks(1)
; for the first version of the
Great Kernel, one tick is one millisecond.
num_events is the number of events being awaited; it
must be one or more. event1 is the first event
awaited; additional events may be specified after
event1; the total number of events specified should
be num_events.
While waiting, only the event handlers for the awaited events may be run in the waiting context.
Wait for one of the events enumerated in the
events array to occur. This function is used
internally by the kernel to implement the other
variable-argument Await
and Block
functions, but it is also made available to the programmer who
may want to construct his own array of events to wait for. The
exclusive parameter is true to have patent-compliant
Block
behavior or false to have the behavior of the
Await
function allowing the execution of the
handler of any event captured by the context.
The Await
function was developed in order to
allow the execution of the handlers of private base-class events
which generate events caught by the derived class. Since both
the base and derived classes form a single context in C++ and
W++, the patent-described wait-for operation on only the derived
event (the only event in the derived class's scope) would never
occur since its dependent event could never run. The
implementor of the Great Kernel felt that the programmer has a
reasonable expectation that other things might be done
(e.g., events handled) during a wait operation, and that
he may not even be aware of all the base-class event
dependencies if modifying someone else's code or library.
Mr. Mays' idea in the wait-for operation of the patent is
that the programmer would like to have complete control over
what event handlers might be executed while waiting. This full
control is implemented in the function named Block
,
which only will run the handlers of events being waited for in
the waiting context.
Note that, when using the Await
function, event
counters of events not being awaited may be decremented since
the counter is decremented each time the event handler is run
(or the handler might reset the counter to zero).
Dawdle for the specified duration.
Specify the level for the context. This function may be run in the constructor of the context only.
Mark the beginning of protected context code. Each
public
member function and member operator of a
context requires an automatic object of type
WK_Context::Marker
to be allocated at the
beginning. The constructor of the marker ensures that the
context is not already active. If it be active, then the
constructor waits until it can acquire exclusive access to the
context. The destructor of the marker relinquishes access. The
constructor and destructor of the Marker
class are
equivalent to the Enter_Context and Exit_Context functions
described in the patent application.
The allocation of Marker variables is automatic in W++. In C++, the programmer must expressly allocate them.
Example:
class A : public virtual WK_Context
{
/*...*/
public:
void Set_x ( int new_value )
{
Marker dummy_var ( this, __FILE__, __LINE__ );
x = new_value;
};
private:
int x;
}
Marks the constructor. The constructor of a context
allocates an object of type Ctor_Marker
just as a
public
function or operator allocates a type of
Marker
. The Ctor_Marker
variable is
strictly only necessary if the context being defined or any of
its base contexts (i.e., contexts from which the context
is inherited) have a Start
method defined, but is
acceptable to allocate a Ctor_Marker
in a
constructor even when it is not strictly needed.
The allocation of Ctor_Marker
variables is
automatic in W++. In C++, the programmer must expressly
allocate them.
WK_Context
methodsThe following chart shows what functions of
WK_Context
are available in which environments.
in context constructor | in context function, operator, or destructor | from higher-level context | from peer or lower-level context | from generic C++ code | |
---|---|---|---|---|---|
new | no | no | yes | yes | yes |
delete | no | no | yes | no | no |
id | yes | yes | yes | yes* | yes* |
Get_Level | yes | yes | yes | yes* | yes* |
Get_Name | yes | yes | yes | yes* | yes* |
Start | no | no | no | no | no |
Associate | yes | yes | no | no | no |
Associate_Array | yes | yes | no | no | no |
Capture | yes | yes | no | no | no |
Notice | yes | yes | no | no | no |
Raise | no | yes | no | no | no |
Signal | yes | yes | no | no | no |
Await | no | yes | no | no | no |
Block | no | yes | no | no | no |
Sleep | no | yes | no | no | no |
Signal event.
Return the context identifier for the context currently
active on this thread of control. NULL
is returned
if no context be active.
Return the number of ticks in num_seconds seconds. For any given system, this value should always be constant. For the first version of the Great Kernel, the value is always (1000×num_seconds) since one tick is one millisecond.
Set the computer-identification portion of event handles (see patent application) to be generated by this system.
Return the event handle for the specified event. A call to
wk_set_sys_id
must be made before _K_Event_Handle
can be called.
Signal an event using its event handle.
Return the count of arguments passed on the command line.
Return an array of pointers to the text of arguments passed on the command line.
Start the kernel. This must be called by the programmer if
he define his own main
or WinMain
routine; otherwise, the kernel provides a stub main
routine that makes this call. It is acceptable and anticipated
to have static constructors of contexts and events run before
this routine.
Shut down the kernel.