Threads and semaphores

The MARTe framework offers a portable API for the management and synchronisation of multi-threaded applications.

Note

The preferred way of using threads is through the Services interface.

Threads

The Threads API allows to launch named threads bounded by to a specific CPU affinity (see ProcessorType) and with a defined amount of stack memory. The thread priority class and priority level can also be changed for every individual thread.

Warning

The change of some of the thread parameters (e.g. priorities) might fail if the user does not have the appropriate operating system permissions.

...
void IncrementDecrementFunction(const void * const parameters) {
   ...
}
...
Threads::BeginThread(&IncrementDecrementFunction, (int64 *)i, THREADS_DEFAULT_STACKSIZE, "NamedThread", ExceptionHandler::NotHandled, 0x1);

Note

When starting a thread, if the affinity parameter is not set, the thread will be allowed to run in the default CPU set. The application default CPU set can be changed with ProcessorType::SetDefaultCPUs(...)

The framework also allows to query the number of threads and its associated properties (see ThreadInformation and Threads::GetThreadInfoCopy).

Atomic operations

The Atomic API offers a set of portable functions that allow to perform atomic operations, i.e. operations that are guaranteed to be uninterruptible in a multi-threaded application environment.

...
b = Atomic::Exchange(&a, b);
...
Atomic::Increment(&a);
...
if (!Atomic::TestAndSet(&locked)) {

In particular, the Atomic::TestAndSet functions are used to implement fast polling mutex semaphores (see below) that can be deployed in environments that do not have an operating system scheduler.

High Resolution Timer

The HighResolutionTimer provides a portable abstraction of a timer and allows to measure ticks with high resolution.

These ticks (see HighResolutionTimer::Counter()) can be converted from/into time using the HighResolutionTimer::Frequency() and HighResolutionTimer::Period() function.

uint64 countStart = HighResolutionTimer::Counter();
...
while (HighResolutionTimer::Counter() < countEnd);

Sleep

The Sleep API allows to voluntarily return the control back to the scheduler. The only exception is the Sleep::Busy which waits for a given time to elapse without yielding the CPU.

...
MARTe::Sleep::Sec(1e-3);
...

Semaphores

The framework offers three distinct sets of portables semaphores:

  • MutexSem: mutex semaphore that offers exclusive access to a critical area;

  • EventSem: barrier semaphore that, after being released, allows shared access to a critical area;

  • FastPollingMutexSem and FastPollingEventSem: mutex and event semaphores that do not require an operating system scheduler. Both work by polling, with atomic operations, a given memory location.

...
MARTe::MutexSem mutexSem;
mutexSem.Create(false);
if(!mutexSem.Lock()){
...
if(!mutexSem.UnLock()){
...

The event semaphores need the Event::Reset method to be called in order to lower the barrier.

Warning

The Event::Reset operation is not atomic. As consequence, depending on the specific use-case, it may require a mutex semaphore to protect access to a given shared resource while this operating is being performed.

...
MARTe::EventSem eventSem;
eventSem.Create();
...
mutexSem.Lock();
if (needsReset) {
   needsReset = false;
   eventSem.Reset();
}
mutexSem.UnLock();
eventSem.Wait();

Examples

Atomic

The following example demonstrates how to use the Atomic API.

Atomic example (AtomicExample1)
 1/**
 2 * @file AtomicExample1.cpp
 3 * @brief Source file for class AtomicExample1
 4 * @date 23/04/2018
 5 * @author Andre Neto
 6 *
 7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 8 * the Development of Fusion Energy ('Fusion for Energy').
 9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
11 * You may not use this work except in compliance with the Licence.
12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
13 *
14 * @warning Unless required by applicable law or agreed to in writing, 
15 * software distributed under the Licence is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17 * or implied. See the Licence permissions and limitations under the Licence.
18
19 * @details This source file contains the definition of all the methods for
20 * the class AtomicExample1 (public, protected, and private). Be aware that some 
21 * methods, such as those inline could be defined on the header file, instead.
22 */
23
24/*---------------------------------------------------------------------------*/
25/*                         Standard header includes                          */
26/*---------------------------------------------------------------------------*/
27
28/*---------------------------------------------------------------------------*/
29/*                         Project header includes                           */
30/*---------------------------------------------------------------------------*/
31#include "AdvancedErrorManagement.h"
32#include "Atomic.h"
33#include "ErrorLoggerExample.h"
34#include "Sleep.h"
35#include "Threads.h"
36
37/*---------------------------------------------------------------------------*/
38/*                           Static definitions                              */
39/*---------------------------------------------------------------------------*/
40
41/*---------------------------------------------------------------------------*/
42/*                           Method definitions                              */
43/*---------------------------------------------------------------------------*/
44
45//The lock variable
46static MARTe::int32 locked = 0;
47
48//Unlock after sleeping
49static void UnlockWithTestAndSet (const void * const args) {
50    MARTe::Sleep::Sec(2.0);
51    locked = 0;
52}
53
54int main(int argc, char *argv[]) {
55    using namespace MARTe;
56    SetErrorProcessFunction(&ErrorProcessExampleFunction);
57
58    int32 a = 3;
59    int32 b = 4;
60
61    REPORT_ERROR_STATIC(ErrorManagement::Information, "Starting with a=%d b=%d", a, b);
62    //Exchange
63    b = Atomic::Exchange(&a, b);
64    REPORT_ERROR_STATIC(ErrorManagement::Information, "After exchanging a=%d b=%d", a, b);
65    //Atomic increment
66    Atomic::Increment(&a);
67    REPORT_ERROR_STATIC(ErrorManagement::Information, "After incrementing a=%d", a);
68
69    locked = 1;
70    //Try to enter a locked region
71    if (!Atomic::TestAndSet(&locked)) {
72        REPORT_ERROR_STATIC(ErrorManagement::Information, "As expected TestAndSet failed");
73    }
74    locked = 0;
75    if (!Atomic::TestAndSet(&locked)) {
76        REPORT_ERROR_STATIC(ErrorManagement::FatalError, "TestAndSet and failed");
77    }
78
79    //Lock again
80    REPORT_ERROR_STATIC(ErrorManagement::Information, "locked should now be 1 locked = %d", locked);
81    //Create thread to perform the unlock
82    REPORT_ERROR_STATIC(ErrorManagement::Information, "Going to wait for thread to unlock");
83    Threads::BeginThread(UnlockWithTestAndSet);
84    //Spin lock
85    while (!Atomic::TestAndSet(&locked))
86        ;
87
88    REPORT_ERROR_STATIC(ErrorManagement::Information, "Unlocked by thread");
89
90    return 0;
91}

HighResolutionTimer

The following is an example of a possible use of the HighResolutionTimer.

HighResolutionTimer example (HighResolutionTimerExample1)
 1/**
 2 * @file HighResolutionTimerExample1.cpp
 3 * @brief Source file for class HighResolutionTimerExample1
 4 * @date 23/04/2018
 5 * @author Andre Neto
 6 *
 7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 8 * the Development of Fusion Energy ('Fusion for Energy').
 9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
11 * You may not use this work except in compliance with the Licence.
12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
13 *
14 * @warning Unless required by applicable law or agreed to in writing, 
15 * software distributed under the Licence is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17 * or implied. See the Licence permissions and limitations under the Licence.
18
19 * @details This source file contains the definition of all the methods for
20 * the class HighResolutionTimerExample1 (public, protected, and private). Be aware that some 
21 * methods, such as those inline could be defined on the header file, instead.
22 */
23
24/*---------------------------------------------------------------------------*/
25/*                         Standard header includes                          */
26/*---------------------------------------------------------------------------*/
27
28/*---------------------------------------------------------------------------*/
29/*                         Project header includes                           */
30/*---------------------------------------------------------------------------*/
31#include "AdvancedErrorManagement.h"
32#include "ErrorLoggerExample.h"
33#include "HighResolutionTimer.h"
34
35/*---------------------------------------------------------------------------*/
36/*                           Static definitions                              */
37/*---------------------------------------------------------------------------*/
38
39/*---------------------------------------------------------------------------*/
40/*                           Method definitions                              */
41/*---------------------------------------------------------------------------*/
42
43
44int main(int argc, char *argv[]) {
45    using namespace MARTe;
46    SetErrorProcessFunction(&ErrorProcessExampleFunction);
47
48    float64 sleepSeconds = 1.1;
49    float64 countUntilF = sleepSeconds * HighResolutionTimer::Frequency();
50    uint64 countUntil = static_cast<uint64>(countUntilF);
51
52    uint64 countStart = HighResolutionTimer::Counter();
53    uint64 countEnd = countStart + countUntil;
54    while (HighResolutionTimer::Counter() < countEnd);
55    float64 countDiff = static_cast<float64>(HighResolutionTimer::Counter() - countStart);
56    float64 sleptSeconds = countDiff * HighResolutionTimer::Period();
57    REPORT_ERROR_STATIC(ErrorManagement::Information, "Was asked to wait for %e and waited for %e", sleepSeconds, sleptSeconds);
58
59    return 0;
60}

Instructions on how to compile and execute the examples can be found here.

Threads

The following example illustrates how to manage threads.

Thread example (ThreadsExample1)
 1/**
 2 * @file ThreadsExample1.cpp
 3 * @brief Source file for class ThreadsExample1
 4 * @date 23/08/2018
 5 * @author Andre Neto
 6 *
 7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 8 * the Development of Fusion Energy ('Fusion for Energy').
 9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
11 * You may not use this work except in compliance with the Licence.
12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
13 *
14 * @warning Unless required by applicable law or agreed to in writing, 
15 * software distributed under the Licence is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17 * or implied. See the Licence permissions and limitations under the Licence.
18
19 * @details This source file contains the definition of all the methods for
20 * the class ThreadsExample1 (public, protected, and private). Be aware that some 
21 * methods, such as those inline could be defined on the header file, instead.
22 */
23
24/*---------------------------------------------------------------------------*/
25/*                         Standard header includes                          */
26/*---------------------------------------------------------------------------*/
27
28/*---------------------------------------------------------------------------*/
29/*                         Project header includes                           */
30/*---------------------------------------------------------------------------*/
31#include "AdvancedErrorManagement.h"
32#include "ErrorLoggerExample.h"
33#include "Sleep.h"
34#include "StreamString.h"
35#include "Threads.h"
36#include "ThreadInformation.h"
37
38/*---------------------------------------------------------------------------*/
39/*                           Static definitions                              */
40/*---------------------------------------------------------------------------*/
41
42/*---------------------------------------------------------------------------*/
43/*                           Method definitions                              */
44/*---------------------------------------------------------------------------*/
45
46//An exit condition for all the threads
47static MARTe::int32 exitAfterCalls = 500;
48
49//Shared variable to be incremented by the threads
50static MARTe::int32 sharedVariable = 0;
51
52//Simulate complicated analysis
53static void ComplexAnalysis(MARTe::float32 sec) {
54    MARTe::Sleep::Sec(sec);
55}
56
57//Thread function call back
58static void IncrementDecrementFunction(const void * const params) {
59    using namespace MARTe;
60    float32 *sleepSec = (float32 *) params;
61    ThreadInformation info;
62    Threads::GetThreadInfoCopy(info, Threads::Id());
63    REPORT_ERROR_STATIC(MARTe::ErrorManagement::Information, "Thread %s started", info.ThreadName());
64    while (exitAfterCalls > 0) {
65        sharedVariable++;
66        ComplexAnalysis(*sleepSec);
67        sharedVariable--;
68        exitAfterCalls--;
69    }
70}
71
72int main(int argc, char *argv[]) {
73    using namespace MARTe;
74    SetErrorProcessFunction(&ErrorProcessExampleFunction);
75    ProcessorType::SetDefaultCPUs(0x1u);
76
77    int32 numberOfThreads = 10;
78    int32 i = 0;
79    float32 *sleepTimes = new float32[numberOfThreads];
80
81    REPORT_ERROR_STATIC(ErrorManagement::Information, "Number of threads = %d", numberOfThreads)
82    ;
83    for (i = 0; i < numberOfThreads; i++) {
84        StreamString threadName;
85        threadName.Printf("Thread-%d", i);
86        sleepTimes[i] = ((i + 1) * 1e-3);
87        Threads::BeginThread(&IncrementDecrementFunction, &sleepTimes[i], THREADS_DEFAULT_STACKSIZE, threadName.Buffer(), ExceptionHandler::NotHandled, 0x1);
88    }
89    //Wait for the thread to run for exitAfterCalls times
90    while (exitAfterCalls > 0) {
91        MARTe::Sleep::Sec(1e-3);
92    }
93    REPORT_ERROR_STATIC(ErrorManagement::Information, "Value of sharedVariable = %d", sharedVariable);
94    delete sleepTimes;
95    return 0;
96}

Semaphores

The following is an example of the use of mutex and event semaphores and on how to query the thread properties.

Event and mutex semaphores (ThreadsExample2)
  1/**
  2 * @file ThreadsExample2.cpp
  3 * @brief Source file for class ThreadsExample2
  4 * @date 23/04/2018
  5 * @author Andre Neto
  6 *
  7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
  8 * the Development of Fusion Energy ('Fusion for Energy').
  9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 11 * You may not use this work except in compliance with the Licence.
 12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 13 *
 14 * @warning Unless required by applicable law or agreed to in writing, 
 15 * software distributed under the Licence is distributed on an "AS IS"
 16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 17 * or implied. See the Licence permissions and limitations under the Licence.
 18
 19 * @details This source file contains the definition of all the methods for
 20 * the class ThreadsExample2 (public, protected, and private). Be aware that some 
 21 * methods, such as those inline could be defined on the header file, instead.
 22 */
 23
 24/*---------------------------------------------------------------------------*/
 25/*                         Standard header includes                          */
 26/*---------------------------------------------------------------------------*/
 27
 28/*---------------------------------------------------------------------------*/
 29/*                         Project header includes                           */
 30/*---------------------------------------------------------------------------*/
 31#include "AdvancedErrorManagement.h"
 32#include "ErrorLoggerExample.h"
 33#include "EventSem.h"
 34#include "MutexSem.h"
 35#include "Sleep.h"
 36#include "StreamString.h"
 37#include "Threads.h"
 38#include "ThreadInformation.h"
 39/*---------------------------------------------------------------------------*/
 40/*                           Static definitions                              */
 41/*---------------------------------------------------------------------------*/
 42
 43/*---------------------------------------------------------------------------*/
 44/*                           Method definitions                              */
 45/*---------------------------------------------------------------------------*/
 46
 47//An exit condition for all the threads
 48static MARTe::int32 exitAfterCalls = 50;
 49
 50//Shared variable to be incremented by the threads
 51static MARTe::int32 sharedVariable = 0;
 52
 53//Event semaphore so that all the threads start at the same time
 54static MARTe::EventSem eventSem;
 55
 56//Mutex semaphore protecting the sharedVariable
 57static MARTe::MutexSem mutexSem;
 58
 59//The number of threads
 60static MARTe::int32 numberOfThreads = 10;
 61
 62//The number of threads to be started
 63static MARTe::int32 numberOfThreadsLeftToStart = numberOfThreads;
 64
 65//The number of threads terminated
 66static MARTe::int32 numberOfThreadsTerminated = 0;
 67
 68//Simulate complicated analysis
 69static void ComplexAnalysis(float sec){
 70    MARTe::Sleep::Sec(sec);
 71}
 72
 73//Thread function call back
 74static void IncrementDecrementFunction(const void * const params){
 75    using namespace MARTe;
 76    float32 *sleepSec = (float32 *) params;
 77    ThreadInformation info;
 78    Threads::GetThreadInfoCopy(info, Threads::Id());
 79    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s waiting for event sem", info.ThreadName());
 80    numberOfThreadsLeftToStart--;
 81    if(!eventSem.Wait()){
 82        REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in event sem (timeout?)", info.ThreadName());
 83    }
 84    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s started", info.ThreadName());
 85    while(exitAfterCalls > 0){
 86        //The mutex protects this region of code
 87        if(!mutexSem.Lock()){
 88            REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in mutex sem (timeout?)", info.ThreadName());
 89        }
 90        sharedVariable++;
 91        ComplexAnalysis(*sleepSec);
 92        sharedVariable--;
 93        exitAfterCalls--;
 94        if(!mutexSem.UnLock()){
 95            REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to unlock mutex sem", info.ThreadName());
 96        }
 97    }
 98    numberOfThreadsTerminated++;
 99}
100
101int main(int argc, char *argv[]){
102    using namespace MARTe;
103    SetErrorProcessFunction(&ErrorProcessExampleFunction);
104
105    int32 i = 0;
106
107    float32 *sleepTimes = new float32[numberOfThreads];
108    //Configure the semaphores
109    eventSem.Create();
110    eventSem.Reset();
111    mutexSem.Create(false);
112
113    REPORT_ERROR_STATIC(ErrorManagement::Information, "Number of threads = %d", numberOfThreads);
114    for(i=0; i<numberOfThreads; i++){
115        StreamString threadName;
116        threadName.Printf("Thread-%d", i);
117        sleepTimes[i] = ((i + 1) * 1e-3);
118        Threads::BeginThread(&IncrementDecrementFunction, &sleepTimes[i], THREADS_DEFAULT_STACKSIZE, threadName.Buffer(), ExceptionHandler::NotHandled, 0x1);
119    }
120    //Wait for all threads to be ready and waiting on the event semaphore (this isn't actually needed, but guarantees that the
121    //REPORT_ERROR_STATIC come in the right order)
122    while(!Atomic::TestAndSet(&numberOfThreadsLeftToStart));
123
124    //List all the threads
125    int32 numberOfThreads = Threads::NumberOfThreads();
126    for (i=0; i<numberOfThreads; i++) {
127        ThreadIdentifier tid = Threads::FindByIndex(i);
128        ThreadInformation tinfo;
129        Threads::GetThreadInfoCopy(tinfo, tid);
130        const char8 * const threadName = tinfo.ThreadName();
131        Threads::PriorityClassType pclass = tinfo.GetPriorityClass();
132        uint8 plevel = tinfo.GetPriorityLevel();
133        REPORT_ERROR_STATIC(ErrorManagement::Information, "Name: %s Class: %d Level: %d", threadName, pclass, plevel);
134    }
135
136    REPORT_ERROR_STATIC(ErrorManagement::Information, "Starting all threads!");
137    //Allow all threads to start
138    eventSem.Post();
139    //Wait for the thread to run for exitAfterCalls times
140    while(exitAfterCalls > 0){
141        MARTe::Sleep::Sec(1e-3);
142    }
143    //Wait to check if some calculation are still to be terminated
144    while (numberOfThreadsTerminated < numberOfThreads) {
145        MARTe::Sleep::Sec(1e-3);
146    }
147    REPORT_ERROR_STATIC(ErrorManagement::Information, "Value of sharedVariable = %d (should be zero)", sharedVariable);
148    mutexSem.Close();
149    delete sleepTimes;
150    return 0;
151}

FastPolling Semaphores

The same example as above, but implemented using the FastPolling semaphores.

FastPolling event and mutex semaphores (ThreadsExample3)
  1/**
  2 * @file ThreadsExample2.cpp
  3 * @brief Source file for class ThreadsExample2
  4 * @date 23/04/2018
  5 * @author Andre Neto
  6 *
  7 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
  8 * the Development of Fusion Energy ('Fusion for Energy').
  9 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 10 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 11 * You may not use this work except in compliance with the Licence.
 12 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 13 *
 14 * @warning Unless required by applicable law or agreed to in writing, 
 15 * software distributed under the Licence is distributed on an "AS IS"
 16 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 17 * or implied. See the Licence permissions and limitations under the Licence.
 18
 19 * @details This source file contains the definition of all the methods for
 20 * the class ThreadsExample2 (public, protected, and private). Be aware that some 
 21 * methods, such as those inline could be defined on the header file, instead.
 22 */
 23
 24/*---------------------------------------------------------------------------*/
 25/*                         Standard header includes                          */
 26/*---------------------------------------------------------------------------*/
 27
 28/*---------------------------------------------------------------------------*/
 29/*                         Project header includes                           */
 30/*---------------------------------------------------------------------------*/
 31#include "AdvancedErrorManagement.h"
 32#include "ErrorLoggerExample.h"
 33#include "FastPollingEventSem.h"
 34#include "FastPollingMutexSem.h"
 35#include "Sleep.h"
 36#include "StreamString.h"
 37#include "Threads.h"
 38#include "ThreadInformation.h"
 39
 40/*---------------------------------------------------------------------------*/
 41/*                           Static definitions                              */
 42/*---------------------------------------------------------------------------*/
 43
 44/*---------------------------------------------------------------------------*/
 45/*                           Method definitions                              */
 46/*---------------------------------------------------------------------------*/
 47
 48//An exit condition for all the threads
 49static MARTe::int32 exitAfterCalls = 50;
 50
 51//Shared variable to be incremented by the threads
 52static MARTe::int32 sharedVariable = 0;
 53
 54//Event semaphore so that all the threads start at the same time
 55static MARTe::FastPollingEventSem eventSem;
 56
 57//Mutex semaphore protecting the sharedVariable
 58static MARTe::FastPollingMutexSem mutexSem;
 59
 60//The number of threads
 61static MARTe::int32 numberOfThreads = 10;
 62
 63//The number of threads to be started
 64static MARTe::int32 numberOfThreadsLeftToStart = numberOfThreads;
 65
 66//The number of threads terminated
 67static MARTe::int32 numberOfThreadsTerminated = 0;
 68
 69//Simulate complicated analysis
 70static void ComplexAnalysis(float sec){
 71    MARTe::Sleep::Sec(sec);
 72}
 73
 74//Thread function call back
 75static void IncrementDecrementFunction(const void * const params){
 76    using namespace MARTe;
 77    float32 *sleepSec = (float32 *) params;
 78    ThreadInformation info;
 79    Threads::GetThreadInfoCopy(info, Threads::Id());
 80    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s waiting for event sem", info.ThreadName());
 81    numberOfThreadsLeftToStart--;
 82    if(!eventSem.FastWait()){
 83        REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in event sem (timeout?)", info.ThreadName());
 84    }
 85    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s started", info.ThreadName());
 86    while(exitAfterCalls > 0){
 87        //The mutex protects this region of code
 88        if(!mutexSem.FastLock()){
 89            REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in mutex sem (timeout?)", info.ThreadName());
 90        }
 91        sharedVariable++;
 92        ComplexAnalysis(*sleepSec);
 93        sharedVariable--;
 94        exitAfterCalls--;
 95        mutexSem.FastUnLock();
 96    }
 97    numberOfThreadsTerminated++;
 98}
 99
100int main(int argc, char *argv[]){
101    using namespace MARTe;
102    SetErrorProcessFunction(&ErrorProcessExampleFunction);
103
104    int32 i = 0;
105
106    float32 *sleepTimes = new float32[numberOfThreads];
107    //Configure the semaphores
108    eventSem.Create();
109    eventSem.Reset();
110    mutexSem.Create(false);
111
112    REPORT_ERROR_STATIC(ErrorManagement::Information, "Number of threads = %d", numberOfThreads);
113    for(i=0; i<numberOfThreads; i++){
114        StreamString threadName;
115        threadName.Printf("Thread-%d", i);
116        sleepTimes[i] = ((i + 1) * 1e-3);
117        Threads::BeginThread(&IncrementDecrementFunction, &sleepTimes[i], THREADS_DEFAULT_STACKSIZE, threadName.Buffer(), ExceptionHandler::NotHandled, 0x1);
118    }
119    //FastWait for all threads to be ready and waiting on the event semaphore (this isn't actually needed, but guarantees that the
120    //REPORT_ERROR_STATIC come in the right order)
121    while(!Atomic::TestAndSet(&numberOfThreadsLeftToStart));
122
123    REPORT_ERROR_STATIC(ErrorManagement::Information, "Starting all threads!");
124    //Allow all threads to start
125    eventSem.FastPost();
126    //FastWait for the thread to run for exitAfterCalls times
127    while(exitAfterCalls > 0){
128        MARTe::Sleep::Sec(1e-3);
129    }
130    //Wait to check if some calculation are still to be terminated
131    while (numberOfThreadsTerminated < numberOfThreads) {
132        MARTe::Sleep::Sec(1e-3);
133    }
134    REPORT_ERROR_STATIC(ErrorManagement::Information, "Value of sharedVariable = %d (should be zero)", sharedVariable);
135    delete sleepTimes;
136    return 0;
137}

Instructions on how to compile and execute the examples can be found here.