1.0 appCORE Overview


appCORE is an ANSI C, Actor Model, message passing, software framework based upon a Serial Dispatch Queue (SDQ) architecture. This SDQ architecture forms the foundation for the development of reliable, lock-free, scalable applications. Distributed as source code, appCORE is a Software Development Kit (SDK) for building applications that support modern multicore processors and concurrent programming. appCORE supports Windows, LINUX, Solaris, Mac OS-X, and Android. Future releases are planned for Wind River's VxWorks and Green Hill's INTEGRITY operating systems.

appCORE is a process, threading, and message passing architecture built on posix threads (pthreads). The internal threading and messaging architecture supports both message and Input/Output (IO) processing within a single thread. An elegant multicore dispatcher extends the SDQ architecture seamlessly to multicore processors. appCORE applications are the antithesis of the ad-hoc, deadlock prone, high maintenance design approach of "threads, objects, locks, and callbacks" commonly seen in multi-threaded applications. With appCORE as the foundation, Software Development Life Cycle (SDLC) costs are vastly reduced compared to ad-hoc approaches. Scalable, reliable, high performance, multi-threaded applications are directly achievable.

Some of the important questions appCORE answers are:

appCORE provides a consistent, proven framework your whole team, and company, can leverage. The use of appCORE in just a single application, or device, will save thousands of hours of developer time. If used in multiple applications, or across a family of devices, or across an organization, the SDLC savings are simply astronomical.

In addition to its core architectural features, appCORE provides the following RAD facilities and capabilities:

Application development is further supported by a multi-platform, command line build system based on scons and a directory structure delivered complete with appCORE source code, header files, samples, templates and directories for your applications and libraries. For Windows development, the build system is able to produce project and solution files for Microsoft's Visual Studio tools.

Taken together, the appCORE SDK, sample applications, code templates, and development tools provide an unparalleled foundation for building high performance, reliable, and maintainable applications.

  +--------------------------------------------------------------+  
  |                            Application                       |  
  +-------------+-------------+-------------+------------+-------+
  |                            appCORE SDK                       | 
  +-------------+-------------+-------------+------------+-------|    
  |  Messaging  |  Threading  |  Dispatch   |  Services  | Utils |
  +-------------+-------------+-------------+------------+-------+
    
  +----------------+ +-----------+
  |  Build System  | |  Samples  |  
  +----------------+ +-----------+
   
  +---------------------+ +-----------+    
  | Directory Structure | | Templates |
  +---------------------+ +-----------+                                           
Figure 1 - appCORE Components

1.1 Process Architecture


The appCORE process architecture implements the simple, but powerful, "Startup-Run-Shutdown" (SRS) design pattern. In the default case, an appCORE application consists of three user specified functions - a Startup function, a Message Handler function, and a Shutdown function. These three user functions are executed in the context of a new thread, created by appCORE, that becomes the application's global SDQ thread. This application thread will have an input message queue, and will process appCORE messages serially, on a First-In-First-Out (FIFO) basis. Hence the name Serial Dispatch Queue.

The Startup function prepares the application to process messages. Typically, this function would initialize parameters, create other SDQ threads, create multicore dispatch components, specify ports to listen on, or open communication channels using the networking facilities. The Shutdown function is responsible for cleaning up state before the application exits. Normally, this function terminates threads, shuts down subsystems, cleans up networking activity, and frees the application's context. What these two, optional, functions do is entirely up to the user. The third, and normally, the central function in an appCORE application, is the Message Handler function. This function is normally just a simple switch statement that processes user defined messages by message type.

The following code fragment illustrates a simple main function. Error checking is omitted for clarity. Referring below, two specification data types are required. The coreSPEC_t type is used to configure appCORE. The coreAppSPEC_t type is used to configure the application. First, appCORE is initialized via the call to coreInit(). On return, appCORE's services are up and running. Second, the application is started via the call to coreAppStart(). The coreAppStart() function starts the application's SDQ thread and then executes the startup function, in the context of the new thread. The coreAppStart() function returns the status of the Startup function. If successful, the application thread will proceed to wait on its input queue for messages, until directed to exit, either internally or externally.

