when can we not use task notificatin as binary semaphore

jigar29 wrote on Sunday, June 23, 2019:

Hello,
I recently came to know about the task notification. It is a very nice feature. However it confuses me with the binary semaphore. What is different in binary semaphore that the task notification can not do? If none, why do we even have binary semaphore if we have a mechanism (task notification)which is 45% faster? If they are same, why we implemented a new mechanism (task notification)? instead we could have changed the binary semaphore implementation with the task notification implementation to make binary semaphore 45% faster. Is there any difference between these two, use case wise? I do not understand. Please advice.

Thanks in advance

1 Like

heinbali01 wrote on Sunday, June 23, 2019:

Richard Barry would ( and maybe will ) give you a more accurate answer, but here is my short answer: when you use a binary semaphore just to wake-up a task, it is definitely more efficient to use task notification in stead. Only one task can wait for its notification, and that makes it simpler.

When semaphores are used like mutexes, they can not be replaced by task notification. Many tasks can wait for the same semaphore, and only the highest-priority task will get it. When the priorities of two tasks are equal, the semaphore will be taken by the task that has waited the longest.

richarddamon wrote on Sunday, June 23, 2019:

One key difference is that any task can wait for an take a binary semaphore, but only the specific task can wait for a specific notification, so a direct to task notification can’t replace a semaphore if you don’t know what task is going to want to wait for it, or perhaps you need to publish somewhere what task is waiting for the event.

A second difference is that the direct to task notification can only a single counting semaphore or a limited number of binary semaphores (and the programmer needs to keep careful track of which bit represents which notification). You can have an effectively unlimted number of semaphores, (the only limit is memory space).

Direct to Task notification came significantly later in the development of FreeRTOS, for the cases where it does work, it likely does work better (task/interrupt A wants to let task B proceed from its wait) and probably should be used in new code (it may not be worth it to change existing working code). There are still a number of cases where you still need the semaphore, or the semaphore is ‘better’.

rtel wrote on Sunday, June 23, 2019:

Yes to the above, and to elaborate.

If you look at the evolution of FreeRTOS, from its roots 15 years ago,
the original design was intended to be as small as possible. As such
semaphores were originally implemented as macros on top of queues, so
the functionality was achieved without adding any code size - but at the
expense of semaphores being somewhat larger than many people expect.
This became a problem when folks started writing FreeRTOS specific
peripheral drivers and wanted to use semaphores per driver port (serial,
IO or whatever) as a blocking/signalling mechanism. At that time we
considered, and even prototyped specific semaphore objects. However,
when looking at data on how people were using semaphores it became
apparent that the majority of use cases always had the same sender and
the same receiver - so rather than implement a new generic semaphore
object that (like the FreeRTOS semaphores) contained logic that enabled
multiple senders and multiple receivers, we implemented a primitive we
called task notifications that was optimised for the most common use
case. Therefore task notifications are smaller as they don’t need to
worry about prioritiesed lists of senders or receivers. Task
notifications are faster as there is no separate storage object - unlike
semaphores that write to an object then have another task read from that
object the write is directly to the receiving task (as an aside, as code
size is less important now, the queue primitive can be updated to do the
same thing if there is already a task waiting to receive when data is
written to a queue).

Now you have a choice of a fully features primitive with lots of
flexibility, or smaller and faster primitives with a use case that is
restricted to the most common use case. Peripheral drivers now don’t
need to contain a semaphore object, but instead just remember the handle
of the task they must send a notification to when the driver wants to
unblock a task.

jigar29 wrote on Monday, June 24, 2019:

Thanks everyone. It really helped.