Thanks for your nice and very useful scheduler libraries.
After having worked with SCoop, I discovered that I was using no one of the many goodies provided, apart from stack printing. So I tried the SchedulerARMAVR and I discovered that it saves over 1500 bytes of program memory and 58 bytes of data memory with respect to SCoop, both of which are important to my project. Having to use dynamic allocation for the stack is a non-issue for me, as I allocate it during setup()
and never free it.
The only thing I was missing is the printing of the size of the stack left free for the tasks. So I got down and wrote my own functions, which appear to work for me.
It is possible to make them slightly more compact by incorporating them into SchedulerARMAVR.cpp, I can provide a patch if you wish. I think they would be a useful addition to SchedulerARMAVR. Here they are:
// Use this rather than Scheduler.StarLoop() and save the pointer returned
// so that you can pass it to stackUnused().
// This function relies on malloc to just give back the same pointer after free(),
// and on Scheduler.StarLoop() to do the first malloc() call to allocate the stack.
// If the SChedulerARMVR code is changed so that the above is not true, memory corruption will result.
byte *startLoop (SchedulerTask task, unsigned stackSize) {
byte *stack = (byte *)malloc(stackSize); // the same allocation Scheduler.StarLoop will do
// If we are being called from the main loop, let's fill our stack, too
extern byte *__brkval; // set after malloc, it is the top of the heap
if ((byte *)&stack > __brkval) // this function called from the main loop
for (byte *end = (byte *)&end - 10; // leave space for a small interrupt routine
__brkval < end; // until end pointer smashes into the heap
*end-- = 0x55) ; // fill stack with fixed value
memset(stack, 0x55, stackSize); // fill the stack's stack with fixed values
free(stack); // now we free the space allocate with malloc
Scheduler.startLoop(task, stackSize); // start the task
memset(stack, 0x55, 4); // rewrite over memory dirtied by free()
return stack; // stack base, to be passed to stackUnused()
}
// When not using the scheduler, call it from setup()
// Else, this work is done when calling startLoop(), so this function should not be used
void fillStack () {
extern byte __heap_start, *__brkval;
byte *beg = (byte*)(__brkval ? __brkval : &__heap_start);
byte *end = (byte*)&end - 10; // leave space for a small interrupt routine
while (beg < end) *beg++ = 0x55; // can't use memset here: it would use the stack
}
// Count bytes marked 0x55 from the stack base to the the local variables
// Should be called with either no arguments, for counting free stack on the main loop,
// or with two arguments, for the tasks.
unsigned stackUnused (byte *stackBase, unsigned stackSize) {
byte *end = stackSize ? stackBase + stackSize : (byte *)&end - 10;
byte *p = stackBase;
while (*p == 0x55 && p < end)
p += 1;
return p - stackBase;
}
In order to use the code, you should use startLoop(task, stackSize)
rather Scheduler.startLoop(task, stackSize)
, and save the return value, that is, the base of the stack of the task just created. Also, save the value of the stack size in a variable.
Then, you can call stackUnused()
either from the main loop without arguments, or from wherever you want with two arguments: a task's stack base pointer and the task stack size. It returns the number of bytes on the stack that have been left unused for the main loop or for that task, respectively.