MTClib - MultiThreaded C

What is MTClib?

The MTC library is a multithreading library for C and C++ programs that is open source, cross platform and high performance.

Features

What's in the package?

The package consists of the library sources, pre-built libraries, C/C++ header files, example C and C++ applications and projects or makefiles for the following target/compiler combinations:

Requirements

Limitations of use

  1. The MTC library is open source, shareware.

  2. The library may be freely used by, or incorporated into products of, any individual for personal and private use or for their sole business without further restriction or charge.

  3. Companies, organisations or governments must first obtain a license to use the library.

  4. Any person, organisation or company intending to sell or redistribute for profit, products containing the library or any part thereof must first obtain a licence.

  5. In no case may the library or any part thereof be re-badged, re-sold or used in a system to deprive anyone of life, liberty or livelihood. 

Copyright ©2006 Software Systems Consultants, www.softsystem.co.uk
e-mail: info(at)softsystem.co.uk

Warranty

MTClib is supplied 'as is' with absolutely no warranty given or implied. The use of MTClib is entirely at the risk of the end user. Software Systems will not be held responsible for any loss or damage, however caused, resulting from the use of MTClib.


Installation

Use WinZip or similar to extract the files into an empty directory. The installation process creates 4 sub-directories:

  1. APPS containing the example programs.

  2. INCLUDE containing C header files defining the public interface to each module.

  3. LIB containing a sub-directory for each target/compiler supported, which in turn contains a pre-built library and scripts and makefiles to rebuild the library.

  4. SOURCE containing the platform independent C source files plus sub-directories containing platform dependent sources.

Example programs

There are 4 example programs provided in the 'APPS' directory:

Building the examples

The examples should be built from the sub-directory for the chosen compiler.


Using MTClib

All MTC library clients must include the header file "mtc.h". This file in turn includes "mtc_host.h" which sets up platform and compiler specific types and definitions.

Error return codes

Most MTC functions return an integer error code from the set 'enum MTC_Error', defined in "mtc_err.h". A zero always indicates 'no error'.

C++ programs

The MTC headers can be included in a C++ program and then offer additional classes and functions. To support the widest range of compilers the following preprocessor symbols can be user defined:

MTC_ANSI_CPP

Normally set to 1 in mtc_host.h but if previously defined to 0 will disable templates and exceptions.

MTC_TEMPLATES

Normally set to 1 in mtc_host.h but if previously defined to 0 will disable templates.

MTC_NOEXCEPTIONS

Normally set to 0 in mtc_host.h but if previously defined to 1 will disable exceptions.

MTC_NAMESPACE

All MTC++ definitions are made within this namespace. Programs should use either of these methods to access MTC++ objects:

class exception

All MTC++ exceptions derive from this class.

throw_on_error

Inline function to throw an MTC++ exception if a function returns an error. Usage:

throw_on_error( MTC_Startup());

throw_if_null

Inline function to throw an MTC++ exception on NULL. Usage:

throw_on_null( MTC_MutexOpen());

Library startup and shutdown

A library client must make a successful call to MTC_Startup before using any other MTC function. The main thread may optionally call MTC_Cleanup to release resources. If not explicitly called beforehand, MTC_Cleanup will be executed during normal program exit handling.

MTC_EXPORT( int, MTC_Startup( void));

Initialises the MTC library. Returns an MTC error code

MTC_EXPORT( void, MTC_Cleanup( void));

Terminates all threads and releases any resources previously allocated by the library.

MTC_EXPORT( void, MTC_AtCleanup(
MTC_FNPTR( MTC_Fcleanup) callback
));

Installs a callback function that is executed during MTC_Cleanup.

StInstance class

A stack based C++ class that calls MTC_Startup in its constructor and MTC_Cleanup in its destructor.

MTC global variables

The MTC library exports the following global variables that can be set before calling MTC_Startup.

MTC_ui32 MTC_uSchedule

The number of milliseconds that a thread may run for before being pre-empted. If this value is 0 then pre-emption is disabled.

MTC environment variables

The following environment variables can be set before calling MTC_Startup:

MTC_SCHEDULE

The number of milliseconds that a thread may run for before being pre-empted. If this value is 0 then pre-emption is disabled.

MTC_STACK

The default stack size.

MTC_YIELD

If non-zero the DOS and DJGPP versions make appropriate idle calls to the OS during idle periods.

MTC_TIMERS

The number of timers.

MTC_DPCS

The number of deferred procedure calls.

MTC_DEBUG

The log trace level for debug builds of MTC.

MTC_LOGFILE

The log file name for debug builds of MTC.

MTC preprocessor macros

MTC_PTR(p)

Used to create a pointer to an object.

MTC_FNPTR(f)

Used to create a pointer to a function.

MTC_CB(f)

Used to create a pointer to a library callback function.

MTC_DLL

Define this if MTC is being used from a DLL.

MTC Types

MTC defines platform independent fixed length types such as MTC_i16 (16-bit int), MTC_ui32 (32 bit unsigned) etc.

C/C++ standard library considerations.

If MTClib is configured to operate pre-emptively (see MTC_uSchedule) then the library client must ensure that C standard library functions are not re-entered. This can be achieved by allocating a named mutex that is claimed and released around these functions or by wrapping them with calls to MTC_ThreadEnterCritical and MTC_ThreadLeaveCritical. Functions that are documented by the compiler vendor as re-entrant do not need this protection; this typically includes memcpy, strcpy and similar.


Threads

Dependent upon the MTC library compilation options used, threads may be implemented as a shim over a platform native thread type or may be implemented independently. In either case the controlling API is identical.

Threads are dispatched on a priority basis when they become ready to run. Threads of equal priority share available CPU resources either by explicitly yielding the CPU or by being pre-empted. The variable MTC_uSchedule contains the number of milliseconds that a thread may run for before being pre-empted. If MTC_uSchedule is zero then pre-emptive dispatching is disabled.

MTC_EXPORT( MTC_ThreadHandle, MTC_ThreadCreate(
MTC_FNPTR( MTC_ThreadFunction) thread_main,
MTC_PTR( void) pvContext,
MTC_i32 suspense MTC_OPTIONAL,
MTC_size_t stack MTC_OPTIONAL
));

Create a thread that will execute the function 'thread_main', which is passed the context pointer pvContext. The thread is initially in the suspended state if 'suspense' is non-zero. The returned handle initially has 2 references. When the thread exits and the handle closed the thread will be destroyed.

MTC_EXPORT( int, MTC_ThreadClose(
MTC_ThreadHandle
));

Closes the thread handle.

MTC_EXPORT( int, MTC_ThreadDestroy(
MTC_ThreadHandle
));

Forcibly stops and destroys the given thread.

MTC_EXPORT( void, MTC_ThreadExit(
MTC_i32 status MTC_OPTIONAL
));

Terminates the current thread. NB the thread that called MTC_Startup will call exit( status);

MTC_EXPORT( int, MTC_ThreadWait(
MTC_ThreadHandle,
MTC_ui32 millisecs,
MTC_PTR( MTC_i32) pStatus MTC_OPTIONAL
));

Wait with timeout for a thread to exit. If pStatus is not NULL then the thread's exit status will be returned. The function may be called repetitively until the thread is destroyed.

MTC_EXPORT( int, MTC_ThreadSetPriority(
MTC_ThreadHandle
MTC_ui32 priority
));

Set a thread's priority. Valid levels range from MTC_PriorityMin to MTC_PriorityMax.

MTC_EXPORT( MTC_ui32, MTC_ThreadGetPriority(
MTC_ThreadHandle MTC_OPTIONAL
));

Get a thread's priority.

MTC_EXPORT( MTC_ThreadHandle, MTC_ThreadGetCurrent( void));

Get the current thread's handle.

MTC_EXPORT( void, MTC_ThreadSleep(
MTC_ui32 milliseconds MTC_OPTIONAL
));

Put the current thread to sleep for an interval. If the interval is 0 then this yields the current timeslice.

MTC_EXPORT( int, MTC_ThreadStop(
MTC_ThreadHandle MTC_OPTIONAL
));