The main thread calls coreAppWaitOnStop() to wait for the application thread to exit. Before the application thread exits, the ShutDown() function is executed. This allows the application to cleanup properly. The function coreDeinit() is called last to shutdown appCORE. At this point the process may exit cleanly.

Please refer to the on-line manual pages of each function for the full range of options and configuration parameters.

#include "appcore.h"

int StartUp( void* Context ) {...}
int MsgHandler( void* Context, coreMsg_t* ) { switch( type ) ... }
int ShutDown( void* Context ) {...}

int main( int argc, char *argv[] )
{
coreSPEC_t coreSpec;
coreAppSPEC_t appSpec;
int StartStatus;

    bzero( &coreSpec, sizeof( coreSpec ) ); // default configuration
    coreInit( &coreSpec );
	
    bzero( &appSpec, sizeof( appSpec ) );   // default configuration
    appSpec.StartUp     = StartUp;          // setup application handlers
    appSpec.MsgHandler  = MsgHandler;
    appSpec.ShutDown    = ShutDown;

    StartStatus = coreAppStart( &appSpec );
    coreAppWaitOnStop();
    coreDeinit();
    return( StartStatus );
}

The following diagram illustrates the execution flow. If the feature is enabled, and the application is run from the command line, the coreAppWaitOnStop() function, will provide the user with a configurable command prompt. This is a very useful feature during all SDLC phases.

    main()
      |
      V
    coreInit()           ===>   Configure & start appCORE Services
      |                           
      V
    coreAppStart()       ===>   New main SDQ thread	
      |                              |
      |                              V
      < Status          <===      Startup()
      .                              |
      .                              V
      .                           MessageHandler() ===> switch( Msg.Type ) { case 1... }
      .                              |
      .                              V 
      .                           Shutdown()
      .                              |
      V                              V
    coreAppWaitOnStop() <===   Exit thread
      |
      V
    coreDeinit()         ===>   Stops appCORE Services
      |
      V
    exit()
Figure 2 - Process Flow

1.2 Thread Architecture


The appCORE threading architecture, like the appCORE process architecture, also implements the "Startup-Run-Shutdown" (SRS) design pattern. An appCORE thread, created with a default specification, will have an input message queue, and will also process appCORE messages serially, on a FIFO basis.

In the simplest case, an appCORE thread may have three user specified functions - a Startup function, a Message Handler function, and a Shutdown function, just as in the process model. The Startup function prepares the thread to process messages. Typically, this function would allocate and initialize the thread's context, setup parameters, open communication channels, etc. The Shutdown function is responsible for cleaning up the thread context before the thread exits. What these two, optional, functions do is entirely up to the user. The third function is the Message Handler function. This function is normally just a simple switch statement to process user messages by type. Again, just like at the process level.

However, an appCORE thread permits the specification of two additional functions to wait for and process IO, namely the IOWaitHandler and IOHandler functions. If the IOWaitHandler is NULL, then appCORE's default wait handler is used. If a message is sent to the thread, appCORE handles waking up the thread and calling the Message Handler function. If IO is ready to be processed, appCORE also handles waking up the thread and calling the IOHandler function with the IO handle. Threads that process IO in addition to messages are designated IO-Message threads. IO-Message threads are a very effective design pattern. IO-Message threads can significantly simplify an application as a single thread can process both data and control paths, or both transmit and receive paths, for example. The following code fragments, illustrates how both types of threads are created and started. Error checking has been omitted for clarity.

#include "appcore.h"
...
int StartUp( void* Context ) {...}
int MsgHandler( void* Context, coreMsg_t* ) {...}
coreIOWaitStatus IOWaitHandler( void* Context ) {...}
int IOHandler( void* Context, int, coreIOWaitStatus_t ) {...}
int ShutDown( void* Context ) {...}
...
coreThreadSPEC_t    Spec;
corehTHR_t          hThr1, hThr2;
int                 Status;
int                 Param1 = 1;
int                 Param2 = 2;
...
    bzero( &Spec, sizeof( Spec ) );
    Spec.ParamBlock = (void* )Param1;
    Spec.StartUp    = Startup;
    Spec.MsgHandler = MsgHandler;
    Spec.ShutDown   = Shutdown;

    Status = coreThreadStart( &Spec, &hThr1 );
...    
    bzero( &Spec, sizeof( Spec ) );
    Spec.ParamBlock    = (void* )Param2;
    Spec.StartUp       = Startup;
    Spec.MsgHandler    = MsgHandler;
    Spec.IOWaitHandler = IOWaitHandler;
    Spec.IOHandler     = IOHandler;
    Spec.ShutDown      = Shutdown;	
	
    Status = coreThreadStart( &Spec, &hThr2 );
...

Thread shutdown is a bit more complicated as the thread may exit due to an internal condition, or via an external command. The functions coreThreadStop() or coreThreadStopJoin() are used to request that a thread exit, either asynchronously or synchronously, respectively. If a thread exits due to an internal condition the thread must detach itself to avoid leaking resources. In all cases, appCORE's standard thread wrapper handles the details regarding thread exit processing. The following diagram illustrates the execution flow.

    coreThreadStart()    ===>   Create and start new thread	
      |                            |
      |                            V
      < Status          <===    Startup()
      .                            |
      .                            V
      .                         MessageHandler() ===> switch( Msg.Type ) { case 1... }
     

    coreThreadStop()     ===>   Sends thread stop message
      .                            | 
      .                            V
      .                         Shutdown()
      .                            |
      V                            V
                                Thread Exit

    OR

    coreThreadStopJoin() ===>   Sends thread stop message
      |                            | 
      |                            V
      |                         Shutdown()
      |                            |
      |                            V
      < Status          <===    Thread Exit
      .
Figure 3 - Thread Flow

1.3 Message Architecture


appCORE is a message passing framework. The standard appCORE message, coreMsg_t, is used for all communications. It is used for intra-thread communication within a process and can also be used for intra-process communication over a network channel. A coreMsg_t has a fixed header and a variable payload. The SDK provides a complete set of functions to manage appCORE messages. Message allocation may be heap or pool based, depending on configuration. This system-wide, standard message format, is a very powerful, unifying, design pattern. The data type is shown below.

typedef struct _core_msg_
{
    coreMsgHdr_t    Hdr;
    unsigned char   Payload[ 1 ];
}
coreMsg_t;

1.3.1 Message Processing

The standard appCORE thread has an input message queue and waits for the arrival of messages. The thread consumes no CPU cycles when its message queue is empty. When a thread's input queue is not empty, the thread is scheduled to run by the operating system. The thread then removes the message form its input queue and processes the message. Normally, the message is processed by message type, via a simple switch statement. The developer implements only the message handler function and the code to process each message. appCORE handles everything else. The following code snippet illustrates a standard message handler function.

int MsgHandler( void* Cntxt, coreMsg_t* pMsg )
{
int	rc;

    switch( CORE_MSG_TYPE( pMsg ) )
    {
    case AppMsgTypeOne: rc = ProcessMsgOne( Cntxt, pMsg ); break;
    case AppMsgTypeTwo: rc = ProcessMsgTwo( Cntxt, pMsg ); break;
    .
    .
    }

    return( rc );	
}

One important feature of message passing systems is testability. The structure of the above message handler function is trivial and all the code for the application resides in the message processing functions. There is a one-to-one relationship between messages, handler functions, and test code. Message passing systems are nearly self documenting at the message layer. This provides enormous potential for savings when realized by an organization.

1.3.2 Lock-Free Code

The processing of a message, within the context of a thread, is an atomic operation. A thread may be suspended and resumed by the operating system many times during the processing of a single message. However, if the only way to modify a thread's internal data is via the processing of a message, then the code that is processing the message does not require a lock. The structure of the system renders a lock unnecessary. The message passing architecture allows the application code to be written in a single threaded, lock-free, manner.

