2 C++ Interface Concepts


2.1 Design Goals

Since C++ is an object-oriented language, the primary goal of the pSOSystem C++ classes is to provide an object-oriented interface to pSOS+ kernel functions. This object-oriented interface is currently restricted to the pSOS+ kernel. The architecture of the pSOSystem C++ environment is shown in Figure 1.

The primary objectives of the pSOS+ C++ classes are:

Additionally, the pSOSystem C++ environment supports C++ memory management and other C++ run-time issues.

Figure 1. pSOSystem C++ Environment

2.2 Design Overview

C++ classes have been supplied for the following pSOS+ system entities:

Semaphores PSemaphore Class

Queues PQueue Class

Regions PRegion Class

Partitions PPartition Class

Tasks PTaskBase, PSelf, and PTask Classes

Since the pSOS+ kernel already has an object-oriented implementation, the pSOS+ C++ classes are best viewed as a binding mechanism to map C++ objects (instances of a class definition) onto pSOS+ system objects (tasks, semaphores, queues, and so on). The following terms are defined here for use throughout the remainder of this manual:

system entity   To avoid confusion, this expression is used in place of system object, which 
                refers to tasks, semaphores, queues, and so on, in the three main pSOSystem 
                manuals.
object          This is an instance of either a user-defined class or a pSOS+ C++ class; for 
                example, an object of type PSemaphore or PTask.  For clarity, the term pSOS+ 
                object is sometimes used to specify an instance of a pSOS+ C++ class.
method          An instance of either a user-defined member function or a pSOS+ C++ class 
                member function; for example, the function PQueue::Delete.

pSOS+ system calls that are not associated with a pSOS+ system entity and have been left with a procedural interface include the following:

de_init, de_open, de_close, de_read, de_write, de_cntrl, tm_set, tm_get, tm_tick, k_fatal, m_ext2int, m_int2ext, as_return, and i_return

Appendix A contains a full cross reference listing of pSOS+ C function names with C++ class method names.

2.2.1 Class Hierarchy

With the exception of the pSOS+ Task classes, the pSOS+ C++ classes have a simple one-level hierarchy. Each class implements a complete C++ encapsulation of the operations available for a particular class of system entity. These classes optionally support a standard error handling mechanism for pSOS+ system errors.

The pSOS+ Task classes are necessarily somewhat more complex. In pSOS+, some task-related functions can be performed only on the calling task (for example, ev_receive, tm_wkafter), and other functions can be performed only on another task, (for example, t_create, t_start). Also, functions exist that are meaningful when performed on either the calling task or another task in the system (for example, t_suspend, t_setreg). The pSOS+ Task classes hierarchy is shown in Figure 2.

Functions that can be performed on both the calling task and other tasks are contained in the PTaskBase Class. Users are not intended to define instances of this class; instead, this class serves as a base from which two other classes are defined. The PSelf Class contains those functions that can be performed only on the calling task. The PTask Class contains those functions that can be performed only on another task.

Figure 2. pSOS+ Task Classes Hierarchy

2.2.2 C++ Object-to-System Entity Mapping

Conceptually, the simplest way to relate pSOS+ objects to pSOS+ system entities is to have a single object correspond to each system entity required. While the pSOS+ classes allow this approach, they were designed to support a many-to-one object-entity mapping, where multiple objects can map to a single-system entity. The implementation details of this design appear in Section 2.3, ``Class Implementation.''

The many-to-one approach is preferable for several reasons. Because multiple tasks usually use queues and semaphores, a one-to-one mapping forces the object to be globally visible. Since restriction of access is one of the central principles of object-oriented programming, visibility of system objects should be minimized. Many-to-one mapping allows local pSOS+ objects to be defined only in tasks that require access to a particular system entity. Many-to-one mapping is seen in Figure 3.

Figure 3. Many-to-One Object-Entity Mapping

Many-to-one mapping also provides flexibility in error handling, allowing users of a system entity to react differently to an error, depending upon their execution scope. For example, two tasks -- one low-priority, one high priority -- may be posting messages on a queue. If the low-priority task invokes the Send method and receives a QFULL error, it may be able to just sleep and retry the Send some time later. In contrast, the same QFULL error to a high-priority task may be a fatal system error requiring a totally different response. The many-to-one mapping allows each task to respond to errors in their own way, if necessary.

2.2.3 Error Handling

