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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * @file AtomicExample1.cpp
 * @brief Source file for class AtomicExample1
 * @date 23/04/2018
 * @author Andre Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class AtomicExample1 (public, protected, and private). Be aware that some 
 * methods, such as those inline could be defined on the header file, instead.
 */

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "Atomic.h"
#include "ErrorLoggerExample.h"
#include "Sleep.h"
#include "Threads.h"

/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/

//The lock variable
static MARTe::int32 locked = 0;

//Unlock after sleeping
static void UnlockWithTestAndSet (const void * const args) {
    MARTe::Sleep::Sec(2.0);
    locked = 0;
}

int main(int argc, char *argv[]) {
    using namespace MARTe;
    SetErrorProcessFunction(&ErrorProcessExampleFunction);

    int32 a = 3;
    int32 b = 4;

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Starting with a=%d b=%d", a, b);
    //Exchange
    b = Atomic::Exchange(&a, b);
    REPORT_ERROR_STATIC(ErrorManagement::Information, "After exchanging a=%d b=%d", a, b);
    //Atomic increment
    Atomic::Increment(&a);
    REPORT_ERROR_STATIC(ErrorManagement::Information, "After incrementing a=%d", a);

    locked = 1;
    //Try to enter a locked region
    if (!Atomic::TestAndSet(&locked)) {
        REPORT_ERROR_STATIC(ErrorManagement::Information, "As expected TestAndSet failed");
    }
    locked = 0;
    if (!Atomic::TestAndSet(&locked)) {
        REPORT_ERROR_STATIC(ErrorManagement::FatalError, "TestAndSet and failed");
    }

    //Lock again
    REPORT_ERROR_STATIC(ErrorManagement::Information, "locked should now be 1 locked = %d", locked);
    //Create thread to perform the unlock
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Going to wait for thread to unlock");
    Threads::BeginThread(UnlockWithTestAndSet);
    //Spin lock
    while (!Atomic::TestAndSet(&locked))
        ;

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Unlocked by thread");

    return 0;
}

HighResolutionTimer

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

HighResolutionTimer example (HighResolutionTimerExample1)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
 * @file HighResolutionTimerExample1.cpp
 * @brief Source file for class HighResolutionTimerExample1
 * @date 23/04/2018
 * @author Andre Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class HighResolutionTimerExample1 (public, protected, and private). Be aware that some 
 * methods, such as those inline could be defined on the header file, instead.
 */

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "ErrorLoggerExample.h"
#include "HighResolutionTimer.h"

/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/


int main(int argc, char *argv[]) {
    using namespace MARTe;
    SetErrorProcessFunction(&ErrorProcessExampleFunction);

    float64 sleepSeconds = 1.1;
    float64 countUntilF = sleepSeconds * HighResolutionTimer::Frequency();
    uint64 countUntil = static_cast<uint64>(countUntilF);

    uint64 countStart = HighResolutionTimer::Counter();
    uint64 countEnd = countStart + countUntil;
    while (HighResolutionTimer::Counter() < countEnd);
    float64 countDiff = static_cast<float64>(HighResolutionTimer::Counter() - countStart);
    float64 sleptSeconds = countDiff * HighResolutionTimer::Period();
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Was asked to wait for %e and waited for %e", sleepSeconds, sleptSeconds);

    return 0;
}

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
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
 * @file ThreadsExample1.cpp
 * @brief Source file for class ThreadsExample1
 * @date 23/08/2018
 * @author Andre Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class ThreadsExample1 (public, protected, and private). Be aware that some 
 * methods, such as those inline could be defined on the header file, instead.
 */

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "ErrorLoggerExample.h"
#include "Sleep.h"
#include "StreamString.h"
#include "Threads.h"
#include "ThreadInformation.h"

/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/

//An exit condition for all the threads
static MARTe::int32 exitAfterCalls = 500;

//Shared variable to be incremented by the threads
static MARTe::int32 sharedVariable = 0;

//Simulate complicated analysis
static void ComplexAnalysis(MARTe::float32 sec) {
    MARTe::Sleep::Sec(sec);
}

//Thread function call back
static void IncrementDecrementFunction(const void * const params) {
    using namespace MARTe;
    float32 *sleepSec = (float32 *) params;
    ThreadInformation info;
    Threads::GetThreadInfoCopy(info, Threads::Id());
    REPORT_ERROR_STATIC(MARTe::ErrorManagement::Information, "Thread %s started", info.ThreadName());
    while (exitAfterCalls > 0) {
        sharedVariable++;
        ComplexAnalysis(*sleepSec);
        sharedVariable--;
        exitAfterCalls--;
    }
}

int main(int argc, char *argv[]) {
    using namespace MARTe;
    SetErrorProcessFunction(&ErrorProcessExampleFunction);
    ProcessorType::SetDefaultCPUs(0x1u);

    int32 numberOfThreads = 10;
    int32 i = 0;
    float32 *sleepTimes = new float32[numberOfThreads];

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Number of threads = %d", numberOfThreads)
    ;
    for (i = 0; i < numberOfThreads; i++) {
        StreamString threadName;
        threadName.Printf("Thread-%d", i);
        sleepTimes[i] = ((i + 1) * 1e-3);
        Threads::BeginThread(&IncrementDecrementFunction, &sleepTimes[i], THREADS_DEFAULT_STACKSIZE, threadName.Buffer(), ExceptionHandler::NotHandled, 0x1);
    }
    //Wait for the thread to run for exitAfterCalls times
    while (exitAfterCalls > 0) {
        MARTe::Sleep::Sec(1e-3);
    }
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Value of sharedVariable = %d", sharedVariable);
    delete sleepTimes;
    return 0;
}

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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
 * @file ThreadsExample2.cpp
 * @brief Source file for class ThreadsExample2
 * @date 23/04/2018
 * @author Andre Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class ThreadsExample2 (public, protected, and private). Be aware that some 
 * methods, such as those inline could be defined on the header file, instead.
 */

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "ErrorLoggerExample.h"
#include "EventSem.h"
#include "MutexSem.h"
#include "Sleep.h"
#include "StreamString.h"
#include "Threads.h"
#include "ThreadInformation.h"
/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/

//An exit condition for all the threads
static MARTe::int32 exitAfterCalls = 50;

//Shared variable to be incremented by the threads
static MARTe::int32 sharedVariable = 0;

//Event semaphore so that all the threads start at the same time
static MARTe::EventSem eventSem;

//Mutex semaphore protecting the sharedVariable
static MARTe::MutexSem mutexSem;

//The number of threads
static MARTe::int32 numberOfThreads = 10;

//The number of threads to be started
static MARTe::int32 numberOfThreadsLeftToStart = numberOfThreads;

//The number of threads terminated
static MARTe::int32 numberOfThreadsTerminated = 0;

//Simulate complicated analysis
static void ComplexAnalysis(float sec){
    MARTe::Sleep::Sec(sec);
}

//Thread function call back
static void IncrementDecrementFunction(const void * const params){
    using namespace MARTe;
    float32 *sleepSec = (float32 *) params;
    ThreadInformation info;
    Threads::GetThreadInfoCopy(info, Threads::Id());
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s waiting for event sem", info.ThreadName());
    numberOfThreadsLeftToStart--;
    if(!eventSem.Wait()){
        REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in event sem (timeout?)", info.ThreadName());
    }
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s started", info.ThreadName());
    while(exitAfterCalls > 0){
        //The mutex protects this region of code
        if(!mutexSem.Lock()){
            REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in mutex sem (timeout?)", info.ThreadName());
        }
        sharedVariable++;
        ComplexAnalysis(*sleepSec);
        sharedVariable--;
        exitAfterCalls--;
        if(!mutexSem.UnLock()){
            REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to unlock mutex sem", info.ThreadName());
        }
    }
    numberOfThreadsTerminated++;
}

int main(int argc, char *argv[]){
    using namespace MARTe;
    SetErrorProcessFunction(&ErrorProcessExampleFunction);

    int32 i = 0;

    float32 *sleepTimes = new float32[numberOfThreads];
    //Configure the semaphores
    eventSem.Create();
    eventSem.Reset();
    mutexSem.Create(false);

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Number of threads = %d", numberOfThreads);
    for(i=0; i<numberOfThreads; i++){
        StreamString threadName;
        threadName.Printf("Thread-%d", i);
        sleepTimes[i] = ((i + 1) * 1e-3);
        Threads::BeginThread(&IncrementDecrementFunction, &sleepTimes[i], THREADS_DEFAULT_STACKSIZE, threadName.Buffer(), ExceptionHandler::NotHandled, 0x1);
    }
    //Wait for all threads to be ready and waiting on the event semaphore (this isn't actually needed, but guarantees that the
    //REPORT_ERROR_STATIC come in the right order)
    while(!Atomic::TestAndSet(&numberOfThreadsLeftToStart));

    //List all the threads
    int32 numberOfThreads = Threads::NumberOfThreads();
    for (i=0; i<numberOfThreads; i++) {
        ThreadIdentifier tid = Threads::FindByIndex(i);
        ThreadInformation tinfo;
        Threads::GetThreadInfoCopy(tinfo, tid);
        const char8 * const threadName = tinfo.ThreadName();
        Threads::PriorityClassType pclass = tinfo.GetPriorityClass();
        uint8 plevel = tinfo.GetPriorityLevel();
        REPORT_ERROR_STATIC(ErrorManagement::Information, "Name: %s Class: %d Level: %d", threadName, pclass, plevel);
    }

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Starting all threads!");
    //Allow all threads to start
    eventSem.Post();
    //Wait for the thread to run for exitAfterCalls times
    while(exitAfterCalls > 0){
        MARTe::Sleep::Sec(1e-3);
    }
    //Wait to check if some calculation are still to be terminated
    while (numberOfThreadsTerminated < numberOfThreads) {
        MARTe::Sleep::Sec(1e-3);
    }
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Value of sharedVariable = %d (should be zero)", sharedVariable);
    mutexSem.Close();
    delete sleepTimes;
    return 0;
}

