EmbeddedRelated.com
Forums

"Boost context" for task switching on embedded Linux on ARM

Started by Unknown August 3, 2018
Following on from my recent question about PThreads I have a new question about Boost:context
https://www.boost.org/doc/libs/1_67_0/libs/context/doc/html/index.html

We have some legacy code(Motorola 68000 C) that we want to use that has cooperative task switching.  We want to run it all as a single thread and keep its co-operative organization within that thread.  Is "Boost context" a reasonable way to do this?  If not, do we have to figure out what registers and state we have to save/ restore and hope that we get everything?

Alternatively we could run each task as a thread and enforce round robin behaviour but that seems a bit wasteful.
On Fri, 3 Aug 2018 05:52:01 -0700 (PDT), gp.kiwi@gmail.com wrote:

>Following on from my recent question about PThreads I have a new question >about Boost:context >https://www.boost.org/doc/libs/1_67_0/libs/context/doc/html/index.html > >We have some legacy code(Motorola 68000 C) that we want to use that >has cooperative task switching. We want to run it all as a single >thread and keep its co-operative organization within that thread. Is >"Boost context" a reasonable way to do this?
Boost Context requires C++ 11. You may be facing a lot of work even to get your old C code to compile.
>If not, do we have to figure out what registers and state we have >to save/ restore and hope that we get everything?
How was the tasking accomplished before? OS? [which one?] Or something in the code?
>Alternatively we could run each task as a thread and enforce round >robin behaviour but that seems a bit wasteful.
That won't necessarily work. In cooperative tasking, the current task runs until it explicitly yields the CPU. Often shared data is manipulated without locks because the programmer knows no other task can run and interrupt the operation. Running such a system under a pre-emptive task switcher is going to fail eventually regardless of the scheduling policy. George
On 18-08-04 10:28 , George Neuner wrote:
> On Fri, 3 Aug 2018 05:52:01 -0700 (PDT), gp.kiwi@gmail.com wrote: > >> Following on from my recent question about PThreads I have a new question >> about Boost:context >> https://www.boost.org/doc/libs/1_67_0/libs/context/doc/html/index.html >> >> We have some legacy code(Motorola 68000 C) that we want to use that >> has cooperative task switching. We want to run it all as a single >> thread and keep its co-operative organization within that thread.
...
>> Alternatively we could run each task as a thread and enforce round >> robin behaviour but that seems a bit wasteful. > > That won't necessarily work. > > In cooperative tasking, the current task runs until it explicitly > yields the CPU. Often shared data is manipulated without locks > because the programmer knows no other task can run and interrupt the > operation. > > Running such a system under a pre-emptive task switcher is going to > fail eventually regardless of the scheduling policy.
If the tasks cooperate, round-robin execution can be enforced even if the scheduler is "basically" pre-emptive. One just defines a mutex (with a waiting-task queue), and each task takes the mutex, does its job, releases the mutex, and repeats. If the mutex queue is FIFO, the result is round-robin execution of these cooperating tasks, no? Of course no task outside this cooperating task group should access the unprotected, shared data. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
On Sat, 4 Aug 2018 11:13:02 +0300, Niklas Holsti
<niklas.holsti@tidorum.invalid> wrote:


>If the tasks cooperate, round-robin execution can be enforced even if >the scheduler is "basically" pre-emptive. One just defines a mutex (with >a waiting-task queue), and each task takes the mutex, does its job, >releases the mutex, and repeats. If the mutex queue is FIFO, the result >is round-robin execution of these cooperating tasks, no?
That will work if the system really does queue tasks waiting on the mutex. But some systems simply wake all waiting tasks when a shared resource is freed - which task gets it then is happenstance. If taking the mutex is a free-for-all, there is a chance that some tasks may starve and never get it. There also is the possibility with cooperative tasking that tasks may sometimes need to run in a particular order by directed yielding. Emulating that under a pre-emptive system requires something like message (token) passing. The OP did not specify what system is being used. Using a mutex will solve the shared data coordination problem - the task currently holding the mutex will be preempted for rescheduling, but because all the other tasks are waiting for the mutex, the one holding it will be scheduled to run again. [However, it may not be run immediately if there are other runnable tasks in the system not participating in the mutex.]
>Of course no task outside this cooperating task group should access the >unprotected, shared data.
Absolutely. George
On Sat, 04 Aug 2018 07:08:37 -0400, George Neuner
<gneuner2@comcast.net> wrote:

>The OP did not specify what system is being used.
Oh, sorry ... the OP did specify Linux. I'd have to double check, but I think Linux does wake all processes pending on an OS semaphore and lets them fight for it. George
On Saturday, August 4, 2018 at 11:22:25 PM UTC+12, George Neuner wrote:
> On Sat, 04 Aug 2018 07:08:37 -0400, George Neuner > wrote: > > >The OP did not specify what system is being used. > > Oh, sorry ... the OP did specify Linux. > > I'd have to double check, but I think Linux does wake all processes > pending on an OS semaphore and lets them fight for it.
Thanks for the replies. I'm using google groups - hope it's readable. Regarding C++11 - we'll be using GCC 7. The guy who wrote the code was C++ aware and it won't be hard to get it to compile - a lot easier than re-writing. If we make each co-operative task a proper thread then I was thinking we would suspend the thread when it does a task switch and the task switch function would release a mutex to allow other "non co-operative code" to access the data being generated by the co-operative threads. The task switch function would resume the next co-operative thread in round robin fashion. The legacy code just saves each task stack pointer in an array and cycles around them. It seems that Linux provides a switch_to and context_switch function that can be used for co-operative threading but I've been unable to find any source code (for ARM 9 or ARM anything) to compare with what boost::context does. I was hoping to avoid putting mutexes all through the legacy code.
After reading your comment about jmp_buf in the other thread I guess we can use setjmp, longjmp to save / restore context.  I'm not sure why we're not already using it.
On Sunday, August 5, 2018 at 12:25:50 AM UTC+12, gp...@gmail.com wrote:
> After reading your comment about jmp_buf in the other thread I guess we can use setjmp, longjmp to save / restore context. I'm not sure why we're not already using it.
After investigating now I see what you mean when you say "if the jmpbuf structure is documented". So this is not very easy and not very portable.
On Sat, 4 Aug 2018 04:49:42 -0700 (PDT), graeme.prentice@gmail.com
wrote:

>On Saturday, August 4, 2018 at 11:22:25 PM UTC+12, George Neuner wrote: >> On Sat, 04 Aug 2018 07:08:37 -0400, George Neuner >> wrote: >> >> >The OP did not specify what system is being used. >> >> Oh, sorry ... the OP did specify Linux. >> >> I'd have to double check, but I think Linux does wake all processes >> pending on an OS semaphore and lets them fight for it. > >Thanks for the replies. I'm using google groups - hope it's readable. > >Regarding C++11 - we'll be using GCC 7. The guy who wrote the code was >C++ aware and it won't be hard to get it to compile - a lot easier than >re-writing.
That's encouraging at least.
>If we make each co-operative task a proper thread then I was thinking we >would suspend the thread when it does a task switch and the task switch >function would release a mutex to allow other "non co-operative code" >to access the data being generated by the co-operative threads. >The task switch function would resume the next co-operative thread in >round robin fashion. The legacy code just saves each task stack pointer >in an array and cycles around them.
Just a word of caution: suspending a thread from the outside can cause problems if done at the wrong time. It's acceptible for a thread to suspend itself, but apart from debugging there really are no good reasons for one thread to suspend another. It's ok for a thread to be in a "suspended" state while waiting for some event that makes it runnable again. Just make sure to distinguish the state from the action.
>It seems that Linux provides a switch_to and context_switch function >that can be used for co-operative threading but I've been unable to >find any source code (for ARM 9 or ARM anything) to compare with what >boost::context does.
Sorry, I can't you help you there - I've never used those functions. It might help to take a look at some of the userspace thread libraries that are available. In particular, the old "cthreads" library [which predates kernel threading in Unix] was designed to be portable - if you can find it, it might give you some good ideas. George
On Sat, 4 Aug 2018 06:01:30 -0700 (PDT), graeme.prentice@gmail.com
wrote:

>On Sunday, August 5, 2018 at 12:25:50 AM UTC+12, gp...@gmail.com wrote: > >> After reading your comment about jmp_buf in the other thread >>I guess we can use setjmp, longjmp to save / restore context. >>I'm not sure why we're not already using it. > >After investigating now I see what you mean when you say "if >the jmpbuf structure is documented". So this is not very easy >and not very portable.
setjmp/longjmp were designed originally to enable abandoning an errant computation by jumping back to state of the program that existed before the computation began. Using it to jump around within a single thread is relatively easy. Multi-tasking with it is another issue. It's generally pretty easy to set up a new stack, but initializing all the other CPU state for the new "task" can be problematic. It was easier to do with simpler CPUs. It still can be done with a modern CPU, but it is much more difficult to get everything right [the more so for lack of documentation]. George