Is there a thread-safe printf to use from LMM?
mindrobots
Posts: 6,506
Playing with more blinky tutorials (picking up where I left off when the real world interrupted!)
This code works like a champ on my C3 as it is:
So the question is can I use stdio from a started LMM cog? It would be nice to be handy to at least be able to print debug info from the COGs you start.
If its not thread-safe, maybe a way to close stdout on the first COG and open it on the started COG?
Of course, then there are follow on questions about how this would work with pthreads in LMM and pthreads in XMM.
Maybe I just really don't want to do something like this!
This code works like a champ on my C3 as it is:
#include <stdio.h> #include <propeller.h> #include "pin.h" // propeller-load variable patching based on *.cfg content - see blinky2.c for details // int _cfg_led_pin = -1; // The value will default to -1 if not patched by the loader _cfg_led_pin = 15; // setting up the stack for the cogstart demo - cogstart is the feature being explained in this demo #define MINIMUM_LMM_STACK 160 // MINIMUM stack in bytes to accomodate thread control structure (40 LONG) #define COG1_LOCAL_VARS 0 // number of local variable in LMM thread (4 bytes / Interger) //#define COG1_LOCAL_VARS 20 // used to give stack space when testing printf within do_blink static int cog1_stack[(MINIMUM_LMM_STACK + (COG1_LOCAL_VARS*4))/4]; // allocate space for the stack - you need to set up a // stack for each Cog you plan on starting volatile int wait_time; // a global variable we will change from Cog0 to pass // delay times to cog1 /* The do_blink function will be started in it's own Cog and toggle the LED at whatever rate is set by the starting Cog - this is the main code from blink1 and blink2 moved to it's own function so it can be run in it's own LMM kernel The only difference is that it picks up the delay from the global wait_time variable. */ void do_blink(void *par) // needs to be defined like this to pass to cogstart // the dummy parameter isn't and can't be used for anything { int pin = (int)(void *)par; // printf("pin: %d\n",pin); pinOutput(pin); while (1) { pinHigh(pin); waitcnt(wait_time + CNT); pinLow(pin); waitcnt(wait_time + CNT); } } /* main runs in Cog0 - it starts a second cog and then enters a loop to change the wait time between the LED toggles. do_toggle picks up this changing value and uses it fo it's waitcnt */ int main (void) { int cog_id; // we get the Cog ID (or error) back from the cogstart // we don't really do anything with it in this program but // good practice to keep it around wait_time = CLKFREQ / 32; // initial wait time between toggles waitcnt((CLKFREQ/2)+CNT); // give the terminal time to start cog_id = cogstart(do_blink, (void *) _cfg_led_pin, cog1_stack, sizeof(cog1_stack)); // startup do_blink function on a new Cog // NULL - no arguments are being passed // cog1_stack - stack area for the new function // size_of(cog1_stack) - size of stack in BYTES // if cog_id is -1, then there was a problem starting the cog // just sit in a loop if that happens - otherwise, cog_id is // the cog number that was started and both Cogs can continue // with their work. if (cog_id < 0) { printf("Unable to start Cog. Status %d returned.\n", cog_id); while (1) { } } else { printf("Blink Cog %d has started.\n", cog_id); // a little looping to show how the original Cog can change the global // wait_time variable and it can then be read by the new Cog doing // the blinking int i; while (1) { printf("Entering Blink Loop.\n"); for (i=1;i<5;i++){ waitcnt((CLKFREQ * 2)+ CNT); wait_time = wait_time << 1; if (i == 4) { wait_time = CLKFREQ / 32; } } } } }While debugging, I thought I tried to put a printf inside the do_blink function (this is the function I cogstart). I also increased the stack by 20 longs (100 at one point) to see if it was a stack issue. With the printf in the started function, it printed garbage and probably ran off someplace to hang one or both Cogs. (you can uncomment the printf and the 2nd #define for COG1_LOCAL_VARS if you want to recreate failure)
So the question is can I use stdio from a started LMM cog? It would be nice to be handy to at least be able to print debug info from the COGs you start.
If its not thread-safe, maybe a way to close stdout on the first COG and open it on the started COG?
Of course, then there are follow on questions about how this would work with pthreads in LMM and pthreads in XMM.
Maybe I just really don't want to do something like this!
Comments
If you printf from two or more cogs you must use locks to keep the output of one cog from interfering with the output of another. The same happens in spin, but the locks are hidden in the fullduplex serial object - probably a good example to follow.
Using pthreads gives more flexibility because the locks are handled for you to a point. The only thing with the pthreads implementation is you can't mix waitcnt with printf. Instead you'll have to use sleep and usleep with printf. Of course with pthreads one has to yield to make sure the COGs aren't being hogged.
Either way this is an advanced topic, but either should work applying the right practice.
This does sound a bit advanced for tutorial #4 or 5!
But a good topic!
Eric
This certainly leads to some interesting fodder for demos and tutorials and good underlying info to document.