I am using an inhouse developed multi tasking scheduler for 8051. Every function that I write, I need to put reentrant on it. Is it possible to add a compiler directive like the c166 compilers for c51 compiler?
"Every function that I write, I need to put reentrant on it."
That sounds like a really, really bad idea!
That puts a serious overhead in terms of both code size and speed on every single function!! :-0
Did whoever developed this "multi tasking scheduler" understand what they were doing??
"Is it possible to add a compiler directive like the c166 compilers for c51 compiler?"
No. This is probably to remind you of the significant impact, and that only those functions that really need it should be declared as 'reentrant' in C51...! :-0
Without setting a function as reentrant, I do not see any way in which it can be made thread safe. It looks like 8051 is not a good microcontroller to have a multi tasking scheduler.
It looks like 8051 is not a good microcontroller to have a multi tasking scheduler You are absolutely right. Anyhow, why do you need one? I know of no case fitting a '51 where such an animal would be an advantage (except, of course, allowing a programmer to "think PC").
Without setting a function as reentrant, I do not see any way in which it can be made thread safe If you absolutotatally need to use such a thing there are many RTOSs made for the '51, to make those that can only "think PC" happy, and all I know of do not need functions to be reentrant. There is one right here: the Keil RTX51
Erik
From this support discussion http://www.keil.com/support/docs/1873.htm
It looks like RTX51 also needs either the function to be inherently reentrant (uses only registers) or have a reentrant keyword. The solution for non-reentrant function is to protect them with a semaphore. I don't see RTX51 any different from the one that I am using now.
Any real implementation of a multithreading OS needs to address the static data usage of the CLib. You must either use only pure functions implemented with registers and stack-allocated locals, or you use indirect addressing for the static data storage of all C lib functions, and supply a different static storage area for each thread. The compiler must be thread-aware, or allow you to reimplement static storage allocator functions for the CLib to be really functional in this architecture. When you can't do that, context switch is costly and you have to surround the CLib with access locks to prevent static clash. That is precisely what happens in the 8051.
Moreover, in the 8051, particularly in Keil C, the default storage class is overlay for local data. This is faster and generates smaller footprint code, but requires a single-threaded architecture, of course, for the compiler to perform static analysis on the code.
The alternative approach is to force the shared functions to be reentrant, i.e., force the default storage class of locals to be allocated on a stack.
On the 8051 this cost is excessive. I know of no small CLib for the '51 that is fully reentrant by design, and it seems that such a library would be much heavier in size and speed when compared with a staggered clib.
I agree with erik malund, in that a cooperative, single_threaded_plus_interrupts architecture can generally be much faster than a true multithreaded architecture for the 8051. This is not just an aesthetic choice. The comparison of a well designed event-driven cooperative multitasking system with a preemptive RTOS-based system leaves little space for argument. Even for 8KB xdata devices running above 20MHz, the CPU bandwidth taken by the RTOS kernel is prohibitive.
For other cores, this is not necessarily true. The ARM, for example, is a core designed to be used in a multithreaded, multiprocessor architecture. And the RVCT libraries are designed in a way that allows you to reimplement the necessary base functions to support your own true multitasking system, in a very optimized and transparent way.
I agree totaly with Jonny and Eric on the advantage of single task system. I also understand the disadvantage of having reentrant functions. Unfortunately, I have to live with this limitation for the time being. Rather than adding the reentrant keyword on every function that is called from tasks, I was looking for a quick compiler directive that will do the trick. Just like c166 compiler.
Your problem may not be as bad as it seems. The Keil C Lib has many functions that are pure or reentrant. They are described in the clib documentation. Those functions are reentrant and thread-safe.
The clib non-reentrant fucntions are another pain. Your clear alternative for a better life would be not using the standard clib supplied functions that are not reentrant in your code. You can implement a version of those needed functions in a lib of yours, and the linker will use your version instead of the clib version. This has the advantage of total control, so if you can't write a pure registered function, you can at least use a tempdata static allocation that can be controlled by your task switcher. One very simple way to do this is to have a global pointer that points to the thread local static storage for your clib functions (your tempdata section). Whenever you switch contexts, you can set the pointer to the new thread local storage base address, and the threads will happily live oblivious of one another. This is MUCH faster to switch, and need no locks.
Your own functions are thread safe if they are pure (no explicit global vars and no static vars) and don't have memory temporary storage, i.e., all its vars are stored in banked registers. Again, if you can't write them small to fit banked registers, you can use the above indirect thread local storage pointer solution for your own functions. Since it is private to a single thread, you can use heavy overlaying to keep the footprint small.