Suspend the given thread. Each thread maintains a suspense count. When the count is 0 the thread can run. MTC_ThreadStop increments the suspense count.

MTC_EXPORT( int, MTC_ThreadStart(
MTC_ThreadHandle MTC_OPTIONAL
));

Decrements the thread's suspense count. When this reaches 0 the thread can run.

MTC_EXPORT( int, MTC_ ThreadGetSuspends(
MTC_ThreadHandle MTC_OPTIONAL
));

Gets the threads suspense count.

MTC_EXPORT( int, MTC_ThreadEnterCritical( void));

Returns the incremented thread critical count. When the count is >0 then thread switching is disabled.

MTC_EXPORT( int, MTC_ThreadLeaveCritical( void));

Returns the decremented thread critical count. When the count reaches 0 thread switching is enabled.

MTC_EXPORT( int, MTC_ThreadIsCritical( void));

Returns the thread critical count. When the count >0 thread switching is disabled.

MTC_EXPORT( int, MTC_ThreadQueueDpc(
MTC_FNPTR( MTC_DpcFunction),
MTC_PTR( void) pvContext MTC_OPTIONAL
));

Schedules a callback function to be called when the critical level is decremented to 0. This routine may be called from any context, including interrupt.

StCritical class

A stack based C++ class that calls MTC_ThreadEnterCritical in its constructor and MTC_ThreadLeaveCritical in its destructor.

Thread class

A C++ template class to manage threads. Usage:

class Worker {
public:
Worker() {}
// Thread main
MTC_i32 operator () () { return 0; }
};

MTC_NAMESPACE::StInstance inst;
MTC_NAMESPACE::Thread< Worker> thrd;
thrd.Join();


Mutexes

A mutex is used to ensure mutual exclusion between threads that share a resource.

MTC_EXPORT( MTC_MutexHandle, MTC_MutexOpen(
MTC_PTR( const char) name MTC_OPTIONAL
));

Create or open a mutex. If name is NULL a mutex is created. If name is not NULL then the first call will create the mutex while subsequent calls increment its reference count. Named mutexes simplify sharing global resources.

MTC_EXPORT( MTC_MutexHandle, MTC_MutexReopen(
MTC_MutexHandle
));

Add a reference to an existing mutex.

MTC_EXPORT( int, MTC_MutexClose(
MTC_MutexHandle
));

Decrements a mutex's reference count. When the count falls to 0 the mutex is destroyed.

MTC_EXPORT( int, MTC_MutexClaim(
MTC_MutexHandle
MTC_ui32 millisecs MTC_OPTIONAL
));

Attempts to claim the mutex. Returns 0 if succeeded.

MTC_EXPORT( int, MTC_MutexRelease(
MTC_MutexHandle
));

Releases the mutex.

Mutex class

A C++ class to manage mutexes, Usage:

MTC_NAMESPACE::Mutex mutex("fred");
mutex.Claim();
mutex.Release();

This class can also be used to create a synchronised variable


Semaphores

A semaphore is used to manage access to a limited number of instances of a shared resource.

MTC_EXPORT( MTC_SemaphoreHandle, MTC_SemaphoreOpen(
MTC_PTR( const char) name MTC_OPTIONAL
MTC_ui32 cnt MTC_OPTIONAL,
MTC_ui32 max MTC_OPTIONAL
));

Create or open a semaphore. If name is NULL a semaphore is created. If name is not NULL then the first call will create the semaphore while subsequent calls increment its reference count. Named semaphores simplify sharing global resources.

The semaphore is created with and initial count of 'cnt'. If max is > 0 then this will set the maximum signal count.

MTC_EXPORT( MTC_SemaphoreHandle, MTC_SemaphoreReopen(
MTX_SemaphoreHandle
));

Add a reference to an existing semaphore.

MTC_EXPORT( int, MTC_SemaphoreClose(
MTC_SemaphoreHandle
));

Decrements a semaphore 's reference count. When the count falls to 0 the semaphore is destroyed.

MTC_EXPORT( int, MTC_SemaphoreWait(
MTC_SemaphoreHandle,
MTC_ui32 millisecs MTC_OPTIONAL
));

Waits with timeout for the semaphore to become signalled. Returns 0 if succeeded in which case the semaphore's signal count will be decremented by one.

MTC_EXPORT( int, MTC_SemaphoreSignal(
MTC_SemaphoreHandle
));

Signals the semaphore.

Semaphore class

A C++ class to manage semaphores, Usage:

MTC_NAMESPACE::Semaphore semaphore("fred");
semaphore.Claim();
semaphore.Release();

This class can also be used to create a synchronised variable


Timers

MTC_EXPORT( MTC_TimerH, MTC_TimerAlloc(
MTC_FNPTR( MTC_FTimer) callback MTC_OPTIONAL,
MTC_PTR( void) MTC_OPTIONAL
));

Create a timer. The optional callback will be made whenever the timer expires.

MTC_EXPORT( void, MTC_TimerRelease(
MTC_TimerH
));

Destroy a timer.

MTC_EXPORT( int, MTC_TimerStart(
MTC_TimerH,
MTC_ui32 millisecs
));

Start a timer.

MTC_EXPORT( MTC_ui32, MTC_TimerStop(
MTC_TimerH
));

Stop a timer. The function returns the unexpired time.

MTC_EXPORT( int, MTC_TimerState(
MTC_TimerH
));

Returns the timer state - one of enum MTC_EtimerState.

MTC_EXPORT( MTC_ui32, MTC_TimerRemaining(
MTC_TimerH
));

Returns the number of milliseconds remaining.

MTC_EXPORT( MTC_ui32, MTC_TimerTicks( void))

A monotonic timing source. Returns milliseconds.

MTC_TimerDelta( t1, t2)

Retuns the difference between two times, allowing for time wraparound.

MTC_TimerLT( t1, t2)

Returns non-zero if t1 is less than t2, allowing for time wraparound.

MTC_TimerLE

Returns non-zero if t1 is less than or equal t2, allowing for time wraparound.

Timer class

A C++ class to manage timers, Usage:

MTC_NAMESPACE::Timer t;
t.Start( 1000);
MTC_ui32 remaining = t.TicksRemaining();
t.Stop();

The class may also be derived from and the virtual member function Timeout overridden to perform custom actions on timer expiry.

WaitableTimer class

A C++ class to manage timers that can be waited for, Usage:

MTC_NAMESPACE::WaitableTimer t;
t.Start( 50);
t.Wait();


Multiple readers, single writer locks

Permits multiple reader threads but a single writer thread of a shared resource.

MTC_EXPORT( MTC_MrswHandle, MTC_MrswOpen(
MTC_PTR( const char) name MTC_OPTIONAL
));

Create or open a MRSW. If name is NULL a MRSW is created. If name is not NULL then the first call will create the MRSW while subsequent calls increment its reference count.

MTC_EXPORT( MTC_MrswHandle, MTC_MrswReopen(
MTC_MrswHandle
));

Add a reference to an existing MRSW.

MTC_EXPORT( int, MTC_MrswClose(
MTC_MrswHandle
));

Decrements a MRSW 's reference count. When the count falls to 0 the object is destroyed.

MTC_EXPORT( int, MTC_MrswReadLock(
MTC_MrswHandle,
MTC_ui32 millisecs
));

Wait with timeout for a read lock.

The same thread may obtain multiple read locks.

If successful, other threads may obtain read locks but cannot obtain a write lock.

MTC_EXPORT( int, MTC_MrswReadUnlock(
MTC_MrswHandle
));

Release the read lock.

MTC_EXPORT( int, MTC_MrswWriteLock(
MTC_MrswHandle,
MTC_ui32 millisecs
));

Wait with timeout for a write lock.

If successful, no other thread may obtain a read or write lock.

The same thread may obtain multiple write locks.

A thread holding the only read lock may also obtain a write lock.

MTC_EXPORT( int, MTC_MrswWriteUnlock(
MTC_MrswHandle
));

Release the write lock.

MTC_EXPORT( int, MTC_MrswHaveWriteLock(
MTC_MrswHandle
));