One of the stated design goals of the pSOSystem C++ class library is to provide a standard mechanism for handling pSOS+ returned error codes. In an installed embedded system, most of these return codes should never occur, but they might well occur during development, and if caught, can save hours of debugging. All pSOS+ service calls return an informative error code to the caller; this code can be used by the application to react to a specific situation.

The goal of the pSOS+ error handling support is to allow user code to be both clear and reliable. The user may install handlers for specific errors that are expected to occur and allow all other obscure or fatal errors to be handled by a default error handler.

The paradigm used is similar to the hardware exception flow control used in the Motorola 68000 microprocessor family. A table of pointers to functions is created (analogous to the vector page in a 68000 processor). Specific error-handler functions are installed into this table, indexed by the pSOS+ error code; in a similar way, interrupt service routines are installed into the vector page, indexed by a vector number. This error table object is bound to a pSOS+ object. If an error is returned by a function within this pSOS+ object, the error is thrown through the error_table object to the handler previously installed. This is similar to the execution of the 68000's exception processing.

A conceptual model of the error handling object is shown in Figure 4.

Figure 4. Conceptual Model of Error Handling

After performing the user-defined processing, the error handler function returns control back to the calling pSOS+ object method and indicates if the pSOS+ service call should be retried. This is similar to retrying a bus access on a previous BUS_ERROR-type exception.

The paradigm differs from 68000 exception handling in that there can be multiple error table objects in a system, and each pSOS+ object uses the error table to which it has been bound. This allows different objects to handle the same pSOS+ error code in a different way. As mentioned above, this is required because a particular error may need to be handled differently on a per task or per object basis.

2.3 Class Implementation

The following section briefly explains common implementation issues concerning the pSOSystem C++ class library. For a detailed description of each class, refer to Chapter 3, ``C++ Interface References.''

2.3.1 C++ Object to System Entity Mapping Implementation

A C++ object is linked with a pSOS+ system entity by means of the unique numeric ID assigned to the pSOS+ system entity when it is created. This ID is used as a parameter in system calls to indicate on which system entity an operation should be performed. Each C++ Class stores the pSOS+ ID as a data member, and the ID is used to link the C++ object to the actual system entity.

The pSOS+ ID for a particular system entity can be retrieved in two ways. These are Create and Ident operations. (For example, pSOS+ semaphore entity operations are called sm_create(), and sm_ident().) The Create operation takes a user-defined four-character name for the system entity, creates the system entity, and returns the numeric ID. The Ident operation takes the same user-defined four-character name and returns the numeric ID of the system entity if it exists, thus providing a runtime binding mechanism.

2.3.2 Naming Conventions

To reduce the need to refer to the manual for exact spellings of class methods or variable names, all names in the C++ classes are mixed case (use both upper and lowercase letters), contain no underscores, and are meant to be in line with current industry naming conventions. This results in names like PrintNumber instead of print_number. All pSOSystem C++ class names begin with an uppercase P followed by the name of the pSOS+ system entity, which also begins with an uppercase letter (for example, the C++ interface for pSOS+ queue entities is the PQueue Class).

The names of the C++ class methods are different from C procedural interface equivalents for several reasons:

In summary, a system call that has the following form in the C interface:

rn_retseg(myrnid, my_ptr)


has the following form in the C++ interface:

MyRegion.ReturnSegment(MyPtr)


2.3.3 Default Parameters

Parameter defaulting is used extensively in the C++ classes. This allows users to use pSOS+ services without providing unused parameters. For example, a typical use of a semaphore is to wait forever. The P() method of PSemaphore therefore has WAIT and FOREVER as default values for the Wait and Ticks parameters, respectively. Default values that are likely to be customized for a user environment are grouped together in the file $PSS_ROOT/include/cxx/pdefs.h.

A side effect of C++ parameter defaulting rules (and the PsosId data member described below) is that the parameter order differs from the C procedural interface.

A system call that took the following form under the C interface:

rn_getseg(myrnid, seg_size, NOWAIT, 0, &my_ptr)


becomes the following under the C++ interface:

MyRegion.GetSegment(SegSize, &MyPtr, NOWAIT, 0);
MyRegion.GetSegment(SegSize, &MyPtr);     //Using defaults.


2.3.4 Common Class Elements

Each pSOSystem C++ class has the following members:

Each class has a protected data member, PsosId. This member stores the identifier for the system entity to which the object refers. This data member is protected to provide information hiding and to allow any derived classes direct access to PsosId without incurring the overhead of invoking member functions. If the object is not bound to any system entity, PsosId is set to ILLEGAL_ID.

The protected data member Error is a pointer to the error handler object bound to this object. This error handler determines the response to any error codes returned by pSOS+. If the value of Error is NULL (the default), any error codes returned are simply returned to the caller. If Error is assigned a value, then any nonzero return codes are thrown to this error handler object. CONSTRUCTORS AND DESTRUCTORS

Except for the PSelf Class, each class has two constructors. These are a default constructor and an ident constructor. The PSelf Class has only a default constructor.

Except for the PSelf Class, the default constructor sets PsosId to ILLEGAL_ID. This means that the object is not bound to any pSOS+ system entity, and any general use methods return ERR_OBJID until the object is correctly bound to a system entity. The object is bound to a pSOS+ system entity by the Create or Ident methods.

Since the PSelf task encapsulates task functions performed on the calling task, the default constructor for the PSelf Class sets PsosId equal to zero. In pSOS+, any task service call with TASK_ID equal to 0 operates on the calling task.

The ident constructor creates an object and attempts to bind this object to a named pSOS+ system entity. This is accomplished by repeatedly calling the pSOS+ xx_ident system call (where xx stands for t, sm, pt, rn, or q) until successful or until the maximum number of retries are exhausted. If successful, the ident constructor then sets PsosId to the ID value returned by the pSOS+ xx_ident system call. If the ident constructor fails, it sets PsosId equal to ILLEGAL_ID and behaves as the default constructor.

Since the pSOSystem C++ classes do not allocate any memory or other resources, the default destructor provided by the compiler is sufficient. OBJECT MANAGEMENT METHODS

Each C++ class provides an AssignPErrorObject method that is used to set the Error data member. This method takes a pointer to an error handler object of type PError. As previously mentioned, if Error == NULL, then no error handling is enabled for this object.

Each class provides methods that allow the creation and deletion of the corresponding pSOS+ system entity, or else they allow an object to be bound to an already existent pSOS+ system entity. These methods provide the same functionality as the corresponding pSOS+ system calls associated with a particular system entity.

For example, the C interface provides the sm_create, sm_delete, and sm_ident system calls. In C++, the PSemaphore Class provides the Create, Delete, and Ident methods. Compared to the original system calls, the parameter lists have been re-ordered to support C++ parameter defaulting, and because of the PsosId protected data member, there is no longer any need to pass the pSOS+ ID.

Additional functionality has been added to the Ident member function of the classes. The Ident method now has two new parameters, a Wait flag and a Retry count. The xx_ident system calls are used to return an ID for a system entity, given a four-character name. These system calls do not block if the entity does not exist. It is typical to enclose the xx_ident system call in a loop to keep trying until the entity comes into existence. This loop functionality has been incorporated in the Ident member function. This is the same method used in the ident constructor described above.

Because there is no Create constructor and Delete destructor, the user must invoke the corresponding Create method to explicitly create the pSOS+ system entity. Likewise, the Delete method must be invoked to explicitly delete the system entity. pSOS+ system entities such as semaphores and queues are almost always used by multiple tasks. Because constructors and destructors are automatically called by the compiler upon entering and exiting the scope of a function, it would be inappropriate for the constructor and destructor for the pSOSystem C++ class to automatically create and delete the corresponding system object. For example, a destructor that automatically deletes the actual pSOS+ semaphore might delete the semaphore while it is still in use by some other task. GENERAL USE METHODS

Each pSOSystem C++ class has a set of methods that are considered general use methods, meaning that these methods are the actual operations that can be performed on a specific class of pSOS+ system entity. These methods provide the same functionality as the corresponding pSOS+ system calls associated with a particular system entity.

For example, the C interface provides the following general use functions for queues: q_send(), q_broadcast(), q_urgent(), and q_receive().

In C++, the PQueue Class provides the following general use methods: Send(), Broadcast(), Urgent(), and Receive().

Compared to the original system calls, the parameter lists have been re-ordered to support C++ parameter defaulting and make use of the PsosId data member.

2.3.5 Error Handling

The Error data member of each pSOSystem C++ class is used to bind an error handling object of type PError to a pSOS+ object.

A PError object behaves like an array of pointers to handler functions. A pSOS+ object throws a pSOS+ error to the PError object, which calls the appropriate error handler function with the error code passed in as a parameter. If no handler has been installed for the error thrown, a default error handler function is called. The standard default error handler will typically generate a fatal error to pSOS+, which stops in the debugger. The default error handler can be replaced by the application with a more appropriate behavior for an installed system.

