Thread Communication

  • Mutex
  • Semaphore
  • Signal
  • Queue
  • MemoryPool
  • Mail

Mutex

A Mutex is used to synchronize the execution of threads: for example to protect the access to a shared resource. A mutex is a lockable object that is designed to signal when critical sections of code need exclusive access, preventing other threads with the same protection from executing concurrently and access the same resource.

!!! image

Note: The Mutex methods cannot be called from interrupt service routines (ISR).

Mutex() Constructor

osStatus lock( uint32_t millisec=osWaitForever) Wait until a mutex becomes available Parameter: millisec, timeout value, default: osWaitForever Returns the status code that indicates the execution status of the function (see Status and Error Codes)

osStatus unlock(void) Unlock the mutex that has previously been locked by the same thread Returns the status code that indicates the execution status of the function

3 Thread need to access the serial port at the same time The Mutex prevents multiple access from different threads If the mutex is locked, the thread trying to access it, will be set to the “Waiting state”, and is set back to the “Running” state when the mutex is unlock by the other thread

#include "mbed.h"
#include "rtos.h"

Mutex stdio_mutex; 

void notify(const char* name, int state) {
    stdio_mutex.lock();
    printf("%s: %d\n\r", name, state);
    stdio_mutex.unlock();
}

void test_thread(void const *args) {
    while (true) {
        notify((const char*)args, 0); 
        Thread::wait(1000);
        notify((const char*)args, 1); 
        Thread::wait(1000);
    }
}

int main() {
    Thread thread1(test_thread, (void *) "Task 1");
    Thread thread2(test_thread, (void *) "Task 2");
    Thread thread3(test_thread, (void *) "Task 3");

    Thread::wait(osWaitForever);
}

!!! image

!!! image

C standard library mutexes The ARM C standard library has already mutexes in place to protect the access to stdio, therefore on the M3 mbed the above example is not necessary. On the contrary, ARM microlib (used on the M0 mbed) does not provide default stdio mutexes making the above example a necessity.

stdio (printf, putc, getc, etc), malloc & new in ISR Because of the mutexes in the ARM C standard library you cannot use stdio (printf, putc, getc, etc), malloc and new in ISR!

Semaphore

A Semaphore is particularly useful to manage thread access to a pool of shared resources of a certain type.

!!! figuur

Semaphore(int32_t count) Constructor Parameter: count, number of available resources

osStatus release(void) Release a semaphore resource that was obtain with Semaphore::wait Returns the status code that indicates the execution status of the function

int32_t wait(uint32_t millisec=osWaitForever) Wait until a semaphore resource becomes available Parameter: millisec, timeout value, default: osWaitForever Returns the number of available tokens, or -1 in case of incorrect parameters

#include "mbed.h"
#include "rtos.h"

Semaphore two_slots(2);

void test_thread(void const *name) {
    while (true) {
        two_slots.wait();
        printf("%s\n\r", (const char*)name);
        Thread::wait(1000);
        two_slots.release();
    }
}

int main (void) {
    Thread thread1(task_thread, (void *) "Task 1 is running\r\n");
    Thread thread2(task_thread, (void *) "Task 2 is running\r\n");
    Thread thread3(task_thread, (void *) "Task 3 is running\r\n");

    Thread::wait(osWaitForever);
}

Semaphore types:

  • Binary semaphore
  • Counting semaphore

Signal

Each Thread can be notified and wait for signals:

!!! image?

osEvent signal_wait( int32_t signals, uint32_t millisec=osWaitForever) Wait for one or more signal flags to become signaled for the current RUNNING thread Parameters: signals: wait until all specified signal flags set or 0 for any single signal flag millisec: timeout value, default: osWaitForever Returns the event flag information or error code

int32_t signal_set (int32_t signals) Set the specified signal flags of an active thread Parameter: signals, specifies the signal flags of the thread that should be set Returns previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters

#include "mbed.h"
#include "rtos.h"

DigitalOut led(LED1);

void led_thread(void const *argument) {
    while (true) {
        // Signal flags that are reported as event are automatically cleared.
        Thread::signal_wait(0x1);
        led = !led;
    }
}

int main (void) {
    Thread thread(led_thread);

    while (true) {
        Thread::wait(1000);
        thread.signal_set(0x1);
    }
}

Queue

A Queue allows you to queue pointers to data from producers threads to consumers threads:

!!! image

osStatus put( T* data, uint32_t millisec=0) Put a message in a queue Parameters: data: message pointer millisec: timeout value or 0 in case of no time-out, default: 0 Returns the status code that indicates the execution status of the function (see Status and Error Codes)

osEvent get(uint32_t millisec=osWaitForever) Get a message or Wait for a message from a queue Returns event information that includes the message and the status code

MemoryPool

The MemoryPool class is used to define and manage fixed-size memory pools:

!!! image??

T* alloc(void) Allocate a memory block of type T from a memory pool Returns the address of the allocated memory block, or NULL in case of no memory available

T* calloc(void) Allocate a memory block of type T from a memory pool and set memory block to zero Returns the address of the allocated memory block, or NULL in case of no memory available

osStatus free(T* block) Return an allocated memory block back to a specific memory pool Parameter: address, the address of the allocated memoryblock that is returned to the memory pool Returns the status code that indicates the execution status of the function

#include "mbed.h"
#include "rtos.h"

typedef struct {
    float    voltage;   /* AD result of measured voltage */
    float    current;   /* AD result of measured current */
    uint32_t counter;   /* A counter value               */
} message_t;

MemoryPool<message_t, 16> mpool;
Queue<message_t, 16> queue;

/* Send Thread */
void send_thread (void const *args) {
    uint32_t i = 0;
    while (true) {
        i++; // fake data update
        message_t *message = mpool.alloc();
        message->voltage = (i * 0.1) * 33; 
        message->current = (i * 0.1) * 11;
        message->counter = i;
        queue.put(message);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(send_thread);

    while (true) {
        osEvent evt = queue.get();
        if (evt.status == osEventMessage) {
            message_t *message = (message_t*)evt.value.p;
            printf("\nVoltage: %.2f V\n\r"   , message->voltage);
            printf("Current: %.2f A\n\r"     , message->current);
            printf("Number of cycles: %u\n\r", message->counter);

            mpool.free(message);
        }
    }
}

Mail

A Mail works like a queue with the added benefit of providing a memory pool for allocating messages (not only pointers).

!!! image

T* alloc(uint32_t millisec = 0) Allocate a memory block of type T Parameter: millisec: timeout value or 0 in case of no time-out, default: 0 Returns the address of the allocated memory block, or NULL in case of no memory available

T* calloc(uint32_t millisec = 0) Allocate a memory block of type T and set memory block to zero Parameter: millisec: timeout value or 0 in case of no time-out, default: 0 Returns the address of the allocated memory block, or NULL in case of no memory available

osStatus free(T* mptr) Free a memory block from a mail Parameter: mptr, a pointer to the memory block that was obtained with Mail::get Returns the status code that indicates the execution status of the function

osEvent get(uint32_t millisec = osWaitForever) Get a mail from a queue Parameter: millisec: timeout value or 0 in case of no time-out, default: osWaitForever Returns an event that contains mail information or error code

osStatus put(T* mptr) Put a mail in the queue Parameter: mptr memory block previously allocated with Mail::alloc or Mail::calloc Returns the status code that indicates the execution status of the function

#include "mbed.h"
#include "rtos.h"

/* Mail */
typedef struct {
  float    voltage; /* AD result of measured voltage */
  float    current; /* AD result of measured current */
  uint32_t counter; /* A counter value               */
} mail_t;

Mail<mail_t, 16> mail_box;

void send_thread (void const *args) {
    uint32_t i = 0;
    while (true) {
        i++; // fake data update
        mail_t *mail = mail_box.alloc();
        mail->voltage = (i * 0.1) * 33; 
        mail->current = (i * 0.1) * 11;
        mail->counter = i;
        mail_box.put(mail);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(send_thread);

    while (true) {
        osEvent evt = mail_box.get();
        if (evt.status == osEventMail) {
            mail_t *mail = (mail_t*)evt.value.p;
            printf("\nVoltage: %.2f V\n\r"   , mail->voltage);
            printf("Current: %.2f A\n\r"     , mail->current);
            printf("Number of cycles: %u\n\r", mail->counter);

            mail_box.free(mail);
        }
    }
}

Status and Error Codes

The Status and Error Codes section lists all the return values that the CMSIS-RTOS functions will return:

  • osOK: function completed; no event occurred.
  • osEventSignal: function completed; signal event occurred.
  • osEventMessage: function completed; message event occurred.
  • osEventMail: function completed; mail event occurred.
  • osEventTimeout: function completed; timeout occurred.
  • osErrorParameter: parameter error: a mandatory parameter was missing or specified an incorrect object.

  • osErrorResource: resource not available: a specified resource was not available.

  • osErrorTimeoutResource: resource not available within given time: a specified resource was not available within the timeout period.
  • osErrorISR: not allowed in ISR context: the function cannot be called from interrupt service routines.
  • osErrorISRRecursive: function called multiple times from ISR with same object.
  • osErrorPriority: system cannot determine priority or thread has illegal priority.
  • osErrorNoMemory: system is out of memory: it was impossible to allocate or reserve memory for the operation.
  • osErrorValue: value of a parameter is out of range.
  • osErrorOS: unspecified RTOS error: run-time error but no other error message fits.

results matching ""

    No results matching ""