Returns the number of write locks held.

Mrsw class

A C++ class to manage mrsws, Usage:

MTC_NAMESPACE::Mrsw mrsw("fred");
mrsw.ReadLock();
mrsw.ReadUnlock();
mrsw.WriteLock();
mrsw.WriteUnlock();

This class can also be used to create a synchronised variable


Message queues

Message queues enable two threads to synchronously exchange a block of data.

MTC_EXPORT( MTC_MsgqHandle, MTC_MsgqOpen(
MTC_PTR( const char) name MTC_OPTIONAL
));

Create or open a messageQ. If name is NULL a messageQ is created. If name is not NULL then the first call will create the messageQ while subsequent calls increment its reference count.

MTC_EXPORT( MTC_MsgqHandle, MTC_MsgqReopen(
MTC_MsgqHandle
));

Add a reference to an existing messageQ.

MTC_EXPORT( int, MTC_MsgqClose(
MTC_MsgqHandle
));

Decrements a messageQ's reference count. When the count falls to 0 the object is destroyed.

MTC_EXPORT( int, MTC_MsgqSend(
MTC_MsgqHandle,
MTC_PTR( const void) msg,
MTC_size_t msgSize,
MTC_PTR( void) replyMsg,
MTC_PTR( MTC_size_t) replyMsgSize,
MTC_ui32 millisecs
));

Send, with timeout, the data buffer 'msg' to the receiver then return the reply.

If the replyMsg is NULL then no data is received.

MTC_EXPORT( int, MTC_MsgqRecv(
MTC_MsgqHandle,
MTC_PTR( void) msg,
MTC_PTR( MTC_size_t) size,
MTC_ui32 millisecs
));

Wait, with timeout, for a message.

If msg is NULL then no data is received.

If size is NULL then the received message size is not returned.

MTC_EXPORT( MTC_size_t, MTC_MsgqRecvd(
MTC_MsgqHandle
));

Returns the total size of the last received message.

MTC_EXPORT( int, MTC_MsgqRead(
MTC_MsgqHandle,
MTC_size_t startOffset,
MTC_PTR( void) buffer,
MTC_PTR( MTC_size_t) bufferSize
));

Fetch a portion of the last received message.

MTC_EXPORT( int, MTC_MsgqReply(
MTC_MsgqHandle,
MTC_PTR( const void) reply,
MTC_size_t size
));

Send a reply to a received message. This concludes the message exchange.

If reply is NULL or size is 0 then no data is returned to the sender.

MessageQ class

A C++ class to manage messageQs, Usage:

Thread1:

MTC_NAMESPACE::MessageQ("fred") msgq;
char reply[16];
MTC_size_t size = sizeof(reply);
msgq.Send( "hello", 5, reply, size);

Thread2:

MTC_NAMESPACE::MessageQ("fred") msgq;
char msg[16];
msgq.Recv( msg, sizeof(msg));
msgq.Reply( "goodbye", 2);


Barriers

Barriers provide a mechanism for a fixed number of threads to rendezvous. Each thread entering the barrier is suspended until the predefined limit is reached, when all threads are released.

MTC_EXPORT( MTC_BarrierHandle, MTC_BarrierOpen(
MTC_PTR( const char) name,
MTC_ui32 count
));

Create or open a barrier. If name is NULL a barrier is created. If name is not NULL then the first call will create the barrier while subsequent calls increment its reference count.

The barrier will be completed when 'count' threads have entered..

MTC_EXPORT( MTC_BarrierHandle, MTC_BarrierReopen(
MTC_BarrierHandle
));

Add a reference to an existing barrier.

MTC_EXPORT( int, MTC_BarrierClose(
MTC_BarrierHandle
));

Decrements a barrier's reference count. When the count falls to 0 the object is destroyed.

MTC_EXPORT( int, MTC_BarrierWait(
MTC_BarrierHandle,
MTC_ui32 millisecs
));

Enter a barrier and wait with timeout until the predefined number have threads have entered.

Barrier class

A C++ class to manage barriers, Usage:

