MultiThreading - Using a Queue as a Semaphore


#1

Hello All,

I’m trying to use a semaphore in my application but found the error undefined reference to `os_semaphore_create when trying to build.

os_queue_create works fine.

I noticed there is nothing in hal_dynalib_concurrent.h for semaphore.

Just interested, has this been left out for any reason? Are you able to suggest a way I can impelement it?

adding,

DYNALIB_FN(28, hal_concurrent, os_semaphore_create, int(os_semaphore_t* semaphore, unsigned max_count, unsigned initial_count))
DYNALIB_FN(29, hal_concurrent, os_semaphore_destroy, int(os_semaphore_t semaphore))
DYNALIB_FN(30, hal_concurrent, os_semaphore_give, int(os_semaphore_t semaphore, bool reserved))
DYNALIB_FN(31, hal_concurrent, os_semaphore_take, int(os_semaphore_t semaphore, system_tick_t timeout, bool reserved))

allowed the system to build but it failed when I called the below code at runtime.

os_semaphore_t semaph;
os_semaphore_create(&semaph, 1, 0);

Any help would be greatly appreciated.

Hayden


#2

I had no success implementing semaphores in the firmware and as I’m time poor I implemented it via queues, which is implemented in firmware.

In freeRTOS i see it calls queue_create for the semaphore anyway. It uses a item size of zero and a queue length of 1 to show create a binary semaphore.

os_queue_t semaphore;
os_queue_create(&semaphore, 0, 1, 0);

Then in a Thread use this to block and take

int error = os_queue_take(semaphore, NULL, CONCURRENT_WAIT_FOREVER, 0);

and in another thread I use this to give

int error = os_queue_put(semaphore, NULL, CONCURRENT_WAIT_FOREVER, 0);

I’m still interested as to the reasoning behind not impelmenting semaphores in the build. I’m guessing saving memory played a role when you can effectively implement the same thing using queues but how much extra would have been consumed?

Thanks


#3

@jvanier I know you have commented on the past for Semaphore, do you have any advice here?


#4

Not much to add about this particular topic. We should definitely improve the tools available for product creators that want to implement more complex multi-threading. Right now the Wiring API for Thread is not enough.


#5

Do we have any updates on this? Otherwise I’ll use this approach to my app.

I see some applications of having my app running with SYSTEM_THREAD(ENABLED) while being suspended until commands from remote server or data received from sensor via ISR post a message and unlocks it, avoiding undesired pooling.


#6

So… I’m playing around with a Electron with DeviceOS@1.1.1 and I’m testing the idea of having a simple thread to handle requests to a specific HW (I2C, but right now just toggling a LED).

Since we are currently based on FreeRTOS, I’m trying to use the os_queue_take (abstraction to xQueueReceive) as a way to suspend my task until a producer send a request (msg) to this to be handle.

According to FreeRTOS docs I should be able to accomplish this behavior by having INCLUDE_vTaskSuspend == 1 and the timeout set to portMAX_DELAY.

Using a quick grep I found some places where CONCURRENT_WAIT_FOREVER is defined as INCLUDE_vTaskSuspend and others where it’s “(system_tick_t)-1”, but that should be ok.

Unfortunately as of right now I’m having this a solid white for the RBD LED and the board is basically hanging. With some debugging I figured that having a task with only a while(1){} has the same behavior, (end adding a delay makes is “run” again) so my guess is that my os_queue_take is just returning immediately (I don’t have any producer posting messages atm).

What are the suggestions as the right place to define INCLUDE_vTaskSuspend = 1 and get my task suspending as desired?

P.S.: SYSTEM_THREAD(ENABLED) and SYSTEM_MODE(MANUAL)


#7

SYSTEM_MODE is always “enabled” what matters with that is which mode you have selected.

Do you mean a thread?

Can you provide some code to look at?


#8

Sorry, I meant MANUAL

Yes… But from the FreeRTOS point of view, DeviceOS Threads are FreeRTOS tasks created with “xTaskCreate”

Setup/Loop Code:

Thread/Task Code:


#11

So, looks like my first idea on whats happening was wrong.

if I change my thread/task to:

    while(1)
    {
        digitalWrite(D6, 1);
        os_queue_take(test_queue, &ptr, CONCURRENT_WAIT_FOREVER, nullptr);
        delay(1000); //Small delay just to make sure that LED was turned on and off
        digitalWrite(D6, 0);
    }

My Thread controlled LED turns on and doens’t go off (expected behavior)
But my Loop/Setup controlled LED doesn’t blink at all (unexpected behavior). So I’m getting the thread suspended, but somehow also messing up with the Loop/Setup thread?


#12

This looks correct. You would take from a queue that stores the result at the destination of &ptr and you will block CONCURRENT_WAIT_FOREVER untill a message arrives. Depending on the priority of the task it may run.

If D6 is your LED that doesn’t blink, that’s because you’ve programmed it to go on then off instantly. Move the delay to the last instruction in that loop and you’ll be right.


#13

Figure it out, it was a case of PMAC :laughing:

I was using “os_queue_t *test_queue;” instead of just “os_queue_t test_queue;” and it was probably pointing to somewhere random after os_queue_create, and resulting in my problem.

I didn’t paid attention to @hdekker example and to the fact that os_queue_t was already a *void.

Thanks for your help @hdekker and @ScruffR