appCORE provides a Request/Response facility that provides a synchronous interface layer on top of a SDQ thread. This allows the creation of synchronous interfaces to message based services. Global, system resources can be synchronized using the Request/Response facility. appCORE makes use of this facility internally.

1.3.3 Thread-Specific Callback Functions

Today's software developers have come of age using callback functions. The Windows programming API established callback functions as a defacto programming standard. Java and C# have continued with this model as well. Unfortunately, callback functions and multi-threaded programming are a volatile, dangerous, and costly mix. Where there are callback functions there must be context; where there is context in a multi-threaded environment, there must be locks. Where there are locks, there is complexity, race conditions, reference counts, and deadlock. This all contributes to vastly increased SDLC costs and delayed time-to-market.

appCORE solves the callback problem by using a Thread-Specific Callback (TSC) design pattern. The message passing framework is used to execute the callback function in the context of the original thread. In this way callback functions are lock-free too. appCORE utilizes Thread-Specific Callback functions in the Timer Facility and the Networking Facility. For example, when a timer expires, a timer expiration message is dispatched to the thread that started the timer. appCORE handles the internal message type and executes the user's callback function in the context of the originating thread. The timer expiration handler function can be lock-free and written in a single threaded fashion. If an application must interface to a component that uses callback functions, the preferred solution is to create a message from the callback function and dispatch it to the appropriate thread, essentially decoupling the operation, and terminating the callback thread.

1.3.4 Orthogonality

In mathematics, the property of Orthogonality, in simplified terms, means to not overlap. For example, orthogonal vectors are at right angles to each other. In a message based system, each message represents an independent unit of work. appCORE, via its SDQ structure, is an orthogonal message processing machine, within the context of each thread. Ideally, the goal is for each message to be independent and not overlap in function. This goal produces a low level of coupling across the application's message set. If the processing of message A is changed, the effect on any other message is minimal. Systems that possess the property of Orthogonality have reduced development and testing time precisely because it is easier to verify designs that have a high degree of functional encapsulation and therefore do not cause side effects nor depend on them.

1.4 Specification Data Types


The appCORE SDK uses Specification Data Types (SDT) to create and configure appCORE components. SDTs are used as a succinct way to organize all the parameters an appCORE function needs in one place. They are a consistent, compact way to simplify function argument lists, and enable the underlying functionality to evolve without any change to the function's signature. This approach allows functions to evolve over time, yet retain backward compatibility. In all cases, the caller MUST zero the SDT before filling in the desired elements. In this way default behavior and backward compatibility is ensured. SDTs are typed 'C' data structures with a consistent naming convention - they all end with SPEC_t. For example, some of the SDTs are:

1.5 Handles


When an appCORE component is created, like a thread, a timer, a command, a command session, or a multicore dispatcher, a handle is returned. The handle is not a pointer and can not be de-referenced, but rather is an integer that is used internally to access the component's context. This design pattern promotes application stability by eliminating the risk of memory faults via stale pointers. The convention for handle naming is coreh[COMPONENT]_t, where COMPONENT is as shown below:

1.6 Design Patterns and Conventions


appCORE implements numerous design patterns and follows many conventions. Most are standard 'C' or POSIX conventions. Others are unique to appCORE. Some of the main conventions are listed below:

1.7 Why ANSI C?


Though appCORE is written in ANSI C, it is fully compatible with the C++ programming language. appCORE is low-level, multi-platform, system code. As such, ANSI C was chosen over C++ for simplicity and portability. appCORE is the foundation for your C++ objects.

1.8 Sample Applications and Templates


Sample applications and templates are provided to speed up your application development. The samples and templates are meant to be used as a starting point for your applications. The following samples are available:

1.9 Target Applications


appCORE is suited to being the foundation for applications ranging from low-level, back-office infrastructure to the threading framework for a User Interface program. Nearly all applications can readily be decomposed into a set of message based operations. Usually this process of application decomposition is a valuable exercise in system design. Some sample applications are:

1.10 Supported Platforms


appCORE was designed from the ground up to be a multi-platform framework. It uses only the standard 'C' library and a minimal subset of pthreads. Currently appCORE is supported on the following platforms: