Problems with mbed rtos thread synchronization

Hello all,

I want to synchronize two threads on an Arduino GIGA but after realizing that mutexes don't work, I took a look into the code and found out that mutexes are shortcut, basically doing nothing :hot_face:. Semaphores don't work as well. So what mechanism does the mbed rtos offer for basic synchronization between threads or is there any other easy way to achieve this? The mutexes from the C++ stl don't work either.

best regards
Herbert

Hi @herbschi From what I've read (no personal experience), ConditionVariable class seems to meet your needs (if it works on a GIGA). Mbed OS Reference | RTOS

This is interesting. Tell us more. Do you mean osMutexAcquire is a NOP on the Giga mbed build? Can you give us an mbed-os GitHub link?

As an aside, you can use the HSEM (hardware semaphore) support as well to synchronise. Obv you would not want to spin hard on it but it also has the nice property of working across cores.

I ask because I’m curious as I’ve started using this platform, not because I’m going to fix anything. :slight_smile:

Thanks for the idea. Unfortunately this will not work for my example. I have (somehow) simulated mutexes with event flags. Not perfect, but works for my purpose.

Look here (scroll down to the implementation part, starting at line 173):
Mbed OS Reference | Mutex.h Source File

I have (somehow) simulated mutexes with event flags. Not perfect, but works for my purpose. :face_with_raised_eyebrow:

I will investigate into the other mechanisms (semaphores, queues, etc.) later and keep the thread informed.

regards
Herbert

are you saying that MBED_CONF_RTOS_PRESENT is not defined for your install?

It’s defined in the conf for the giga: ArduinoCore-mbed/variants/GIGA/mbed_config.h at main · arduino/ArduinoCore-mbed · GitHub. See line 324.

That being said you have to include Arduino.h or mbed_config.h before including Mutex.h if you don’t then things go badly wrong.

No, other RTOS components like event flags work fine (going now to test queues). The working code for mutex is simply eliminated, leaving only empty methods which return without doing anything. Look at the link in my previous post: Mbed OS Reference | Mutex.h Source File.

regards
Herbert

Those stub methods are wrapped in #if !MBED_CONF_RTOS_PRESENT. This should be set on the Giga. If these stub methods are being executed then MBED_CONF_RTOS_PRESENT is not present which will be because mbed_config.h or Arduino.h is not being included or for some reason your install of the mbed Arduino core is incorrectly configured.

If you look at the Arm mbed GitHub repo you'll see the source file with the implementations in. See mbed-os/rtos/source/Mutex.cpp at 036ee2c6634b1fcccae73c00a1cb896281b3b7be · ARMmbed/mbed-os · GitHub. You will see the implementation is #ifdef'd out in the opposite way.

It's worth noting that EventFlags has the same construct:

#if MBED_CONF_RTOS_PRESENT
osEventFlagsId_t _id;
mbed_rtos_storage_event_flags_t _obj_mem;
#else
uint32_t _flags;
#endif

but has a fallback mechanism for when MBED_CONF_RTOS_PRESENT is not set to 1 (which it should be for the arduino mbed core build of mbed.

and to make sure I was correct I added the following garbage to Mutex.h in my install:

#if !MBED_CONF_RTOS_PRESENT


wdqw[jdwqeijfhoiqweh
s
]
inline Mutex::Mutex()
{
}

and everything compiles because the stubs aren't built for me because MBED_CONF_RTOS_PRESENT is defined and is 1 in my setup. This is why I asked you about your setup and pointed you to the mbed_config.h for the mbed arduino core - assuming that's what you were using.

You might wish to verify this quickly by, where the stubs are defined in your Mutex.h, adding #error "NO RTOS" inside the #if. If this fires inside Mutex.h in your app then you either have i) an mbed_config.h with RTOS disabled (as per above comment) or ii) Arduino.h or mbed_config.h is not being included before Mutex.h.

@herbschi , if it helps I have tested Mutex's on my Giga running the arduino mbed core and they work in my setup.

Here's my code (running in my test harness on the Giga - hence the SCENARIO boilerplate):

#include <Arduino.h>
#include <Thread.h>

#include "doctest/doc-test-harness.h" // Just the test harness
#include "logger/logger.h" // just the logger

static rtos::Mutex m;

static void ping() {
  while (true) {
    m.lock();
    DBC_INFO(subsystem::test, "ping");
    delay(1000);
    m.unlock();
  }
}

static void pong() {
  while (true) {
    m.lock();
    DBC_INFO(subsystem::test, "pong");
    delay(10000);
    m.unlock();
  }
}


SCENARIO("giga-threads") {
  rtos::Thread t1;
  rtos::Thread t2;

  t1.start(ping);
  delay(5000);
  t2.start(pong);

  t1.join();
  t2.join();

}

What I expect to see is ping messages every second for 5 seconds then when t2 is started it'll lock for 10s and I will see the ping stop for 10 seconds.

You can see this in the results below:

INFO:		<--- TEST LOG :dt=2024-10-01 15:46:27,:l=I,:c=M7,:s=test,ping
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:28,:l=I,:c=M7,:s=test,ping
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:29,:l=I,:c=M7,:s=test,ping
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:30,:l=I,:c=M7,:s=test,ping
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:31,:l=I,:c=M7,:s=test,ping
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:32,:l=I,:c=M7,:s=test,pong <--- see the 10s after this
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:42,:l=I,:c=M7,:s=test,ping
INFO:		<--- TEST LOG :dt=2024-10-01 15:46:43,:l=I,:c=M7,:s=test,pong

I'm happy to run a trivial test case for you if you post one here.

I just included this in my code and it compiles fine:

I include RPC.h which via RPC_client.h includes Arduino.h. Also Queues just work perfect.

:thinking:

So you’re saying that if you explicitly include Arduino.h before Mutex.h the Mutex doesn’t work like it does in my example?

Can you create a simple test case like mine?
What I mean though with that error preprocessor statement was to put it in mutex.h where the stubs were.z

One issue with mbed is it can be inconsistent with how mbed_config is included. You might find Queue is using functionality that’s always on or gets the RTOS define via some include. You have to trace this stuff through the source code to be sure. I took a Quick Look actually and found that Queue ends up using this for locking:

void equeue_mutex_lock(equeue_mutex_t *m)
{
core_util_critical_section_enter();
}

Which is different from Mutex's implementation.

So you can’t draw conclusions about one working an another not without going down the source code rabbit hole :slight_smile:

Critical section support is (AFAIK) always on and working.

I have to apologize but currently I am too far in my project already to spend much time experimenting with mutex. I do have a working solution now with flags and queues but in the next project I will take up the issue again.

Anyways, thanks for you tips and hints
Herbert