FastPolling Semaphores

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

FastPolling event and mutex semaphores (ThreadsExample3)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
 * @file ThreadsExample2.cpp
 * @brief Source file for class ThreadsExample2
 * @date 23/04/2018
 * @author Andre Neto
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing, 
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.

 * @details This source file contains the definition of all the methods for
 * the class ThreadsExample2 (public, protected, and private). Be aware that some 
 * methods, such as those inline could be defined on the header file, instead.
 */

/*---------------------------------------------------------------------------*/
/*                         Standard header includes                          */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                         Project header includes                           */
/*---------------------------------------------------------------------------*/
#include "AdvancedErrorManagement.h"
#include "ErrorLoggerExample.h"
#include "FastPollingEventSem.h"
#include "FastPollingMutexSem.h"
#include "Sleep.h"
#include "StreamString.h"
#include "Threads.h"
#include "ThreadInformation.h"

/*---------------------------------------------------------------------------*/
/*                           Static definitions                              */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/*                           Method definitions                              */
/*---------------------------------------------------------------------------*/

//An exit condition for all the threads
static MARTe::int32 exitAfterCalls = 50;

//Shared variable to be incremented by the threads
static MARTe::int32 sharedVariable = 0;

//Event semaphore so that all the threads start at the same time
static MARTe::FastPollingEventSem eventSem;

//Mutex semaphore protecting the sharedVariable
static MARTe::FastPollingMutexSem mutexSem;

//The number of threads
static MARTe::int32 numberOfThreads = 10;

//The number of threads to be started
static MARTe::int32 numberOfThreadsLeftToStart = numberOfThreads;

//The number of threads terminated
static MARTe::int32 numberOfThreadsTerminated = 0;

//Simulate complicated analysis
static void ComplexAnalysis(float sec){
    MARTe::Sleep::Sec(sec);
}

//Thread function call back
static void IncrementDecrementFunction(const void * const params){
    using namespace MARTe;
    float32 *sleepSec = (float32 *) params;
    ThreadInformation info;
    Threads::GetThreadInfoCopy(info, Threads::Id());
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s waiting for event sem", info.ThreadName());
    numberOfThreadsLeftToStart--;
    if(!eventSem.FastWait()){
        REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in event sem (timeout?)", info.ThreadName());
    }
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Thread %s started", info.ThreadName());
    while(exitAfterCalls > 0){
        //The mutex protects this region of code
        if(!mutexSem.FastLock()){
            REPORT_ERROR_STATIC(ErrorManagement::FatalError, "Thread %s failed to wait in mutex sem (timeout?)", info.ThreadName());
        }
        sharedVariable++;
        ComplexAnalysis(*sleepSec);
        sharedVariable--;
        exitAfterCalls--;
        mutexSem.FastUnLock();
    }
    numberOfThreadsTerminated++;
}

int main(int argc, char *argv[]){
    using namespace MARTe;
    SetErrorProcessFunction(&ErrorProcessExampleFunction);

    int32 i = 0;

    float32 *sleepTimes = new float32[numberOfThreads];
    //Configure the semaphores
    eventSem.Create();
    eventSem.Reset();
    mutexSem.Create(false);

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Number of threads = %d", numberOfThreads);
    for(i=0; i<numberOfThreads; i++){
        StreamString threadName;
        threadName.Printf("Thread-%d", i);
        sleepTimes[i] = ((i + 1) * 1e-3);
        Threads::BeginThread(&IncrementDecrementFunction, &sleepTimes[i], THREADS_DEFAULT_STACKSIZE, threadName.Buffer(), ExceptionHandler::NotHandled, 0x1);
    }
    //FastWait for all threads to be ready and waiting on the event semaphore (this isn't actually needed, but guarantees that the
    //REPORT_ERROR_STATIC come in the right order)
    while(!Atomic::TestAndSet(&numberOfThreadsLeftToStart));

    REPORT_ERROR_STATIC(ErrorManagement::Information, "Starting all threads!");
    //Allow all threads to start
    eventSem.FastPost();
    //FastWait for the thread to run for exitAfterCalls times
    while(exitAfterCalls > 0){
        MARTe::Sleep::Sec(1e-3);
    }
    //Wait to check if some calculation are still to be terminated
    while (numberOfThreadsTerminated < numberOfThreads) {
        MARTe::Sleep::Sec(1e-3);
    }
    REPORT_ERROR_STATIC(ErrorManagement::Information, "Value of sharedVariable = %d (should be zero)", sharedVariable);
    delete sleepTimes;
    return 0;
}

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