Note that for PError, a few hundred possible pSOS+ return codes exist, and these codes have a range of values from 1 to approximately 4000. Thus, a PError implementation that uses an array with pointers for each error handler wastes memory -- especially because, for any pSOS+ object, there may be only a few errors for which a user is likely to install a handler. The PError Class implementation model is shown in Figure 5.

Figure 5. PError Object Implementation Model

This implementation allows the user to specify the number of entries in a PError object search table, allowing a trade-off of memory usage versus speed of handler function call. To achieve this, a PError object contains an array whose elements point to a linked list of pointers to error handlers. The size of the search table is a parameter supplied to the PError default constructor.

When an error code is thrown, it is hashed (by doing a MOD TableSize), and the appropriate linked list is traversed to retrieve the address of the handler function to be called. For example, if the hash table is only one element long, all error handlers are on one linked list, and the time to throw a specific error to a handler function depends on the function's placement in the linked list. If the hash table is 20 elements long, then the handlers are spread across multiple linked lists, and this depends on the value of the ErrorCode MOD 20. Thus, increasing the table size shortens the linked lists to be traversed and speeds the time to throw an error to a handler.

The PError Class has the following elements:

  • Private data members:
  • Constructors and Destructors:
  • Assignment Operator:                      The `=' operator provides the same functionality as the copy constructor.
  • InstallHandler Method:                 This method is called to install a user-provided error handler function for a specific                                                        error code.
  • ThrowPError Method:                   This method is called from the pSOSystem C++ class libraries on any non-zero return                                                        code from a pSOS+ system call.
  • In addition to these data members and member functions, PError also provides the following predefined error handler functions:

  • PError::Retry() Static Method:
                                                         This is a predefined error handler function that can be installed as a handler using the
                                                         InstallHandler method. It does nothing but return TRUE to the calling pSOS+
                                                        object, to retry the pSOS+ system call that caused an error.
  • PError::NoRetry() Static Method:
                                                       This function provides the same service as the Retry() method, except that it returns
                                                       FALSE to the calling pSOS+ object, to tell it to NOT retry the pSOS+ system call
                                                  and to return the error code to the calling application.
  • PError::DefaultPErrorHandler Static Method:
                                                  This is the function that is called for any unprocessed pSOS+ errors thrown. The default                                               implementation calls k_fatal().
  • Error Handler Function Implementation

    This section briefly overviews how the pSOSystem C++ class methods expect a user-provided error handler to function.

    Each method in a pSOS+ object first checks to see if a PError object has been associated with the pSOS+ object. If there is no PError object, the pSOS+ system call is made and the return value is passed straight back to the application.

    If a PError object has been set up (using the AssignPErrorObject method), the pSOS+ call is made and the return code is checked to see if the call was successful (ErrorCode == 0). If the system call was successful, the method simply returns the return value (in this case zero) to the application.

    If the return code is not 0, the method calls the Error->ThrowPError method to pass the ErrorCode to the error handler. This acts as an indirect call to the installed error handler function. If the error handler returns a TRUE, the method remains in the loop and retries the system call until it succeeds or until the error handler returns a FALSE. If the error handler returns a FALSE, the loop is exited, and the ErrorCode is returned to the application.

    A user-provided function must have the following form:

    See Chapter 3, ``C++ Interface References'' for more information regarding the PError Class.

    Overriding the Default Error Handler

    Most systems will likely replace the Default Error Handler provided by pSOSystem. A global variable is used to point to the default error handler for all PError objects in the system and is defined as part of the pSOSystem C++ environment.

    To install the MyErrorHandler() function as the new default error handler for a system, the following code fragment is needed:

    Note that this is analogous to the method defined in the C++ language that is used to install a new_handler for the global new allocation failures.

    2.4 C++ Runtime Support

    The pSOSystem C++ support encompasses support for C++-specific runtime services. These services include the C++ heap management operators new and delete and compiler-specific support for initialization.

    2.4.1 C++ Heap Management

    This section explains the problems presented to traditional heap management methods by C++ applications and covers the new mechanism provided by pSOSystem to alleviate these problems.

    Heap Usage in C++

    The object-oriented nature of C++ applications results in much more intensive memory usage than in C applications. In particular, a much larger proportion of total memory usage is taken in allocations on the heap, compared to a procedural language like C, where the stack and static data are used more.

    Most memory allocation for an object takes place during object creation, when the object's constructor is executed. In Object Oriented Programming (OOP), it is a typical practice for an object's constructor to have a list of calls to the new operator to allocate memory for objects it uses internally. The destructor has a corresponding list of delete calls to delete these internally used objects. These internal objects can be of any size, but experience has shown that objects requiring only four or eight bytes are frequently allocated via the new operator.

    Because these constructors and destructors are called whenever an object definition enters and exits scope, this style of programming can have a major impact on performance in an embedded system. The most obvious impact comes from the execution time of the new and delete operators, but another effect is the heap fragmentation caused by frequent use of the new and delete operators. This problem is exacerbated by the frequent allocation and deallocation of small memory blocks.

    To better support C++ heap usage, pSOSystem includes an implementation of new and delete that can reduce fragmentation and give better performance for small allocations.

    pSOSystem New and Delete Design Overview

    Since compiler runtimes simply map the new and delete operators directly to the malloc() and free() functions of the standard C library, pSOSystem addresses these heap fragmentation and performance problems by new versions of malloc() and free() optimized for C++. These replace the versions of malloc() and free() supplied with the pREPC+ Standard C Library.

    pSOS+ offers two styles of memory management. pSOS+ regions implement a traditional malloc-style heap, and specifically Region 0 is the system wide heap used by the pREPC+ malloc() and free() functions. pSOS+ also supports a simple style of memory management of fixed-size memory buffers called partitions.

    The pSOS+ region manager supports a minimum allocation size parameter (unit_size) when creating the heap to allow trade-offs between internal and external fragmentation. Because Region 0 is typically large, and because there is a header overhead associated with each unit allocated, this unit_size is typically fairly large (unit_size is usually greater than or equal to 32 bytes). For allocations that are smaller than unit_size, an entire unit is still allocated.

    For simple memory management, pSOS+ partitions have several qualities that make them attractive. Partition allocation is very fast and completely deterministic. Furthermore, because all partition buffers have the same size, no header is associated with each buffer allocated, and this reduces the system memory overhead per block.

    The revised versions of malloc() and free() use a combination of two styles of memory management. The partition memory manager is used for small allocations. Since this is very fast it is called the ``quick heap.'' The pSOS+ region manager, optimized to handle variable-sized blocks of memory, is still used for larger allocations.

    The following is an example of malloc() and free() psuedo code:

                     rn_getseg(0, size, RN_NOWAIT, 0, &ptr);
            { /* Allocate from QuickHeap. */
                     if (pt_getbuf(QuickPID, &ptr))  /* QuickHeap empty */
                            { /*  Allocate from pSOS+ region manager */
                                     rn_getseg(0, size, RN_NOWAIT, 0, &ptr);
                            } 
            }

                    pt_retbuf(QuickPID, ptr);



    As this listing shows, malloc() now allocates from one of the two heaps, and this depends on the size of the allocation. free() must check the address range of the quick heap to know if the pointer should go back to the partition or to the pSOS+ region.

    This implementation improves performance in two ways. First, since partition services are inherently much faster than those designed to accommodate variable-sized memory, allocation of small blocks is very fast. Second, heap fragmentation is reduced by ensuring that the smallest allocations all take place on the quick heap. NOTE: If the pt_getbuf() system call fails, presumably because there are no buffers in the quick heap, then the allocation request is satisfied by calling the rn_getseg() allocator.

    Since the block size and number of blocks on this quick heap will vary with the application, it is important that the user tune these parameters for optimal performance.

    To help tune these parameters, a GetHeapStats() function returns statistics on system-wide memory allocation and deallocation. These statistics include the number and average size of allocations made from both the quick heap and the regular heap. An additional statistic is maintained on the maximum number of buffers simultaneously allocated from the quick heap.

    The statistics-gathering code can be turned off and on during runtime, by the functions TurnHeapStatsOn(), and TurnHeapStatsOff(). For more detailed information on these functions, see Chapter 3, ``C++ Interface References.''

    2.4.2 Initialization Issues

    The C++ heap management services must be initialized prior to any calls to new and delete. Additionally, static object constructors must be called prior to being used.

    An object's constructors are called when an object is created. The point at which an object is created is obvious for dynamic objects. For static objects, the point of creation is not so obvious. The compiler generates initialization code for the static constructors. This initialization code should be called in the ROOT task before any reference is made to a static object.

    Since the static constructors will almost certainly be allocating memory from the heap, the heap must be initialized before the static constructors. To simplify the initialization process, pSOSystem includes an initialization routine called InitCxxRuntime(). This routine first initializes the C++ heap services and then calls the static object constructors. This routine must be called from the ROOT task before any references to static objects or any calls to new and delete. Any local or automatic objects with constructors within the ROOT task must be declared after this routine is invoked.

    InitCxxRuntime() takes two parameters that are passed to the InitMemAlloc() routine, which creates the quick heap. The parameters are QuickHeapBuffSize and NumQuickHeapBuffs. The default values for each of these parameters are defined in pdefs.h and will need to be tuned for each application at some stage in its development.

    The following are potential initialization pitfalls with static objects:

      1. Because InitCxxRuntime() is called from the ROOT task and InitCxxRuntime() calls the static constructors, the ROOT task owns any resource where ownership is assigned. Specifically, if the static constructors open files or sockets, then the ROOT task owns the corresponding file and socket descriptors.

      2. If a static object constructor needs other pSOSystem resources, (for example, DEV_TIMER for System Clock tick, DEV_SERIAL for serial output) these resources should be initialized before calling InitCxxRuntime() from ROOT.

    Exercise caution when defining static pSOS+ objects using the ident constructor. The following code fragment illustrates the problem:


    By default, the ident constructor is implemented to loop forever (while it attempts an ident on the system entity). In the preceding code fragment, the ident constructor for the AcqPipe queue object would be called from the InitCxxRuntime() routine. It would loop infinitely, because the pSOS+ queue would not have been created.

    There are a number of ways to avoid this situation. You can pass a parameter to the constructor to ensure it timed out, but this requires a check to determine if the constructor failed. One approach is to create all the system entities in ROOT before the static constructors are called. This only works if ROOT references no static objects in doing so.

    The safest approach is to use the default constructor for all static pSOS+ objects, and then invoke the Ident method to bind to the system entity.

    2.4.3 Task Deletion

    A C++ compiler automatically calls the constructors for local objects when they enter scope or if they are allocated through the new operator. Likewise, the destructor is automatically called when the object leaves scope or is deleted through the delete operator. This works well if, once a scope is entered, it is later exited. Stated differently, for every opening `{` a closing `}' must be executed to guarantee object destruction.

    In pSOS+ however, it is possible for a task to be started then later restarted or deleted without exiting scope(s). As a result, the constructors of the objects defined in that task would be called, allocating memory, but the destructors would never be called. In this case the memory originally allocated for the objects defined in the task would never be freed, and eventually the system would run out of memory.

    Synchronous and Asynchronous Task Deletion are the operations that can cause this memory loss, and they are described in the next two sections.

    Synchronous Task Deletion

    The pSOSystem System Concepts and pSOSystem Programmer's Reference manuals state that the correct method for a task to delete itself is for it to call t_delete(0) as the last statement to be executed.

    Here is a C task implementation:

    If successful, the t_delete(0) system call shown above deletes the caller and therefore never returns to execute the closing braces. This method is unsafe in C++. The PTask Class resolves this problem for C++ applications by calling the user task function from a wrapper function. Although this is done transparently to the user, when the t_start method of an object of PTask is invoked, the user needs to be aware of how this wrapper routine works to use it correctly.

    The C++ wrapper routine is structured roughly as follows:

            {
            // call user_task() function
            // close any open device, file, or 
            //networking resources 
               t_delete () ;
            }

    Since the t_delete() is performed in the wrapper routine, the code for the task itself should not contain a call to PTaskBase::Delete(). Furthermore, because the closing brace of the task() function must be executed for the destructors of objects defined in the task to be invoked, the user must structure the task's code to ensure that this happens. Any error condition that would prompt a call to t_delete() in a C application should instead cause the task() function to exit by its closing brace.

    Note that the exit() and abort() routines supplied as part of the C++ library support call t_delete() and should therefore be considered unsafe in regard to local object destruction.

    Asynchronous Task Deletion

    The case where a task is restarted or deleted by another task is much more complex because it is not possible for the affected task to make an orderly exit from scope. In these circumstances it is not possible for the runtime environment to deallocate objects allocated during the life of a task. Asynchronous task deletion and restarting should not be used if at all possible. If this functionality is required by the application, then a mechanism must be devised to store pointers to objects allocated, so that their destructors can be called explicitly before task deletion or restart.

    A preferred alternative to an asynchronous task deletion is to use some form of a synchronous message sent to the task to be deleted to allow it to free any resources owned and do an orderly self deletion.





    psos_support@isi.com

    Copyright © 1996, Integrated Systems, Inc. All rights reserved.