Hooking into yield()

Status
Not open for further replies.

luni

Well-known member
I know how to override yield() but doing this in a library is not very polite since it disables the standard yield functionality (serial event etc..)

Does anyone know if it is possible to hook into yield without completely overriding it?
 
I would override it and copy also existing yield functionality (backward compatibility)
 
I would override it and copy also existing yield functionality (backward compatibility)
Pragmatic solution but not very elegant (need to change my lib whenever the core yield changes). Anyway, it does the job, so I implemented it this way.
 
A weak blank subcall function called from yield() could be implemented to retain core configuration. run_from_yield() :)

It can be teensy specific without breaking backward compatibility with arduino, and work through all teensyduino updates, thats how I implemented up to 3 background callbacks in flexcan for external accesses
 
run_from_yield()

Ah, that's interesting. Never bothered to read into the event responder thing. On a first glance it looks like it should work, will give it a try later.
 
Somehow I still don't understand if / how it is possible to hook into yield without replacing it. I tried to use the event responder but this stuff is hard to understand without any documentation.

What I would like to achieve is to add a function which should be called from yield. I.e., something with this effect:

Code:
void setup()
{
     attachToYield(myFunction)
}

void yield()
{
      // do all the usual yield stuff

     myFunction(); 
}

@Kurt: since you were working on yield recently you might know if this is even possible...
@tonton81: Don't find the code you are referring above anymore. Do you have a link to the relevant part of flexcan?
 
have stared at yield() regularly - if not using EventResponder or and Arduino serialEvent() code it can be replaced, but the PJRC weak version is all(weak) or nothing(replaced locally).

PJRC yield() calls eventResponder - perhaps that is what tonton81 did? Added a run_from_yield() task to that?
 
Yes, currently I'm replacing it with my code but in libraries this is quite rude since users might need SerialEvents or the EventResponder.... Also copied the yield content from the core to my yield which works of course but that gets messy when the core yield gets updated as just happened.
 
Yes, currently I'm replacing it with my code but in libraries this is quite rude since users might need SerialEvents or the EventResponder.... Also copied the yield content from the core to my yield which works of course but that gets messy when the core yield gets updated as just happened.

Yes, it did just happen. Big drop in overhead from work by @KurtE, but still better without it if unused. But new code won't work on old builds with added support 'stuff'
Saw the yield options for timer (?) code - but didn't read sources how the option was done there.
 
I believe the prescribed way is for you to use an eventResponder object (like I did for Async SPI), And you use the simple attach method, which adds your function to the list of things called by yield...
 
Thanks Kurt, tried your suggestion but it didn't work.

Code:
#include "EventResponder.h"

EventResponder er;

void testfunc(EventResponderRef r)
{
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));  
}

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  er.attach(testfunc);
}

void loop()
{
}


Obviously something needs to trigger the event, so I did

Code:
#include "EventResponder.h"

EventResponder er;

void testfunc(EventResponderRef r)
{
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));

  r.triggerEvent();  // trigger the next event after finishing this one
}

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT); 

  er.attach(testfunc);
  er.triggerEvent();    // start the chain
}

void loop()
{
}

which works. Anyway, feels not right to do it that way... probably quite inefficient?
 
I meanwhile tested this hook to yield method and found that it is actually quite efficient. I put some explanations, example code and an easy to use attach function in the user wiki https://github.com/TeensyUser/doc/wiki/Hooking-into-yield. I also changed the tick strategy of TeensyTimerTool software timers accordingly.

Here the naked code for reference:

Code:
#include "EventResponder.h"

 void myCallback() // test function will be called from yield
 {
   digitalToggleFast(0);
 }

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(0,OUTPUT);

  attachYieldFunc(myCallback); // attach the callback to the standard yield stack
}

void loop()
{
  digitalToggleFast(LED_BUILTIN);
  delay(250);
}


// helpers-------------------------------------------------

void attachYieldFunc(void (*_callback)(void))  // pass a pointer to the function you want to be called from yield
{
  static void (*callback)() = _callback;       // store the passed in function pointer
  static EventResponder er;                    // define a new EventResponder which will handle the calls. 

  er.attach([](EventResponderRef r) {          // we can not directly attach our function to the responder since we need to retrigger the repsonder 
    callback();                                // instead we attach an inline defined relay function which ...
    r.triggerEvent();                          //... calls our function and then retriggers the responder to schedule the next call. 
  });

  er.triggerEvent();                           // to start the call chain we need to trigger the responder once
}
 
Last edited:
If you are willing to edit boards.txt to add linker options, you can use the wrap functionality of the linker:

--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.

This can be used to provide a wrapper for a system function. The wrapper function should be called "__wrap_symbol". If it wishes to call the system function, it should call "__real_symbol".

Here is a trivial example:

Code:
void *
__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu\n", c);
  return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead. The call to "__real_malloc" in "__wrap_malloc" will call the real "malloc" function.

You may wish to provide a "__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as "__wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".

Note --wrap is an option to the linker. You will need to use the -Wl,--wrap=yield option when calling the GCC driver (i.e. gcc or g++).
 
Interesting indeed! But I don't see how that would work if more than one user would like to add functions to yield?
 
with something like:
Code:
void TeensyYield() {
  // real PJRC yeild
}
attribute 'weak' >> void yield() { TeensyYield(); }

Will the compiler just replace the call to yield() with TeensyYield() as an optimization? Or will the HEX have a call to yield() calling TeensyYield()?
 
Status
Not open for further replies.
Back
Top