The Great Kernel: Willie under Windows 2000

Update: The patent for the kernel's underlying mechanism was approved after slight adjustments from the original application. The U.S. patent number is 6,035,321, granted March 7, 2000. The Voyager product described as being under development is now complete, being sold under the name "Ongoer" and marketed using the business name "Simtrol". The Willie Kernel also runs under Windows XP, and, in an experimental form, under Linux. A few details of the API have been modified since this document was originally written.

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.

Omega and the Omega Kernel

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.

Development of the Great Kernel

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.

Underlying patent-pending technology

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 Willie application programmer's perspective

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.

Events

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.

Handles

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 );

Event Identifiers

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 );

Event identifiers in the Great Kernel

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

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.

The event class

Event 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
};

void Signal()

Signal the event. For event my_event, my_event.Signal() is the same as wk_signal_event(my_event.id())

wk_eventid_t id()

Return the event identifier (event ID) for the specified event. Note that this does not return the event handle

wk_counter_t Counter()

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.

wk_counter_t Reset()

Reset the current context's counter for the event back to zero. The value of the counter before it was reset is returned.

bool Link(event&)

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.

bool Raise()

Signal the event, but only allow a context in the current calling scope to capture the event.

bool Await( wk_timeout_timeout )

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.

void Await()

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.

void Check()

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.

The WK_Context class

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.

WK_Context ( const char * class_name, wk_level_t level )

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.

wk_contextid_t id()

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.

wk_level_t Get_Level()

Returns the level assigned to the context.

const char * Get_Name()

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.

virtual void Start()

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.

void Associate ( event & my_event, WK_Handler my_handler, void * my_this [, bool uncaught] )

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();
    };
}

void Associate_Array ( event * events, WK_Indexed_Handler my_handler, size_t num_elements, void * my_this, bool uncaught )

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 );
    };
}

void Capture ( event & e )

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.

void Notice ( wk_eventid_t e, WK_Handler h, WK_Indexed_Handler hi, size_t my_index, void * my_this, WK_Notice_Option flag, wk_eventid_t alias_event )

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.

void Uncapture ( event & e )

Quit capturing or seizing the event e. Any defined association with a handler is discarded.

bool Raise ( event & e )

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.

void Signal ( wk_eventid_t e )

Signal the event.

int Await ( wk_timeout_t max_wait, size_t num_events, event * event1, ... )

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.

int Block ( wk_timeout_t max_wait, size_t num_events, event * event1, ... )

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.

int Await ( wk_timeout_t max_wait, size_t num_events, wk_eventid_t events [], bool exclusive );

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).

int Sleep ( wk_timeout_t duration )

Dawdle for the specified duration.

void Set_Level ( wk_level_t new_level )

Specify the level for the context. This function may be run in the constructor of the context only.

Marker( const WK_Context * ctx, const char * file, unsigned int line )

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;
}

Ctor_Marker( WK_Context * ctx, const char * ctx_name )

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.

Availability of WK_Context methods

The 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
* - Doable but unenforced violation of hierarchical context ordering.

Miscellaneous Global Functions

void wk_signal_event ( wk_eventid_t event_to_signal )

Signal event.

wk_contextid_t wk_current_context()

Return the context identifier for the context currently active on this thread of control. NULL is returned if no context be active.

wk_timeout_t wk_timeout_ticks ( int num_seconds )

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.

void wk_set_sys_id ( const K_Event_Handle * sys_handle )

Set the computer-identification portion of event handles (see patent application) to be generated by this system.

K_Event_Handle _K_Event_Handle ( const event * e )

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.

void _K_Signal_Event ( K_Event_Handle event_handle )

Signal an event using its event handle.

int wk_argc()

Return the count of arguments passed on the command line.

char * const * wk_argv()

Return an array of pointers to the text of arguments passed on the command line.

int wk_dispatch()

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.

int wk_shutdown()

Shut down the kernel.


See text of plaque awarded to Curtis for his kernel contributions
Go back to Curtis's home page