Skip to content

sys/tasklet: rename irq_handler to tasklet#12459

Closed
jia200x wants to merge 1 commit intoRIOT-OS:masterfrom
jia200x:pr/irq_handler_rename
Closed

sys/tasklet: rename irq_handler to tasklet#12459
jia200x wants to merge 1 commit intoRIOT-OS:masterfrom
jia200x:pr/irq_handler_rename

Conversation

@jia200x
Copy link
Member

@jia200x jia200x commented Oct 15, 2019

Contribution description

This PR is a replacement of #12420. It renames the irq_handler module (#10555) to tasklet, as described in #12420 (comment)

The reason is because the concept of "tasklets" is common in OSs and scheduling systems, so this way it should be easier to expose this feature in the documentation.

Testing procedure

Use tests/sys_tasklet as following.

make -C tests/sys_tasklet BOARD=...

Issues/PRs references

#10555
Closes #12420

@jia200x jia200x added Impact: minor The PR is small in size and might only require a quick look of a knowledgeable reviewer Area: sys Area: System labels Oct 15, 2019
@jia200x jia200x force-pushed the pr/irq_handler_rename branch 2 times, most recently from 3a6e2c9 to 70ffefd Compare October 15, 2019 12:55
@jia200x jia200x force-pushed the pr/irq_handler_rename branch from 70ffefd to 568938b Compare October 15, 2019 16:02

