Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Fix minor typos.

Signaling Events from Interrupt Handlers

Best way to wake multiple threads from interrupt?

> I want to make a character device driver that passes the same data to all tasks that are reading it. It is not so important whether the data is queued or if just latest sample is retrieved. Problem is just how to wake up the waiting threads.

At the most primitive level, a thread can be waiting for a semaphore, a signal, or a message queue (not empty or not full). Then there are higher level level wrappers around these like mutexes, semaphores, poll waits, etc. But under the hood those are the three fundamental wait mechanisms. Any could be used to accomplish what you want.

...

And you basically have gone through the list of wait mechanisms:

Message Queues

> 1) Open a message queue when the device is opened (a new queue for each task) and keep them in a list. Post to a non-blocking endpoint of these queues in the ISR. Read from a blocking endpoint in the device read(). I would need to generate names for the message queues, as there doesn't seem to be anonymous message queues?

...

In this case, your waiting task will block on a call to mq_receive() until a messages message is received. It will then wake up and can process the message. In the interrupt handler, it will call mq_send() when an event of interest occurs which will, in turn, wake up the waiting task.

...

If the interrupt level consumes all of the CONFIG_PREALLOC_MQ_MSGS messages, it will fall back and use the emergency pool of 8 pre-allocated messages. If those are also exhausted, then the message will not be sent and an interrupt is effectively lost.

Semaphores

> 2) Allocate a semaphore per each device open and keep them in a list. Post the semaphores when new data is available in a shared buffer. Read the data inside sched_lock().

If you don't have an architecture that uses message queues, and all of these threads are waiting only for the interrupt event and nothing else, then sigalling signaling semaphores would work fine too. You are basically using semaphores as condition variables in this case so you do have to be careful.

...

sem_setprotocol() is a non-standard NuttX function that should be called immediately after the sem_init(). The effect of this function call is to disable priority inheritance for that specific semaphore. There should then be no priority inheritance operations on this semaphore that is used for signallingsignaling. See Signaling Semaphores and Priority Inheritance for further information.

Since the semaphore is initialized to zero, each time that a thread joins the group of waiting threads, the count is decremented. So a simple loop like this would wake up all waiting threads:

...

The above code snippet is essentially what the NuttX pthread_cond_broadcast() does (see nuttx/sched/pthread_condbroadcast.c). In NuttX condition variables are really just wrappers around semaphores that give them a few new properties. You could even call pthread_cond_broadcast() from an interrupt handler: See http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_signal.htmlImage RemovedImage Added for usage information.

Neither of the above mechanisms are portable uses of these interfaces. However, there is no portable interface for communicating directly with interrupt handlers.

...

NOTE: There is possibility of improper interactions between the semaphore when I it is used for signaling and priority inheritance. In this case, you should disable priority inheritance on the signaling semaphore using sem_setprotocol(SEM_PRIO_NONE). See the signaling semaphore Wiki Page for further information.

Signals

> 3) Store the thread id's in a list when read() is called. Wake up the threads using{{ sigqueue()}}. Read the data from a shared buffer inside sched_lock().

...

The POSIX signal definition includes some support that would make this easier for you. This support is not currently implemented in NuttX. The kill() interface for example (http://pubs.opengroup.org/onlinepubs/009695399/functions/kill.htmlImage RemovedImage Added) supports this behavior:

...

NuttX does not currently support process groups. But that might be a good RTOS extension. If you and others think that would be useful I could probably add the basics of such a feature in a day or so.

poll()

> Is there some better way that I haven't discovered?

The obvious thing that you did not mention is poll(). See http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.htmlImage RemovedImage Added . Since you are writing a device driver, support for the poll() method in your driver seems to be the natural solution. See the drivers/ directory for many examples, drivers/pipes/pipe_common.c for one. Each thread could simply wait on poll(); when the event occurs the driver could then wake up the set of waiters. Under
the Under the hood, this is again just a set of sem_post's. But it is also a very standard mechanism.

In your case, the semantics of poll() might have to be bent just a little. You might have the to bend the meaning of some of the event flags since they are all focused on data I/O events.

...

> > That would be something great! PX4 project has that implemented somehow
> > (in C++), so maybe - if license permits - it could be ported to NuttX in
> > no time?
>
> https://pixhawk.ethz.ch/px4/dev/shared_object_communicationImage RemovedImage Added

I don't know a lot about this, but it might be worth looking into if it matches your need.