MTC_NAMESPACE::Barrier b("fred");
b.Wait();


C++ synchronised variables

The C++ header file "mtc_sync.h" contains a definition of the template class Synchronised. This class can be used to declare variables that can be safely shared between threads but may be accessed using standard C++ syntax. For instance:

typedef MTC_NAMESPACE::SynchronisedArith< double, MTC_NAMESPACE::Mutex> Double;
Double d;

Thread 1:

for ( int i = 0; i < 20; ++i)
d += 0.5

Thread 2:

for ( int i = 0; i < 20; ++i)
d += 0.5

The template can also be used with user defined types:

typedef struct { doubdle d;} mytype;
typedef MTC_NAMESPACE::Synchronised< mytype, MTC_NAMESPACE::Mrsw> MyData;
MyData data;

Thread 1:

for ( int i = 0; i < 20; ++i)
data->d += 0.5

Thread 2:

for ( int i = 0; i < 20; ++i)
data->d += 0.5


Heaps

MTC provides a local heap sub-allocator to permit threads to allocate memory without the locking necessary to use the C standard library heap or new/delete.

The heap uses a 'first fit' allocation strategy with recursive coalescence on free.

MTC_EXPORT( MTC_HeapH, MTC_HeapCreate(
MTC_PTR( void) buffer,
MTC_size_t size
));

Create a heap from the buffer.

MTC_EXPORT( void, MTC_HeapDestroy(
MTC_HeapH
));

Destroy an existing heap.

MTC_EXPORT( MTC_PTR( void), MTC_HeapMalloc(
MTC_HeapH,
MTC_size_t size
));

Returns a pointer to a buffer of size bytes allocated from the heap.

MTC_EXPORT( MTC_PTR( void), MTC_HeapRealloc(
MTC_HeapH,
MTC_PTR( void) ptr,
MTC_size_t size
));

Resize a pointer allocated from the heap.

If ptr is NULL then a new allocation is made.

If size is 0 then ptr is freed.

MTC_EXPORT( void, MTC_HeapFree(
MTC_HeapH
MTC_PTR( void)
));

Free a ptr allocated from the heap.

MTC_EXPORT( int, MTC_HeapVerify(
MTC_HeapH
));

Verify that the heap is intact.

In the DEBUG build additional checks are made to ensure that blocks have not been overwritten at their beginning or end.

MTC_EXPORT( int, MTC_HeapPtrIsValid(
MTC_HeapH,
MTC_PTR( void) block,
MTC_size_t
));

Verify that the block is contained in the heap.

MTC_EXPORT( MTC_size_t, MTC_HeapSize(
MTC_HeapH
));

Returns the total heap size in bytes.

MTC_EXPORT( MTC_size_t, MTC_HeapAvail(
MTC_HeapH
));

Returns the total available free space in the heap in bytes.

MTC_EXPORT( MTC_size_t, MTC_HeapMax(
MTC_HeapH
));

Returns the largest available free block in the heap in bytes.


Rebuilding the MTC library

The library should be built in the LIB sub-directory for the chosen platform/compiler.

The library can be built in either debug or release mode. To perform a debug build define the symbol DEBUG or MTC_DEBUG. The debug build includes significant runtime logging to enable problem tracking.

By default, many functions include assertion checks at entry and/or exit. To disable these, predefine the symbol NDEBUG.


Porting MTC

The process of porting MTC to a new compiler and/or platform is typically relatively straightforward. The bulk of the library is written in standard ANSI C90 and should need no change. The following files will need compiler specific changes:

In the header file "mtc_host.h" appropriate definitions of these macros should be made:

In the header file platform.h appropriate definitions of these macros should be made

In the header file debug.h appropriate definitions of these macros should be made

The bulk of the work will be in providing the platform adaptation module, the interface to which is defined in "platform.h". To help with this process an example with stub definitions is provided in skeleton.c.. This file should be read and thoroughly understood before proceeding.


Who are Software Systems?

We provide software development services and consultancy. Our products include:


Web: www.softsystem.co.uk
e-mail: info(at)softsystem.co.uk

Release History