I am using NXC to automate my trains. I want to apply the same functions/tasks to many components. I do not see a way to do this. With the sample code below only task1 is executed.
By using "inline" you don't actually share the function.
To share, the function that will be shared must be protected -- while one task is using it, the others must be made to wait until the first task is done. One way to do that is declare the function with "safecall" as shown in the following example.
Function calls in NXC are not reentrant/threadsafe so you cannot call the same function from two separate tasks safely unless you either force all function calls to be "safecall" via a compiler switch or you mark the specific functions that you want to call from multiple threads as safecall functions. Just like Morton demonstrated above. The alternative is to inline the function containing the code you want multiple tasks to execute. That would let you keep the while loop in the function but as Morton indicated it would actually emit a copy of the inline function at the call site as hergipotter described.
There have been multi-threads in single-core machines for decades! It is a scheduling/context-switching problem an OS hopefully has implemented. Reentrancy and (user-level) thread-safety are separate issues. Any shared resources (global data and IRLink) the functions to operate on I have protected by mutexes. The mutexes nicely fix the thread-safety problem. safecall is a brute-force mutex that is not isolated around the data it is trying to protect.
It sounds like to make a function reentrant (I am not even asking for recursion!) I have to make it and all its sub-routines inline?
What exactly happens to execution when you try to call a function that another task is executing?
The VM in the standard and enhanced firmwares requires that a function call consist of the function name followed by that function's return address variable.
subcall func, func_return_address
In the called function you return to the caller via the subret opcode which requires the function's return address variable.
subret func_return_address
All variables in the VM are globals. These two opcodes make it impossible to safely reenter a function while it is already executing on another thread. There is also no such thing as a call stack in the firmware VM. The resource that the mutex generated by the compiler when you use safecall is protecting is the function's global return address variable.
Fortunately, you can make your functions and the ones they call inline and not worry about the resulting code size or function call safety. Or you can write your functions in a way that works well with safecall (i.e., keep the long running loops outside the shared functions).
that's what I wanted to express:
I always use "inline" if I have functions (e.g. math functions: max, min, round,...) which might be accessed by different threads at the "same" time and which don't block other threads while being executed by 1 specific thread.