ShareEmail this to someoneShare on RedditTweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

The library has a set of classes, data types, and macros that isolate platform-dependent threading model. They are located in Common::Threading namespace.

Threads

The GaThread class wraps system threads and provides control over them through Start, Pause and Abort methods. Pause and Abort methods are not available on all platforms and should be used only for debugging purposes. Join method will block calling thread and wait for the targeted thread to finish execution. GetId method returns ID of the underlying system thread and Status returns the current state of the thread.

Each thread can be in one of the of the states defined by GaThreadStatus enumeration:

  • GATS_RUNNING – indicates that the underlying system thread exists and it is in runnable state.
  • GATS_STOPPED – indicates that the thread is stopped and the underlying system thread does not exist or it is destroyed.
  • GATS_PAUSED – indicates that the thread is not runnable, but the underlying system thread exists but is in paused state. This state is not available on all platforms and it should be used only for debugging purposes.
  • GATS_NOT_RUNNING – combined state that can be used for checking current state of the underlying system thread. The thread is either stopped or paused.

Constructor of GaThread allows user to start thread immediately by setting started parameter to true. This constructor also takes parameters needed for creating and running new system thread. These parameters are represented by the GaThreadStartInfo structure. It stores pointer to a function that servers as entry point for the thread, _functionPointer field. This structure also contains an additional pointer that will be passed to the function as a parameter, _functionParameters field.

The library has additional supporting types to isolate system dependent types:

    SystemThread – system specific thread handle.
    ThreadID – system specific data type that stores thread ID.
    ThreadFunctionPointer – pointer to entry point function.
    ThreadFunctionReturn – type of value that entry point function returns to operating system after it finishes the execution.

For more details about threads see documentation of following classes:

Thread Pool

To avoid unnecessary thread creation and destruction library provides implementation of thread pool that is used for execution of asynchronously tasks. These tasks are called work items.

Base class for all work items is GaWorkItem. Each item has action that it should perform and an object that will store results of the operation. GaWorkItem is inherited by the following classes: GaMethodWorkItem and GaFunctionWorkItem. The first one represent work item that executes method on a certain object and the second one is for execution of a function. GaMethodWorkItem class expects pointer to method and object on which the method will be executed and as well as parameters that will be passed to the method. GaFunctionWorkItem only expects function pointer and the parameters that will be passed to the function.

GaWorkResults class stores and handles results of work items execution. It contains event object that is signaled when the work item is completed and the result is available or an exception was thrown during the execution of the item. This event object can be used for synchronization of threads that requires results to be available before they can continue execution. For this purpose user can call Wait method that will block calling thread until the results become available. If blocking is not required user can call IsReady method to check the state of the work item results. In case of an exception user can call GetException to get exception object or ThrowException to rethrow expectation in calling thread. HasException method returns true if an exception did occurred during the execution, otherwise it will return false. SetException method is used internally by the library to store thrown exception.

GaTypedWorkResults class inherits base result class and it contains value returned by work item operation, return value of method/operation. GetResults method returns reference to stored return value. SetResults method is used internally by the library to store return value.

Thread pool is represented by the GaThreadPool class. There can be only one instance of the pool during the lifetime of the application. This instance is created during the initialization of the library and it is available through Instance class method. ExecuteWork method enqueues work item and pool will assign it to existing thread if there is one that is available. If that is not the case, pool will create new thread. When the work item is enqueued, pool makes its copy and uses it instead of original object, so user can be free to destroy original even though the execution of the item have not yet been completed or even begun. Size of pool can be controlled with SetSize method. Thread pool will destroy all threads that are returned when the number of available threads reaches the specified size. SetExceptionHandler method sets the handler that will be called in case that work item throws exception.

Threads in the pool are internally managed by GaWorkerThread which holds information like current state of the pool threads and work item that they are executing.

More information about thread pools is available in documentation of following classes:

Synchronization

The library has several classes that abstract platform dependent synchronization objects:

  • GaCriticalSection (more) – represents abstraction of system-specific mutex-like synchronization primitive.
  • GaSpinlock (more) – implements spinlock synchronization primitive.
  • GaSemaphore (more) – represents abstraction of system-specific semaphore-like synchronization primitive.
  • GaEvent (more) – represents abstraction of system-specific event-like synchronization primitive.
  • GaBarrier (more) – implements barrier synchronization primitive.
  • GaSectionLock (more) – implements automatic locking and unlocking of synchronization primitives such as mutexes or spinlocks.
  • GaBarrierLock (more) – implements automatic barrier synchronization.

There are several macros that make use of synchronization objects easier:

  • GA_DECLARE_SPINLOCK – The macro declare spinlock object as member of the class: mutable Common::Threading::GaSpinlock LOCK_NAME
  • DEFINE_SYNC_CLASS – This macro inserts members to a class which are needed to synchronize access to an instance of the class. The LOCK_OBJECT and LOCK_THIS_OBJECT macros are used for synchronization of access to the object.
    Injected members are: mutable GaCriticalSection _synchronizator attribute and GaCriticalSection* GACALL GetSynchronizator() const method
  • LOCK(LOCK_NAME) – This macro is used for acquiring access to a critical section protected by the synchronization primitive (such as GaSectionLock or GaCriticalSection…)
  • UNLOCK(LOCK_NAME) – The macro is used when a thread exits a critical section and releases access to the synchronization primitive (such as GaSectionLock or GaCriticalSection…)
  • LOCK_OBJECT(LOCK_NAME,OBJECT) – This macro acquires access to an object with a built-in synchronizer and prevents concurrent access. It instantiates a GaSectionLock object with the name LOCK_NAME, and acquires access to the object. When execution leaves the scope in which LOCK_OBJECT is specified, the GaSectionLock object is destroyed and access to the locked object is released. Unlocking access to the object before leaving the scope can be done by calling the UNLOCK(LOCK_NAME) macro.
  • LOCK_THIS_OBJECT(LOCK_NAME) – This macro acquires access to this object and prevents concurrent access. It declares and instantiates a GaSectionLock object with the name LOCK_NAME and acquires access to this object. When execution leaves the scope in which LOCK_OBJECT is specified, the GaSectionLock object is destroyed and access to this object is released. Unlocking access to the object before leaving the scope can be done by calling the UNLOCK(LOCK_NAME) macro.
  • GA_LOCK_SECTION(LOCK_NAME, SECTION) – This macro acquires access to critical section protected with synchronization primitive specified by LOCK_NAME. It declares and instantiates a GaSectionLock object with the name SECTION and acquires access to this object. When execution leaves the scope in which LOCK_OBJECT is specified, the GaSectionLock object is destroyed and access to this object is released. Unlocking access to the object before leaving the scope can be done by calling the UNLOCK(LOCK_NAME) macro.
  • GA_BARRIER_SYNC(LOCK_NAME, BARRIER, COUNT) – The macro initiate synchronization on barrier object specified by BARRIER parameter and blocks threads that have reached it until the required number of threads, specified by COUNT, hit it. It declares and instantiates GaBarrierLock with name LOCK_NAME that handles synchronization. User can specify a block of code that will be executed only by the last thread that reached barrier before releasing other threads.
class SomeClass
{
    DEFINE_SYNC_CLASS

    // rest of the class declaration
};

The user can use macros to synchronize access to an object of the class.

void SomeMethod()
{
    SomeClass obj;

    // Declares GaSectionLock object obj_lock that
    // use critical section object embedded into obj object
    LOCK_OBJECT( obj_lock, obj );

    // ...critical section code...

    // lock is released automatically
    // by destroying obj_lock object
}
void SomeClass::SomeMethod()
{
    // Declares GaSectionLock object obj_lock that
    // use critical section object embedded into this object
    LOCK_THIS_OBJECT( obj_lock );

    // ...critical section code...

    // lock is released automatically
    // by destroying obj_lock object
}

If a critical section ends before the end of the scope, the UNLOCK macro can be used to unlock the critical section object.

void SomeClass::SomeMethod()
{
    // Declares GaSectionLock object obj_lock that
    // use critical section object embedded into this object
    LOCK_THIS_OBJECT( obj_lock );

    // ...critical section code...

    // release lock on critical section object
    UNLOCK( obj_lock );

    // ...non-critical code...

    // section can be locked again using same lock object
    LOCK( obj_lock );

    // ...critical section...
}

Locking a critical section is possible without using GaSectionLock objects. The LOCK and UNLOCK macros can be used directly on critical section objects.

// ...

GaCriticalSection _criticalSection;

// ...

void SomeMethod()
{
    // lock critical section object
    LOCK( _criticalSection );

    // ...critical section code...

    // release lock
    UNLOCK( _criticalSection );
}

When barrier is used for synchronization user can specify additional block of code that will be executed by the last thread that has reached the barrier.

// ...

GA_BARRIER_SYNC( lock, barrier, threadCount )
{
    // execute by the last thread to reach barrier
}

// or

GA_BARRIER_SYNC( lock, barrier, threadCount );

// ...

Atomic Operations

GaAtomicOps class abstract platform dependent implementation of atomic operation for operands of different sizes. There are two additional helper classes that handle atomic operations that have to be performed on data types that are twice the size of CPU word: GaCmpXchg2 and GaQWordExtract.

GaAtomic class wraps variable of specified type and overload operators to use atomic operations instead of thread-unsafe instruction. There is specialization for pointers that handles pointer arithmetic correctly.

More information about atomic operations is available in documentation of following classes: