Matthew,you described the message passing process you had in mind that the producer allocates the container for the data via malloc() and the consumer would free that container again when the data has been processed via free().Coming up for a special case for each instance when an ISR wants to be the producer of an event is not acceptable, as then the consumer needs to know about all the special cases, and where an event comes from. Even worse, each time a user wants to code a producer that is triggered via a callback to "attachInterrupt()" they need to special code that.At the end of the day, malloc() / free() does not work for that purpose.What you could do is to have an event that is a tuple of { uint32_t eventID, void *data } and then let the user decide what "data" is, and how it's allocated and free, or unused. But in reality that will lead to a queue or ringbuffer type class implementation for each type of data you are passing around. So you need to supply a generic queue implementation the is preemption safe as well (as this is for the ISR producer, which cannot lock or block). In practice that is not any better for the user than having one global queue where data is copied into.Another solution could be to simply give up on passing around data, and just live with some "eventID" being passed around.In any case, if the goal is to have one producer and one consumer, in a classic RTOS this problem is solved by a queue object, where the writer is always non-blocking (ISR, Task), and the reader may be blocking/waiting (Task), or non-blocking (ISR). If the size of the data item is zero, this type of queue simple degenerates to a semaphore. So why reinvent the wheel ?- ThomasOn Tue, Jun 13, 2017 at 1:45 AM, Matthew Ford <Matthe...@forward.com.au> wrote:For Serial I was thinking of having 2 pre-allocated buffers which are never freed. At the end of the message processing the call to "free" just marks the buffer a empty
so receive a char, put in buffer 1 post msg with buffer 1.
Switch to buffer 2 to hold subsequent received chars while waiting for buffer 1 to be processed and 'freed' i.e. marked as empty
When buffer 1 is 'freed' then post msg with buffer 2 and switch to filling buffer 1 etc
On 13/06/2017 2:49 PM, Thomas Roell wrote:
--Ad iii). The implied use of malloc for an event producer poses a problem with system generated events from the ISR level. I'd assume we'd want to have events like "SERIAL_DATA_AVAILABLE". That also means that any kind of queueing operation needs to work without the use of malloc() (no list entries via new(), no queue resizing).
Multiple consumers are typically uses for barriers.
On Mon, Jun 12, 2017 at 8:58 PM, Matthew Ford <Matthe...@forward.com.au> wrote:
It seems to me that most RTOS projects concentrate on scheduling and largely ignore data consistency.
i.e. it is left to the user to work out the locking etc.
I have a couple of suggestions to assist novice users to avoid corrupted data with out the complication of locks, so avoiding deadlocks.
i) Non-peremptive tasks (on a single cpu) ensures data consistency between blocking operations, without locks.
ii) static locals can be used instead of globals in a lot of cases for better data security without locks
iii) message passing which includes data can be used to protect data in a simple manner, from the user's point of view
The idea for passing data via msgs is that the producer calls new() and passes that ptr with the message, BUT does not keep a copy if it.
The consumer ALWAYS frees the pointer on exit.
The consumer should be able to call a support method to find the size of the data since the malloc() management must know this.
Of course being C the user can always shoot themselves in the foot, but it requires extra code, i.e. explicitly saving the new() pointer in a global variable
This should work in many cases.
The exception is where there are multiple consumers.
Question: How many commons use cases are there where there are multiple consumers of complex data i.e. data bigger then int, (which can be carried with the msg)?
micro:bits ManagedStrings https://lancaster-university.github.io/microbit-docs/data-types/string/#managedstring
which are immutable would also be useful.
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+unsubscribe@arduino.cc.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+unsubscribe@arduino.cc.
There are some downsides, and it's fair to list them:
- a event handler cannot use blocking IO, only async IO where later on a event is generated upon completion
- a event handler cannot use "delay()" as this would recursively call the event dispatcher
- a event handler cannot call "yield()" as this would recursively call the event dispatcher
- for complex problems a sequence of event handlers will be needed realizing a state machine ... at that point classic threads may be easier to deal with
I don't agree that these downsides exist from the user's point of
view
delay()/sleep() does not seem to be recursive at all in the micro:bit code. Just puts the method to sleep. Same for yield() == sleep(0)
state machines are not implicit in the basic design but as you say may need to be implemented by the user for complex problems.
Here are the permissible options for event handlers in
micro:bit
Seems to cover most cases and is selectable by user at coding
time per listener
There are four permissible modes for event handlers. These are:
Threading mode | Brief Description |
---|---|
MESSAGE_BUS_LISTENER_IMMEDIATE | Handler is called directly from the code raising the event. Event handler is not permitted to block. |
MESSAGE_BUS_LISTENER_DROP_IF_BUSY | Handler is executed through its own fiber. If another event arrives whilst the previous event is still being processed, the new event will be silently dropped. |
MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY | Handler is executed though its own fiber. If another event arrives, it is queued, and the event handler will immediately be called again once processing is complete. (default) |
MESSAGE_BUS_LISTENER_REENTRANT | Every event is executed in its own fiber. if another event arrives, it is handled concurrently in its own fiber. |
What I want from this multi-tasking API/Library is
i) A non-preemptive task method that may block. This lets me
update globals without using locks and lets me intuitively use
delay() as and when I wish, and to access slow IO from within the
task.
ii) Tasks driven by messages that pass the associated data as an
argument to the task, or within the message instance. (i.e. not
using globals or locks). As with normal method arguments, I don't
what to bothered with memory recovery or disposal of these args
when the task method exits. The library should do what ever is
necessary behind the scenes.
iii) IO that is available in both blocking and async (i.e. send it a message) forms, so I can choose which one suits my task. IO includes Serial, Wire, SPI, external memory, etc.
iv) Don't force me to even know about the existence of locks,
semaphores, synchronization etc just to code a simple blink task
and a task that handles IO. Sure I may need these when I start
doing fancy complex sketches, but I should be able to code most
tasks without explicit access to, or knowledge of, them.
The code example previously posted, below, is still an example of
what I would like to use.
For use on the Arduino UNO, I don't care if I can only run a
couple of tasks and if there is a strict limit to the number of
pending messages. I already have libraries and sketches that
won't run on UNO. That's just life.
The progress in uC development is much faster then the speed at
which we can sort out what to do here and get this api/library
coded.
We should not be too worried about uC memory limitations as they
will quickly disappear.
What we really need to do is devise a multi-tasking coding style
that is simple, straightforward, elegant and as intuitive as
possible for non-programmers to use. Lots of Arduino users are
non-programmers.
It think this makes my position clear and I am bowing out of this
discussion now.
I look forward to those "on high" making a decision
on the way forward.
--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
What I want from this multi-tasking API/Library is
i) A non-preemptive task method that may block. This lets me update globals without using locks and lets me intuitively use delay() as and when I wish, and to access slow IO from within the task.
ii) Tasks driven by messages that pass the associated data as an argument to the task, or within the message instance. (i.e. not using globals or locks). As with normal method arguments, I don't what to bothered with memory recovery or disposal of these args when the task method exits. The library should do what ever is necessary behind the scenes.
iii) IO that is available in both blocking and async (i.e. send it a message) forms, so I can choose which one suits my task. IO includes Serial, Wire, SPI, external memory, etc.
iv) Don't force me to even know about the existence of locks, semaphores, synchronization etc just to code a simple blink task and a task that handles IO. Sure I may need these when I start doing fancy complex sketches, but I should be able to code most tasks without explicit access to, or knowledge of, them.
The code example previously posted, below, is still an example of what I would like to use.
For use on the Arduino UNO, I don't care if I can only run a couple of tasks and if there is a strict limit to the number of pending messages. I already have libraries and sketches that won't run on UNO. That's just life.
The progress in uC development is much faster then the speed at which we can sort out what to do here and get this api/library coded.
We should not be too worried about uC memory limitations as they will quickly disappear.
What we really need to do is devise a multi-tasking coding style that is simple, straightforward, elegant and as intuitive as possible for non-programmers to use. Lots of Arduino users are non-programmers.
It think this makes my position clear and I am bowing out of this discussion now.
+1
I like the idea that libraries can be “fixed” w/o the user having to know anything, it just works better.
--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
Ad John said, an OS/sheduler male no sense or is just impractical for low end micros; so can we first put a line on what is supposed to be supported? And how will low power stuff handled? That is really important to understand the possible solution. (I guess would be nice to keep full support of the atmega 328)
Also nobody is talking about the issue with data synchronization and starvation between task, with is one of the most common error I see around from people using thread without knowing what they are doing.
if we ditch low end MCU, then my guess is to use an existing rtos like freertos or chibios or whatever is the most complete (and open) out there, so both project can help each other.
Someone proposed an event driven approach, this is extremely interesting in my opinion as we approach cost-zero abstraction (for hw interrupt) and we "just" have to implement the missing event on software.
Of course NO blocking function as all should be handled by event, and this also require library update (and a good excuse to clean up the API standard).
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
--
--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.