3

I have one init task and several worker tasks. At some point the workers must wait for init to complete some setup. I'm trying to do this with a binary semaphore.

When the scheduler starts, all tasks are ready to run. So to guarantee that the workers wait, the semaphore must be "taken by the init task" before the scheduler starts. (Otherwise a worker could get the semaphore before init was even scheduled.)

How do I do that? Or how else should I solve that problem?

not-a-user
  • 4,088
  • 3
  • 21
  • 37

3 Answers3

3

A binary semaphore would work if you only had one worker task. You would create the semaphore as "unavailable" / "taken", and the init task would xSemaphoreGive the semaphore when initialization is done, notifying the single worker task that initialization was complete. However, when you have several tasks, then only one of them will be able to take/acquire the semaphore.

A counting semaphore would work. The semaphore should have a capacity equal to the number of worker tasks, and it should start as empty. As system designer, you know that the init-task is designed to own the underlying resource at startup, so init-task won't have to acquire the semaphore on the first initialization. After initialization, you should call xSemaphoreGive once for each worker. When/if you want to reinitialize, call xSemaphoreTake once for each worker task. For this to work, the workers must periodically "release" the semaphore briefly and then reacquire it.

An Event Group could also be used if you'll only initialize once. Your init-task can use xEventGroupSetBits to indicate that initialization is complete. Your worker tasks can use xEventGroupWaitBits to wait until the init-task is done.

Pinetwig
  • 673
  • 5
  • 13
  • Thanks, I'll have a look at the event bits. But for my initially proposed solution, how to 'create the semaphore as "unavailable" / "taken"'? I can only see `xSemaphoreCreateBinary( void );` and it has no argument to tell it whether it should be initially available or taken. – not-a-user May 23 '18 at 13:39
  • Ah... It seems [binary semaphores](https://freertos.org/xSemaphoreCreateBinary.html) are always created as taken in FreeRTOS. Also, as I mentioned, binary semaphores don't work with multiple worker tasks. – Pinetwig May 23 '18 at 13:42
  • @not-a-user The binary semaphore is created in the empty state and must be given before it can be taken. It is not necessary for the Init task to take the semaphore. The Init task would give the semaphore (without ever taking it) after the necessary initialization is complete. Think of it as a signal. The Init task asserts/sets the signal by giving the semaphore. The other tasks are waiting for the signal by taking the semaphore. But the problem with the binary semaphore is that only one of the waiting tasks can take the binary semaphore at a time. – kkrambo May 23 '18 at 14:19
  • 1
    'problem with the binary semaphore is that only one of the waiting tasks can take the binary semaphore at a time' - that's not a problem. When a waiting thread gets the unit and runs, it posts the unit back to the semaphore for the next thread to acquire :) – Martin James May 23 '18 at 21:02
  • @MartinJames, That could work, but you wouldn't know that all worker tasks have stopped if you need to reinitialize (if that's necessary). But good point! – Pinetwig May 24 '18 at 05:14
2

When you have multiple tasks waiting for a signal or event then a binary semaphore is not the right mechanism. With a binary semaphore, only one task can take the semaphore at a time and only one task will be made ready to run when the binary semaphore is given. I suppose each waiting task could take and then immediately give the semaphore to the next waiting task but that seems strange.

Can you simply make the Init task the highest priority task until it has completed the necessary initialization. The other tasks won't run until initialization is complete by virtue of being lower priority. When initialization is complete you could delete the Init task or maybe lower it's priority.

If priority is not an option then you should probably use an event bit (event flag). For your use case an event bit is a better mechanism than a binary semaphore because multiple waiting tasks can be made ready to run when the Init task signals the event.

kkrambo
  • 6,643
  • 1
  • 17
  • 30
  • 'I suppose each waiting task could take and then immediately give the semaphore to the next waiting task' - that's not strange, it's fine:) – Martin James May 23 '18 at 20:42
2

A binary semaphore would be fine. Initialize it to 0 units.

Start all the other threads and have them wait on the semaphore at their appropriate point and, if they get a unit, immediately post the unit back again before continuing. The all have to wait because the sema has no unit.

Carry on with the initialization as needed, then signal the semaphore. All the waiting threads will then run on, one-by-one as they get the unit and post back again.

Martin James
  • 24,453
  • 3
  • 36
  • 60