/**
* @brief Default priority of the interrupt handler thread
* @brief Default priority of the tasklets thread
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be added to the config Doxygen group?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I advocate to not expose this now to a broader audience. While the API of offloading work to another thread seems to fulfill all needs, the configuration of the taskletthread is very likely to be changed in the near future to allow multiple threads, if requested.

*
* Defines the prototype of the function that is registered together
* with an interrupt event and to be called when the interrupt is handled.
* with a taskletand to be called when the tasklet is handled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* with a taskletand to be called when the tasklet is handled.
* with a tasklet and to be called when the tasklet is handled.

* corresponding source has occurred and needs to be handled. Each interrupt
* event can only be pending once.
* Used modules have to define a structure of this type for each tasklet
* used by the modules. Structures of this type are used to put
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's used modules here. Also the sentence reads a bit odd.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about "Modules using tasklets have to define..."

TASKLET_HANDLER_PRIO,
THREAD_CREATE_WOUT_YIELD |
THREAD_CREATE_STACKTEST,
_tasklet_loop, NULL, "irq_handler");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename thread?

Copy link
Contributor

@kaspar030 kaspar030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jia200x @gschorcht please let's take a step back.

irq_handler is a wrapper around the event module.

it adds:

  1. thread creation function
  2. a check on every event post whether the thread is running
  3. a check whether a handler has already been registered ("pending flag")

It removes the ability to have multiple queues (there's only one highest prio thread running a single queue), and it's API description makes it seem like a good way to defer ISR work to thread context.

1.) could have been added to event

2.) Has to be removed. Doing that check every time (disabling IRQ, checking flag, re-enabling IRQ for an API that's supposed to be mostly used from within ISRs) is useless, as once started, the thread won't ever be stopped. Worse, if the thread has not been started, it will, from ISR context, at less than defined time. (It might happen on the first radio packet received...). Thus that has to be moved into an initialization function.

3.) the pending flag is IMO implicit in event, as event->next is always set when in queue (pending), and NULL when not. If not, this could have been added to event.

Now you're proposing to rename this wrapper around event to "tasklet", which seems to some to be a well-known name for "put function plus argument on a queue, have a loop collect and execute it later".

At that point, when would I use tasklets, when would I use events?

IMO, they're the same. "irq_handler" (or "tasklets") have some ISR related how-to-use documentation, and there's a shared thread for executing the handlers. But that one thread might not be enough has already been discussed. So has adding shared event-threads for the event module.

TL;DR I think we should consolidate to one "event / event loop" API.

@jia200x
Copy link
Member Author

jia200x commented Oct 16, 2019

hi @kaspar030

Note tasklets are not a replacement of event queues, they are used for different things. The irq_handler implementation (renamed here to tasklets) is close to the Tasklets implementation of Linux and the event API closer to the Workqueues of Linux (check the differences here).

Using event loops on irq_handler/tasklets is a design decision, same as using softIRQ for implementing tasklets in Linux.

it adds:
thread creation function
a check on every event post whether the thread is running
a check whether a handler has already been registered ("pending flag")

I'm aware of this, and this was not the case of the original tasklets PR ( #12420 ).
I also understand @gschorcht argument (#12420 (comment)) that it doesn't waste memory the irq_handler_add function is not called, but on the other hand the module wouldn't be included if not used.

I'm with @kaspar030 on this argument, but this can be modified to the behavior of #12420

It removes the ability to have multiple queues (there's only one highest prio thread running a single queue), and it's API description makes it seem like a good way to defer ISR work to thread context.

It doesn't remove the ability to have multiple event queues. The event module is used for that.
We might need an extra "tasklet" thread (TASKLET_HI) if we need preemptiveness for tasklets.

3.) the pending flag is IMO implicit in event, as event->next is always set when in queue (pending), and NULL when not. If not, this could have been added to event.

True. Already solved in #12420, and can be adapted here as well.

Now you're proposing to rename this wrapper around event to "tasklet", which seems to some to be a well-known name for "put function plus argument on a queue, have a loop collect and execute it later".
At that point, when would I use tasklets, when would I use events?

Tasklets should be used for handling low level stuff (PHY/MAC, drivers ISR offloading, internal OS signals, etc). In order to preserve real time constrains, they should aim to be deterministic.

Events should be used for all the rest (interfacing to user API, network stack components, libraries that require timeouts, and in most cases as a replacement of IPC messages).

IMO, they're the same. "irq_handler" (or "tasklets") have some ISR related how-to-use documentation, and there's a shared thread for executing the handlers. But that one thread might not be enough has already been discussed. So has adding shared event-threads for the event module.

The tasklets wrap the event module. But, the event loop relays on a thread given by the user of the module. A tasklet can be scheduled from anywhere without caring about who process the events

I think we should consolidate to one "event / event loop" API.

If that means that the event api adds the possibility to process events from the OS (so adding one or more threads to process this events) then I would be fine. Otherwise, network devices, drivers and different modules might need to add their own thread to process their events (as described by @gschorcht in the motivation of the irq_handler)

@kaspar030
Copy link
Contributor

But, the event loop relays on a thread given by the user of the module. A tasklet can be scheduled from anywhere without caring about who process the events

Yes, because there's no "shared event thread" within the current event module. If there were, instead of
tasklet_schedule(tasklet) you'd use event_post(SHARED_QUEUE, my_event). What I'm saying is, if "event" doesn't have the shared thread(s), adding irq_handler + shared thread, and then argue irq_handler is needed because it has shared threads and event has not, doesn't make sense if they're otherwise identical.

Let's add shared threads to event (as @haukepetersen already has, unfortunately hidden in a branch).

Tasklets should be used for handling low level stuff (PHY/MAC, drivers ISR offloading, internal OS signals, etc). In order to preserve real time constrains, they should aim to be deterministic.

Events should be used for all the rest (interfacing to user API, network stack components, libraries that require timeouts, and in most cases as a replacement of IPC messages).

How do they differ (other than being put in different queues)?

@gschorcht
Copy link
Contributor

2.) Has to be removed. ... Worse, if the thread has not been started, it will, from ISR context, at less than defined time. (It might happen on the first radio packet received...). Thus that has to be moved into an initialization function.

The single interrupt handler thread was initially motivated by the fact that there are a lot of drivers for sensors that are connected via I2C or SPI and the mutex based synchronization mechanism doesn't work in ISRs.

The question at that time was, who should create the handler thread. The different drivers have no knowledge about the handler thread. The application should't need any knowledge about the internals of the drivers and whether they need the handler thread or not. That was the reason why the thread was created when it is used the first time.

A possible solution could be the autoinitialization if irq_handler module is enabled.

@jia200x
Copy link
Member Author

jia200x commented Oct 16, 2019

Yes, because there's no "shared event thread" within the current event module. If there were, instead of
tasklet_schedule(tasklet) you'd use event_post(SHARED_QUEUE, my_event). What I'm saying is, if "event" doesn't have the shared thread(s), adding irq_handler + shared thread, and then argue irq_handler is needed because it has shared threads and event has not, doesn't make sense if they're otherwise identical.

What you describe is exactly the same as a tasklet, but it's explicitly called "tasklet" instead of "shared_queue". The problem here is only about semantics, because the "shared queue" would also need to initialize the thread, put the queue somewhere, etc.

So, this is only a naming convention to me.

How do they differ (other than being put in different queues)?

Semantics. Tasklets are intended to be used for "Bottom Half Processing" or deferring the execution until the kernel finds a safe time to run the task.
Events on the other hand, are a generic mechanism of triggering the execution of a task in the context of the queue.

Tasklets is just an implementation of this "shared event thread" that shares the definition of tasklets of most libraries and OSs.

@kaspar030
Copy link
Contributor

Semantics. Tasklets are intended to be used for "Bottom Half Processing" or deferring the execution until the kernel finds a safe time to run the task.
Events on the other hand, are a generic mechanism of triggering the execution of a task in the context of the queue.

How are they semantically different? Not what the name implies to you what they should be used for, but how a function executed as a tasklet is semantically different from a function executed as an event handler?

IIUC, apart from naming, they are equivalent.

Tasklets is just an implementation of this "shared event thread" that shares the definition of tasklets of most libraries and OSs.

I understand that Linux and Zephyr call their function deferring mechanism "tasklet". Where else?
IMO, especially application developers will look for libevent/libev/libuv style event queues.

@jia200x
Copy link
Member Author

jia200x commented Oct 16, 2019

How are they semantically different? Not what the name implies to you what they should be used for, but how a function executed as a tasklet is semantically different from a function executed as an event handler?
IIUC, apart from naming, they are equivalent.

I meant language semantics. Since tasklets/irq_handler are implemented on top of events, they are the same in terms of functionality.
As said, the tasklet is just the name of this "shared thread" queue.

I understand that Linux and Zephyr call their function deferring mechanism "tasklet". Where else?

OpenThread, Spring Batch, some Google JS libraries, etc.

IMO, especially application developers will look for libevent/libev/libuv style event queues.

Exactly, and they should use events instead of tasklets. As said before, tasklets are intended to be used by the kernel components. I don't see why an end user should offload an ISR or do something really low level if there's a driver that handles that. If someone needs low level operations (even on Linux), then would be working closer to the kernel.

@kaspar030
Copy link
Contributor

Since tasklets/irq_handler are implemented on top of events, they are the same in terms of functionality. As said, the tasklet is just the name of this "shared thread" queue.

Exactly, and they should use events instead of tasklets. As said before, tasklets are intended to be used by the kernel components. I don't see why an end user should offload an ISR or do something really low level if there's a driver that handles that. If someone needs low level operations (even on Linux), then would be working closer to the kernel.

Is your point that because some deferred work is "low level", "Bottom Half" or "ISR offload", it should be done by an API named "tasklet" because that is a commonly used name for that, and if it is "application stuff", it should be done with a differently named API because "tasklets" is not supposed to be used for that?

@haukepetersen
Copy link
Contributor

Hej, I'd like to jump into this discussion, too - as I seem to have missed the discussion when irq_handler was merged...

I have to say that I get a bad stomachache looking at the concept. My major concern is that it destroys all real-time properties for device drivers that use this thread! In the current form it is highly dependent on the selected modules on how they will perform. Example: a none-time-critical sensor on a slow I2C bus can block a highly time critical accelerometer/radio/whatever on a completely independent SPI bus -> bad, right!?

Taking a step back: the general concept of creating a shared thread context that can be used by multiple modules is a very nice thing to do, especially considering the benefits in saved system resources (stack space, ...). So I am in for that. BUT: just having a single thread for various interrupt operations is IMHO not an option (see example above)!

So my proposal would be something like the following:

  • have a more generic module that allows (via compile time configuration) to spawn any number of event handler threads with configurable priorities.
  • allow to configure (at compile time) which handler thread to actually use for each driver/module that wants to use one

So in a default configuration, I could imagine one high-prio handler for drivers, and maybe a low-prio handler for logging and similar. So in that case, the approach would not differ much from what is proposed here. BUT: with that approach it is possible to implement more fine grained setups if needed!

@gschorcht
Copy link
Contributor

I have to say that I get a bad stomachache looking at the concept. My major concern is that it destroys all real-time properties for device drivers that use this thread! In the current form it is highly dependent on the selected modules on how they will perform. Example: a none-time-critical sensor on a slow I2C bus can block a highly time critical accelerometer/radio/whatever on a completely independent SPI bus -> bad, right!?

Yes I know. The original version of the irq_handler did not use sys/event and at least provided least different queues for events of different priorities so that all events of a high priority sensor could be handled before a low priority sensor event. Not perfect, but better than that what we currently have.

After a long discussion with @maribu and @kaspar030 these queues were removed and the irq_handler was implemented with a single thread on top of sys/event. The argument was that an event of a high priority sensor can't interrupt an ongoing I2C or SPI transmission. Although queues with different priorities can solve the interrupt context problem in a lot of cases, it can't solve it in all cases.

Regarding sensors and I2C and SPI, we would need a separate handler thread for each bus. However, separate handler threads require a lot of resources and can't solve the problem for events which reuire access to the same bus.

Therefore, we (@maribu and me) decided to start with the irq_handler module as it is, knowing that it does not meet all the requirements and the option to extend it later by different priorities and different threads.

This shouldn't be a justification of the approach but merely an explanation of how it came to the irq_handler in the present form.

Taking a step back: the general concept of creating a shared thread context that can be used by multiple modules is a very nice thing to do, especially considering the benefits in saved system resources (stack space, ...). So I am in for that. BUT: just having a single thread for various interrupt operations is IMHO not an option (see example above)!

So my proposal would be something like the following:

  • have a more generic module that allows (via compile time configuration) to spawn any number of event handler threads with configurable priorities.
  • allow to configure (at compile time) which handler thread to actually use for each driver/module that wants to use one

I have thought about such an approach very often. The question was how to tell a driver which handler threads exist and which handler thread to use. The problem described in the example above wouldn't be solved if all drivers use the same thread.

So in a default configuration, I could imagine one high-prio handler for drivers, and maybe a low-prio handler for logging and similar. So in that case, the approach would not differ much from what is proposed here. BUT: with that approach it is possible to implement more fine grained setups if needed!

When we discussed with @jia200x in #12420 to reuse the irq_handler for tasklets, we planned to have the possibility to define the number of handler threads during compile time. This is not part of this PR, but it was planned.

@maribu
Copy link
Member

maribu commented Oct 16, 2019

I have to say that I get a bad stomachache looking at the concept. My major concern is that it destroys all real-time properties for device drivers that use this thread!

Hi, the misconception is that there has to be a single thread to offload to. It is conceptionally impossible to use a single thread for stuff with hard real time requirements and low-priority slow stuff.

@jia200x
Copy link
Member Author

jia200x commented Oct 16, 2019

Is your point that because some deferred work is "low level", "Bottom Half" or "ISR offload", it should be done by an API named "tasklet" because that is a commonly used name for that, and if it is "application stuff", it should be done with a differently named API because "tasklets" is not supposed to be used for that?

Not really. My point is that the event module is a primitive for scheduling events to event queues.
Tasklets/irq_handler is a module implemented on top of the event primitive for scheduling tasks as soon as the OS finds a safe time to run them. It doesn't say anything about the number of threads nor real time constrains.

@bergzand
Copy link
Member

bergzand commented Oct 16, 2019

I have to say that I get a bad stomachache looking at the concept. My major concern is that it destroys all real-time properties for device drivers that use this thread! In the current form it is highly dependent on the selected modules on how they will perform. Example: a none-time-critical sensor on a slow I2C bus can block a highly time critical accelerometer/radio/whatever on a completely independent SPI bus -> bad, right!?

I think this can be extended to any high priority thread getting blocked due to a number of slow and unpredictable I2C bus transaction taking priority. This is the strong point of the current model where interrupts set only a flag and the IRQ itself is handled at the priority of the handling thread (see below). With the current design having priority 0 as a must the timing behaviour for other threads becomes rather unpredictable¸ being dependent on bus usage among other things.

What I'm afraid of here is that a method is made available to schedule relative slow processes (I2C bus exchanges with a potential timeout or clock stretching) on a high priority thread. This to me looks like an easy and not so transparant way to hamper other relative high priority threads such as netif threads. Which could show itself as a non-deterministic RTT spike in a network packet.

So my proposal would be something like the following:

* have a more generic module that allows (via compile time configuration) to spawn any number of event handler threads with configurable priorities.

* allow to configure (at compile time) which handler thread to actually use for each driver/module that wants to use one

To me it would already help if this tasklets idea can be modified to run as a low priority thread where tasks are run eventually.

@gschorcht
Copy link
Contributor

I think we all agree that it would be nice to have something that helps to handle events from different drivers that require the access to exclusive resources in thread context, without having to create a separate thread for each driver.

The question is how to realize and how it should be configured.

@haukepetersen
Copy link
Contributor

@bergzand you put it to the point!

It seems to me we all agree, that real-time properties are a key issue here. And we don't have to argue, that lengthy operations in a high-prio thread will always dry out anything in lower prio tasks -> that is how priority based scheduling, like done in RIOT, is all about...

Now how to chose the actual priorities used for certain tasks is something that is very specific to each and every single application/firmware/project, as it depends on many things, and configuring these priorities so that application requirements are fulfilled is not an easy task. But key in RIOT is, that users/developers are actually able to select and tune priorities for their used modules so they can achieve their goals. So when introducing shared event handlers, I think it is very important to leave that door open to developers, so they still have the power to fine tune their system regarding runtime priorities.

So what I am aiming for is a solution, that can use a simplified (default) configuration, but also allows for fine-grained tuning if needed. So as a default configuration, we might simply introduce a single event handler for high-prio driver related operations (so basically irq_handler). This should actually work fine for many use-cases. And for more elaborate setups there is then the possibility for more fine grained priority tuning.

I have thought about such an approach very often. The question was how to tell a driver which handler threads exist and which handler thread to use.

Its actually not hard to make the used event handler queue configurable for drivers. Simply add a config option to their params struct pointing to the queue of choice.

The problem described in the example above wouldn't be solved if all drivers use the same thread.

Exactly, hence (optionally) multiple threads/queues

It is conceptionally impossible to use a single thread for stuff with hard real time requirements and low-priority slow stuff.

ACK - so hence my problem with the current state of the code :-)

@gschorcht
Copy link
Contributor

So when introducing shared event handlers, I think it is very important to leave that door open to developers, so they still have the power to fine tune their system regarding runtime priorities.

Just for clarification, the priority of the irq_handler is configurable at the compile time.

@bergzand
Copy link
Member

Just for clarification, the priority of the irq_handler is configurable at the compile time.

Technically yes, but if the comment here is to be trusted there isn't that much to configure.

@haukepetersen
Copy link
Contributor

I think we all agree that it would be nice to have something that helps to handle events from different drivers that require the access to exclusive resources in thread context, without having to create a separate thread for each driver.

Absolutely.

The question is how to realize and how it should be configured.

In the core I think it can be pretty straight forward by running a number of event handler threads during system initialization, each simply running event_loop(). The configuration for this module could look similar to all the x_params.h constructs we use for drivers:

typedef struct {
    char priority;
    const char *name; // make this possibly optional
} abceventrunner_params_t;

static const abceventrunner_params_t abceventrunner_params[] =
{
    { 1, "irq_handler_high" },
    { 4, "irq_handler_med" },
    { (THREAD_PRIORITY_IDLE - 1), "run_me_if_you_can" },
}

Initialization is trivial, simply allocate ARRAY_SIZE(abceventrunner_params) stacks, and loop thread_create() over these... The thread function is identical for all of them, a one-liner calling event_loop() -> voila, a configurable event handler setup with user definable priorities.

I started to play with something like this a time ago: https://github.com/haukepetersen/RIOT/tree/add_eventhandlerthread, though never came to integrate multiple queues...

Now for drivers we just need to add some syntactic sugar so we can directly tell them which queue to use.

@haukepetersen
Copy link
Contributor

Just for clarification, the priority of the irq_handler is configurable at the compile time.

Yes, but only collectively without any option for differentiation (slow dev A vs picky dev B...)

@haukepetersen
Copy link
Contributor

My original intention was because tasklet and event are not exactly the same

as was shown: from a technical perspective they are :-)

Tasklets can e.g run in several cores

Lets not worry about multi-core systems (for now) -> these have so many implications on all the core modules (msg, mutex, ...), that event threads are the least concern...

That's all I can say. If all here prefer to extend the event module I'm fine and we can close this PR.

+1 for going the event module path from my side. But how about to rework this PR in that direction instead of closing it?!

@kaspar030
Copy link
Contributor

Tasklets can e.g run in several cores (if we support at some point cpu's like LPC4337, which I'm aware we are way too far from that anyway), and ensure that the tasks is ran as soon as possible in safe OS context.

Once we have multiple cores, starting multiple handler threads and changing event to use a mutex instead of thread_flags for notification would solve that. Once we have actual multicore support....

@jia200x
Copy link
Member Author

jia200x commented Oct 17, 2019

note 1: lets not worry about multi-core systems for now -> there are huge number of implications for all core modules with that, and the event handler threads are the least pressing...

Yes, I'm aware of that. I was only referring on why tasklets differ from event queues.

Once we have multiple cores, starting multiple handler threads and changing event to use a mutex instead of thread_flags for notification would solve that. Once we have actual multicore support....

That's exactly the point why I was arguing for tasklets/irq_handler/whatever instead of extending the event module. You can change the implementation of the tasklets mechanism to semaphores, mutex, whatever at any time.

What's the technical reason behind extending the API of event instead of having it's own module, considering the implementation of the tasklet module using event is one of many ways to implement it?

@jia200x
Copy link
Member Author

jia200x commented Oct 17, 2019

+1 for going the event module path from my side. But how about to rework this PR in that direction instead of closing it?!

I meant, if it's decided to go with the extension of the event module, it makes no sense to continue here if there are already 2 PoC PRs

@kaspar030
Copy link
Contributor

What's the technical reason behind extending the API of event instead of having it's own module, considering the implementation of the tasklet module using event is one of many ways to implement it?

The only extension that we need for now is the creation of dedicated handler threads. Otherwise, the event API is already a "tasklet" implementation that is already there. What would be the technical reason to create a new module, if an existing one provides all the functionality needed?

@kaspar030
Copy link
Contributor

That's exactly the point why I was arguing for tasklets/irq_handler/whatever instead of extending the event module. You can change the implementation of the tasklets mechanism to semaphores, mutex, whatever at any time.

events are tasklets. We can change the event implementation to semaphores, mutex, whatever at any time...

@jia200x
Copy link
Member Author

jia200x commented Oct 17, 2019

events are tasklets. We can change the event implementation to semaphores, mutex, whatever at any time...

This is the root of the problem. Tasklets and events are not equivalent.

  1. Tasklets run ASAP as the OS finds a safe time and it doesn't matter which thread/core run them.
  2. Tasklets may have priorities, but they still stick to point 1.

The low priority tasks introduced in #12480 and #12474 are not considered tasklets!

I'm aware tasklets can be implemented using events, but I insist they are not the same!

@jia200x
Copy link
Member Author

jia200x commented Oct 17, 2019

Tasklets and events are not equivalent.

On @haukepetersen comment above, yes. This PR and the current irq_handler on master don't reflect what I wrote above. But that's the aim of the tasklet concept.

@kaspar030
Copy link
Contributor

This is the root of the problem. Tasklets and events are not equivalent.

I think you're quite fixed on what tasklets are doing in Linux (which is quite a different environment).

1. Tasklets run ASAP as the OS finds a safe time and it doesn't matter which thread/core run them.

On Linux, Tasklets are always run on the CPU on which they were scheduled.
Same on RIOT when using events.
Depending on the priority of the worker thread, on RIOT, the event handler will be run ASAP as well, unless another event is currently being handled by that thread. Same as Tasklets on Linux.

2. Tasklets may have priorities, but they still stick to point 1.

So do events when using multiple handler threads.

@jia200x
Copy link
Member Author

jia200x commented Oct 17, 2019

Ok, it was not the intention to make that a big discussion because of naming conventions or where to implement a module. I gave my reasons to implement it on top of the event loop and not next to because of maintainability. I don't want to stall the discussion neither.

If I'm the only one aiming for a tasklet module, then I respect the rough consensus approach, drop this PR and continue with #12474.

@maribu
Copy link
Member

maribu commented Oct 17, 2019

@gschorcht: Would you be fine if I open a PR targeting the release candidate that removes irq_handler for now? As the API is under heavy discussion, I think it would better to not get the feature to a broader audience yet. Otherwise we likely have to maintain two APIs.

@gschorcht
Copy link
Contributor

@gschorcht: Would you be fine if I open a PR targeting the release candidate that removes irq_handler for now? As the API is under heavy discussion, I think it would better to not get the feature to a broader audience yet. Otherwise we likely have to maintain two APIs.

Of course. It seems that we an agreement.

@bergzand
Copy link
Member

I'm missing a bit of background information here. Is there an example of a sensor/module that requires processing ASAP (and thus where a low priority queue doesn't work) after the interrupt but where it can't be done in the IRQ. In other words, what's the use case of the high priority handler?

@gschorcht
Copy link
Contributor

gschorcht commented Oct 17, 2019

I'm missing a bit of background information here. Is there an example of a sensor/module that requires processing ASAP (and thus where a low priority queue doesn't work) after the interrupt but where it can't be done in the IRQ. In other words, what's the use case of the high priority handler?

Each sensor that requires access to SPI or I2C after the sensor triggered an interrupt, for example when exhausting a defined threshold. Another example are I2C GPIO extenders that indicate the change of an input by an interrupt. The access to SPI and I2C is only possible in thread context.

@bergzand
Copy link
Member

Each sensor that requires access to SPI or I2C after the sensor triggered an interrupt, for example when exhausting a defined threshold.

Maybe I'm missing the point here, but why does the average sensor require a high priority thread to handle a threshold exceeded notification?

Another example are I2C GPIO extenders that indicate the change of an input by an interrupt. The access to SPI and I2C is only possible in thread context.

This is IMHO the only example I've seen so far having a hard requirement on the concept discussed here. Just to have the option out in the open here: it is perfectly valid to decide not to support interrupt for these GPIO expander devices and only support GPIO interrupts on the MCU built-in GPIO.

@bergzand
Copy link
Member

bergzand commented Nov 14, 2019

On a more constructive note, is it okay with people to as a first step create the concept discussed here as a low priority thread (THREAD_PRIORITY_IDLE - 1 or THREAD_PRIORITY_MAIN - 1) by default first. This gives us the option to validate the API and ideas discussed here in an unobtrusive way for the rest of the ecosystem. While it might not be suitable for the GPIO expander use case, I think it would still be suitable for sensor notification handling.

We can always extend to multiple handler threads and/or multiple priorities later if deemed necessary.

Edit: added numbers for low priority.

@maribu
Copy link
Member

maribu commented Nov 14, 2019

@kaspar030: Are you willing to complete your proof of concept PR?

@gschorcht
Copy link
Contributor

Each sensor that requires access to SPI or I2C after the sensor triggered an interrupt, for example when exhausting a defined threshold.

Maybe I'm missing the point here, but why does the average sensor require a high priority thread to handle a threshold exceeded notification?

It depends on the sensor. If it is just a temperature sensor, exceeding a threshold might not to be important. But what about a accelerometer in security critical applications? The system has to react in milliseconds.

@bergzand
Copy link
Member

Each sensor that requires access to SPI or I2C after the sensor triggered an interrupt, for example when exhausting a defined threshold.

Maybe I'm missing the point here, but why does the average sensor require a high priority thread to handle a threshold exceeded notification?

It depends on the sensor. If it is just a temperature sensor, exceeding a threshold might not to be important. But what about a accelerometer in security critical applications? The system has to react in milliseconds.

I wouldn't call an accelerometer in a security critical application the average sensor. Doesn't mean that your example is invalid, but I don't think it is the common case.

@maribu
Copy link
Member

maribu commented Nov 14, 2019

I think we have concluded that we let the user configure the priority levels, as arguing about priorities without the context of the intended application makes no sense, right?

But I agree with @bergzand that in absense of any user provided priority assignment low priority is reasonable default (maybe just higher than the main thread).

@gschorcht
Copy link
Contributor

low priority is reasonable default (maybe just higher than the main thread).

This is exactly what I already proposed in #12474 (comment) to avoid that drivers use the high priority thread to ensure to be handled before the main thread.

@gschorcht
Copy link
Contributor

gschorcht commented Nov 14, 2019

While it might not be suitable for the GPIO expander use case, I think it would still be suitable for sensor notification handling.

@bergzand By the way, the need for a single handler thread came up when I wrote the driver for PFC857x in PR #10430. Therefore, the handler priority was originally that high.

@jia200x
Copy link
Member Author

jia200x commented Nov 15, 2019

I think we have concluded that we let the user configure the priority levels, as arguing about priorities without the context of the intended application makes no sense, right?
But I agree with @bergzand that in absense of any user provided priority assignment low priority is reasonable default (maybe just higher than the main thread).

I think this went a little bit out of the context of my original motivation, although this approach could be useful for other things too.

My original motivation was to have an event queue with a "high enough" priority, so the OS can handle transceiver ISR and MAC layer logic. So, a low priority thread wouldn't work for my use case.

If low level priorities are useful for notifications, I think #12474 or #12480 are good candidates for both use cases

@gschorcht
Copy link
Contributor

My original motivation was to have an event queue with a "high enough" priority, so the OS can handle transceiver ISR and MAC layer logic. So, a low priority thread wouldn't work for my use case.

That's why PR #12474 defines a high priority and a low priority thread. That should cover both cases. IMHO, the question is how low should the low priority be, less than the main thread or less than the idle thread.

@kaspar030
Copy link
Contributor

@kaspar030: Are you willing to complete your proof of concept PR?

Sure!

@maribu
Copy link
Member

maribu commented Nov 15, 2019

I think this went a little bit out of the context of my original motivation, although this approach could be useful for other things too.

How about this: We use something like EVENT_HANDLER_PRIO_DEFAULT and EVENT_HANDLER_PRIO_DEFAULT_HIGH for the default priors in the drivers, but enable the use to overwrite the priority of each event source individually. And we also also the user to overwrite whatever value those two default priorities have. That way we can provide a default that will work with the most common use cases out of the box.

I think it would also be a nice implementation detail, if the number of event handler threads could be chosen regardless of the actually used priorities. E.g. if only one event handler thread is enabled, that thread should just handle all events regardless of their priorities. The more threads are actually created, the more priority levels start to actually show different behavior.

@jia200x
Copy link
Member Author

jia200x commented Nov 15, 2019

How about this: We use something like EVENT_HANDLER_PRIO_DEFAULT and EVENT_HANDLER_PRIO_DEFAULT_HIGH for the default priors in the drivers, but enable the use to overwrite the priority of each event source individually. And we also also the user to overwrite whatever value those two default priorities have. That way we can provide a default that will work with the most common use cases out of the box.
I think it would also be a nice implementation detail, if the number of event handler threads could be chosen regardless of the actually used priorities. E.g. if only one event handler thread is enabled, that thread should just handle all events regardless of their priorities. The more threads are actually created, the more priority levels start to actually show different behavior.

That would be nice.
Then as proposed in #12480 (comment) it would be possible to have default priorities for each module. So we can not only configure in which queue a module should have its function processed, but also what's the priority of that queue.

@jia200x
Copy link
Member Author

jia200x commented Mar 12, 2020

I guess we can close this one since #12474 got merged.

@jia200x jia200x closed this Mar 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: sys Area: System Impact: minor The PR is small in size and might only require a quick look of a knowledgeable reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants