Shop OBEX P1 Docs P2 Docs Learn Events
What is a pthread? — Parallax Forums

What is a pthread?

RsadeikaRsadeika Posts: 3,837
edited 2012-05-07 19:29 in Propeller 1
I thought I would give this subject its own space. Is a pthread a separate, simultaneous activity that occurs in a new cog? Is it a separate, simultaneous activity that occurs in the same cog? Is it a combination of the two? Or none of the above.

Ray

Comments

  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-07 05:00
    I pulled this out of my other thread. It now runs as expected when I use sleep() instead of waitMS(). In this code I show usleep(), has an error:
    Undefined reference to _usleep.

    When I get some answers to my first post here, then I will try some other things, and I will flesh out the program. In the mean time I have to figure out what is really going on.

    Ray

    /*
    * threads1.c
    */
    #include <stdio.h>
    #include <propeller.h>
    #include <pthread.h>
    #include <unistd.h>
    #include "misc.h"
    
    
    void *do_toggle(void *argv)
    {
    	 pthread_set_affinity_thiscog_np(); 
    	
    	while(1)
    	{
    		high(21);
    		/*waitMS(1000);*/
    		usleep(500);
    		low(21);
    		/*waitMS(1000);*/
    		usleep(500);
        }
    }
    
    void main (int argc,  char* argv[])
    {
    	pthread_t thr;
    	
    	pthread_create(&thr, NULL, do_toggle, NULL);
    	while(1)
    	{
    	printf("Hello, World!\n");
    	/*waitMS(2000);*/
    	usleep(3000);
        }
    }
    
  • ersmithersmith Posts: 6,097
    edited 2012-02-07 05:13
    A thread is a separate, simultaneous activity. Where it occurs is unspecified -- it could be on the same cog, or on a different cog. In XMM mode all the threads run on the same cog. In LMM mode we keep allocating cogs until we run out, and at that point threads have to start sharing cogs. Note that a thread may move between cogs, unless you use the setaffinity function to force it to always run on the same cog. When a thread blocks (e.g. by calling sleep()) the cog it is running on will start running the next thread that wants time to run. You can see this behavior in the pthread demo program, which runs 10 threads on 7 cogs, and each thread prints the result of cogid() -- the cog numbers change for a given thread every time.

    Note that the PropGCC implementation of threads is "cooperative" rather than "preemptive"; if two threads are trying to share a cog, each must call sleep(), pthread_yield(), or some similar blocking pthread call in order to give time to the other. If a thread is busy waiting, for example with waitcnt(), then no other threads can use that cog.

    Eric
  • ersmithersmith Posts: 6,097
    edited 2012-02-07 05:16
    For some reason usleep didn't make it into the library. That's a bug, and we'll fix it for the next release. Thanks for finding this!

    In the meantime you can use the (undocumented) __napuntil() function, which works like waitcnt() -- it waits until the _CNT variable reaches a particular value, but it does this in a thread-friendly way, giving up time on the cog to other threads.

    Eric
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-07 05:36
    I just tried compiling using -xmmc, -xmm, -xcog. The -xcog compile came back with a lot of errors. The -xmm, and -xmmc compiled, but when I did a load, not forgetting to use -b C3, it ran, but it did not do anything, no flashing LED, no "Hello, World". So, does pthread only work in LMM mode?

    Ray
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-07 05:51
    In the propeller.h I noticed that you have a cognew() command, is there an example as to how you use that in a program? Since a pthread is an uncontrollable activity, as far as which cog it will be using, does it make more sense to just use cognew()?

    Ray
  • ersmithersmith Posts: 6,097
    edited 2012-02-07 06:57
    Rsadeika wrote: »
    I just tried compiling using -xmmc, -xmm, -xcog. The -xcog compile came back with a lot of errors. The -xmm, and -xmmc compiled, but when I did a load, not forgetting to use -b C3, it ran, but it did not do anything, no flashing LED, no "Hello, World". So, does pthread only work in LMM mode?

    Pthreads is too big to fit in cog memory, so it won't work in -mcog mode.

    The pthread library does work in XMM and XMMC modes, but as I mentioned all the threads have to run on the same cog in that mode. That's a limitation of the way external memory is accessed; only one cog can read from external memory at a time. Someday I hope that restriction will be lifted.

    There is a demo in the propgcc/demo/pthread directory. You can make it for any model by specifying MODEL= on the command line, e.g. for XMM mode do:
    make MODEL=xmm
    
    The default is MODEL=lmm.

    Eric
  • ersmithersmith Posts: 6,097
    edited 2012-02-07 07:05
    Rsadeika wrote: »
    In the propeller.h I noticed that you have a cognew() command, is there an example as to how you use that in a program?
    There are a number of simple demos showing how to toggle LEDs in propgcc/demos/toggle. pthreads_toggle uses pthreads to toggle the LEDs. gas_toggle and pasm_toggle use cognew() to start a new cog up with some assembly code running in it. cog_c_toggle uses cognew() to start some C code (compiled with -mcog) in another cog.

    The cognew() function requires that the memory it works on be in the hub, so some of the demos above only work in LMM mode. The gas_toggle demo has been adapted to work in XMM and XMMC modes as well; you can look at the start_cog() function in gas_toggle/toggle.c to see how that works.
    Since a pthread is an uncontrollable activity, as far as which cog it will be using, does it make more sense to just use cognew()?

    It depends on what you're doing. cognew() is a simple way to run assembly language code on another cog, but for C code it's a little more complicated (you have to compile the C code with -mcog and use objcopy to convert its symbols). pthreads is simpler for C code and allows for more threads than cogs, but in XMM and XMMC all the threads will run on the same cog.

    The toggle demos that jazzed did are a great place to start. There are many ways to achieve the same goal, and which one works best will depend on your application.

    Eric
  • jazzedjazzed Posts: 11,803
    edited 2012-02-07 08:28
    Pthreads give an industry standard way to deal with Propeller's Symmetric Multi-Processing (SMP) hardware.
    Another advantage is that it allows sharing C named variables with COG threads.

    As Eric mentioned, using cognew with C programs requires some special handling.
    The demos/toggle/cog_c_toggle/Makefile does this for us.

    The cog_c_toggle is pure C without PASM. The pasm_toggle uses C and PASM with BSTC for compile.

    Simple steps required for building and loading the cog_c_toggle example without make are:
    1. propeller-elf-gcc -r -mcog -o toggle_fw.cog -c toggle_fw.c
    2. propeller-elf-objcopy --localize-text --rename-section .text=toggle_fw.cog toggle_fw.cog
    3. propeller-elf-gcc -Os -o toggle.elf toggle_fw.cog toggle.c -s
    4. propeller-load -r toggle.elf
    Simple steps for building PASM example demos/toggle/pasm_toggle without make are:
    1. bstc -Ox -c toggle.spin
    2. propeller-elf-objcopy -I binary -B propeller -O propeller-elf-gcc toggle.dat toggle_firmware.o
    3. propeller-elf-gcc -Os -o toggle.elf toggle_firmware.o toggle.c -s
    4. propeller-load -r toggle.elf
    In either case above, it will be necessary to put different cog programs into different files for multiple cogs.

    Hope this helps.
    --Steve
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-07 12:37
    I tried compiling my simple little program in XMM, and XMMC mode, did not get "Hello, World" to show up on the screen. It works in LMM mode, so I figured it would work in the other modes, I was wrong. If you guys are getting acceptable program runs, then it must be something that I am doing wrong. I tried to replicate exactly what you guys are describing, but I am not getting the same results, so I am not sure what I should do to remedy the situation.

    Ray
  • ersmithersmith Posts: 6,097
    edited 2012-02-07 13:55
    Rsadeika wrote: »
    I tried compiling my simple little program in XMM, and XMMC mode, did not get "Hello, World" to show up on the screen. It works in LMM mode, so I figured it would work in the other modes, I was wrong. If you guys are getting acceptable program runs, then it must be something that I am doing wrong. I tried to replicate exactly what you guys are describing, but I am not getting the same results, so I am not sure what I should do to remedy the situation.

    Thanks for keeping at this, Ray -- it's great to have more eyes on the library, and it's certainly possible that you're coming across a bug in it. But I don't think so in this case. I ran exactly what you posted, with the following changes:

    usleep(500) replaced by sleep(1) (and similar changes to the other usleep calls)
    high(21) replaced by high(15) (for the LED built in to my c3 board)

    and it worked in LMM, XMMC, and XMM modes on my C3 board. Source code is at the end of this post. I compiled the XMM mode with
      propeller-elf-gcc -mxmm -o t1.elf -O t1.c -lpthread
    

    If you use WaitMS() instead of sleep() or __napuntil(), then it probably won't work in XMM or XMMC mode, because the single cog will be stuck on one thread forever. In LMM mode it doesn't matter, because in that mode the threads will have their own cogs and so busy-waiting in WaitMS will not hurt anything.

    The full revised code is:
    /*
    * threads1.c
    */
    #include <stdio.h>
    #include <propeller.h>
    #include <pthread.h>
    #include <unistd.h>
    #include "misc.h"
    
    
    void *do_toggle(void *argv)
    {
        pthread_set_affinity_thiscog_np(); 
        
        while(1)
        {
            high(15);
            /*waitMS(1000);*/
            sleep(1);
            low(15);
            /*waitMS(1000);*/
            sleep(1);
        }
    }
    
    void main (int argc,  char* argv[])
    {
        pthread_t thr;
        
        pthread_create(&thr, NULL, do_toggle, NULL);
        while(1)
        {
          printf("Hello, World!\n");
        /*waitMS(2000);*/
          sleep(3);
        }
    }
    
  • Daniel HarrisDaniel Harris Posts: 207
    edited 2012-02-07 16:10
    Hi Eric,

    What is in your misc.h library? If I comment out, the compiler complains at me saying "undefined reference to _high" and "..._low". (I figured something would be missing, but I thought I'd give it a try anyway). I also tried "#define _high 1" and "#define _low 0" to no avail.

    Additionally, when compiling under Linux, the compiler can't find -lpthread. This is on my build box, I probably dont have the paths set up correctly.

    Thanks,
    Daniel
  • ersmithersmith Posts: 6,097
    edited 2012-02-07 18:02
    What is in your misc.h library? If I comment out, the compiler complains at me saying "undefined reference to _high" and "..._low". (I figured something would be missing, but I thought I'd give it a try anyway).
    The misc.h header file is Ray's, not mine. high and low are defined by:
      int high(int WCpin)
        {
                unsigned int bits = 1 << WCpin;
            DIRA |= bits;
            OUTA |= bits;
    
              return WCpin;
        }
    
        /*
        * Set a pin low without affecting other pins.
        * param WCpin = pin number to set low.
        */
        int low(int WCpin)
        {
                unsigned int mask = 1 << WCpin;
            DIRA |= mask;
            OUTA &= ~mask;
    
              return WCpin;
        }
    
    Additionally, when compiling under Linux, the compiler can't find -lpthread.

    That's very strange, my compilation actually was on Linux. Do you have the most recent version of the compiler for Linux? -lpthread was added relatively late (I think in the last release or two).

    Eric
  • Daniel HarrisDaniel Harris Posts: 207
    edited 2012-02-07 18:13
    Great, thank you!

    About the -lpthread thing - thats probably what it is. Its been a while since I have done hg pull/update on my build box.. It didn't throw the error on the Windows side; I just used the pre-built package.

    Thanks for the info :).
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-08 03:55
    @ersmith- What does your propeller-load command look like? I am using 'propeller-load -r -t -b C3 threads2', to load an XMM version. I just tried this on my Windows 7 64 bit machine, and I am getting the same results, loads but "Hello,World" does not appear, and no flashing LED. But, it works in regular LMM mode. Maybe with the Eclipse IDE, these types of problems will no longer be a problem, I hope.

    Ray
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-08 05:51
    I was just thinking, since almost everybody is using a linux setup, and they are getting positive results, could there be a problem with the windows version, maybe something got left out?

    Ray
  • jazzedjazzed Posts: 11,803
    edited 2012-02-08 06:53
    Rsadeika wrote: »
    I was just thinking, since almost everybody is using a linux setup, and they are getting positive results, could there be a problem with the windows version, maybe something got left out?

    Ray


    I'm using windows too.

    Use sleep for now because the pthread safe function usleep is missing from the library.
    We'll need another release to use usleep.

    This works for me.
    /*
     * raypthread.c
     */
    #include <stdio.h>
    #include <propeller.h>
    #include <pthread.h>
    #include <unistd.h>
    
    /*
     * Set a pin high without affecting other pins.
     * param WCpin = pin number to set high.
     */
    int high(int WCpin)
    {
         unsigned int bits = 1 << WCpin;
         DIRA |= bits;
         OUTA |= bits;
         return WCpin;
    }
    
    /*
     * Set a pin low without affecting other pins.
     * param WCpin = pin number to set low.
     */
    int low(int WCpin)
    {
         unsigned int mask = 1 << WCpin;
         DIRA |= mask;
         OUTA &= ~mask;
         return WCpin;
    }
    
    void *do_toggle(void *argv)
    {
        pthread_set_affinity_thiscog_np(); 
        
        while(1)
        {
            high(21);
            sleep(1);
            low(21);
            sleep(1);
        }
    }
    
    int main (int argc,  char* argv[])
    {
        pthread_t thr;
        
        pthread_create(&thr, NULL, do_toggle, NULL);
        while(1)
        {
            printf("Hello, World!\n");
            sleep(5);
        }
        return 0;
    }
    


    Build with:

    propeller-elf-gcc.exe -o a.out -Os raypthread.c -lpthread -s
    propeller-load.exe a.out -r -t
  • RsadeikaRsadeika Posts: 3,837
    edited 2012-02-08 10:33
    @jazzed - what you have shown works fine for me to, it is when I compile it with -mxmm, and load it, it does not work.

    Ray
  • jazzedjazzed Posts: 11,803
    edited 2012-02-08 11:00
    Hi Ray,

    Sorry I missed that part. I also have trouble with the old windows distribution.

    I've started a new windows build this morning and will post an update if it works better.
    I think Eric has provided a fix for this and usleep.
  • jazzedjazzed Posts: 11,803
    edited 2012-02-08 20:20
    jazzed wrote: »
    I've started a new windows build this morning and will post an update if it works better.
    I think Eric has provided a fix for this and usleep.

    Hi Ray. Please download the new Windows PropGCC Alpha Distribution.
    The program listed above works with it in XMM and XMMC mode (change P21 to P15 for C3 testing).
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-02-16 12:44
    This is a great thread and contains a lot of valuable information but it's scattered across 20 posts. As part of my volunteer documentation contributions, I want to consolidate it and clarify the multi-COG, multi-thread options available to us. With the toggle examples, the information in this thread and a few other resources, I think a pretty good summary of Propeller multi-COG/multi-thread programming can be developed. Inter-thread messaging and parametr passing will also be addressed. (I really like multi-core, multi-process, multi-thread, SMP type programming too!!)

    I tried to summarize the options available into a table to clarify what can be done and what the restrictions are:
     [TABLE="width: 615"]
    [TR]
    [TD="class: xl63, width: 125"]Memory   Model[/TD]
    [TD="class: xl63, width: 91"]Thread Code[/TD]
    [TD="class: xl63, width: 78"]Thread Type[/TD]
    [TD="class: xl63, width: 104"]Threads/Cogs[/TD]
    [TD="class: xl63, width: 125"]Multi-tasking[/TD]
    [TD="class: xl63, width: 92"]COG Affinity?[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]LMM[/TD]
    [TD="class: xl63"]C-LMM[/TD]
    [TD="class: xl63"]Pthread[/TD]
    [TD="class: xl63"]Multiple/multiple[/TD]
    [TD="class: xl63"]Cooperative[/TD]
    [TD="class: xl63"]Y - if specified[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]COGM[/TD]
    [TD="class: xl63"]C-COGM[/TD]
    [TD="class: xl63"]COGINIT[/TD]
    [TD="class: xl63"]1/available COG[/TD]
    [TD="class: xl63"]No[/TD]
    [TD="class: xl63"]by definition[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]XMM[/TD]
    [TD="class: xl63"]C-XMM[/TD]
    [TD="class: xl63"]Pthread[/TD]
    [TD="class: xl63"]Multiple/single[/TD]
    [TD="class: xl63"]Cooperative[/TD]
    [TD="class: xl63"]N/A[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]XMMC[/TD]
    [TD="class: xl63"]C-XMMC[/TD]
    [TD="class: xl63"]Pthread[/TD]
    [TD="class: xl63"]Multiple/single[/TD]
    [TD="class: xl63"]Cooperative[/TD]
    [TD="class: xl63"]N/A[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]LMM[/TD]
    [TD="class: xl63"]C-LMM[/TD]
    [TD="class: xl63"]COGINIT[/TD]
    [TD="class: xl63"]1/available COG[/TD]
    [TD="class: xl63"]No[/TD]
    [TD="class: xl63"]by definition[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]ANY[/TD]
    [TD="class: xl63"]C_COGM[/TD]
    [TD="class: xl63"]COGINIT[/TD]
    [TD="class: xl63"]1/available COG[/TD]
    [TD="class: xl63"]No[/TD]
    [TD="class: xl63"]by definition[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]ANY[/TD]
    [TD="class: xl63"]GAS[/TD]
    [TD="class: xl63"]COGINIT[/TD]
    [TD="class: xl63"]1/available COG[/TD]
    [TD="class: xl63"]No[/TD]
    [TD="class: xl63"]by definition[/TD]
    [/TR]
    [TR]
    [TD="class: xl63"]ANY[/TD]
    [TD="class: xl63"]PASM[/TD]
    [TD="class: xl63"]COGINIT[/TD]
    [TD="class: xl63"]1/available COG[/TD]
    [TD="class: xl63"]No[/TD]
    [TD="class: xl63"]by definition[/TD]
    [/TR]
    [/TABLE]
    
    
    

    Now, let's see if I can explain and summarize this line by line and how much I got right:

    1) You can use PThreads in an LMM memory C program to start LMM C code threads, you can create threads up to and beyond the number of physical COGs available (multiple threads running on multiple COGs). If the thread count exceeds the number of COGs available, then the threads without COG affinity must play nice and employ cooperative multi-tasking techniques. COG affinity is possible up to the number of COGs available.

    2) COGM C programs can start COGM C code threads via the COGINIT/COGNEW mechanism (sys/threads) up to the number of available COGs. A single code thread per COG, COG affinity is implied by definition.

    3) XMM memory model C programs can start XMM memory model code threads via PThreads. All threads will run in a single COG and MUST employ cooperative multi-tasking techniques.

    4) XMMC works the same as XMM model programs.

    5) LMM memory model C programs can start LMM-C code threads via COGINIT/COGNEW (sys/threads) up to the number of available COGs. A single code thread per COG, COG affinity is implied by definition.

    6,7 and 8) These are essentially the same rules for ANY memory model. The difference is the source of the code blob being loaded and executed in the COG. The start mechanism is through COGINIT/COGNEW (sys/thread) with a single code thread per available COG. COG affinity is implied by definition.

    Program design will play a big part in selecting the mechanics used and the rules that need to be followed. The Propeller is a hardware architecture hat is limited in some areas but it does have the features to leverage multi-thread and multi-COG programming through C as well if not better than can be done currently with SPIN/PASM. The PThreads implementation opens up some exciting new opportunities.

    Please let me know where I'm going astray with any of my thoughts or assumptions. I think this is an important area to document in both terms of how to do it and why to do it and how each method can benefit different applications.

    I'll probably start another forum thread once there is a working document and set of programs to discuss.

    Thanks!
  • jazzedjazzed Posts: 11,803
    edited 2012-02-16 13:57
    Hi Rick. Great summary so far.

    Some things to note: pthreads must use a thread-safe delay such as usleep() or sleep() - waitcnt can not be used with pthreads. Also __simple_printf is not thread safe, so you can't use -Dprintf=__simple_printf with pthreads programs. LMM C programs can't start LMM threads with COGINIT/COGNEW, they must use the _start_cog_thread function.

    LMM C programs can start COG threads with COGINIT/COGNEW. You will note that in SimpleIDE toggle/cog_c_toggle that the file toggle_fw.cogc is a "_NATIVE" C to PASM program file. I used the .cogc extension so that the SimpleIDE build rule machine knows what to do.

    Thanks for your effort!
    --Steve
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-02-16 14:17
    Steve,

    Cool!

    There will be a section on thread safe coding techniques. Bunches of things to consider and to do and not do.

    LMM C starting LMM threads with COGINIT/COGNEW was just laziness on my part in the table to indicate not a PThread thread. _start_cog_thread and all its ugly parameters is what I'm using in my program.

    Marching onward!
  • jazzedjazzed Posts: 11,803
    edited 2012-02-16 15:16
    mindrobots wrote: »
    _start_cog_thread and all its ugly parameters is what I'm using in my program.

    Marching onward!

    We could probably make a wrapper or two for that function. STARTCOG ?
    We can't really simplify it though.
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-05-07 18:53
    Found it!! (it got archived - the nerve!)

    I'll cut my table out of here with corrections and add it to the doc pile.
  • jazzedjazzed Posts: 11,803
    edited 2012-05-07 19:29
    mindrobots wrote: »
    Found it!! (it got archived - the nerve!)

    I'll cut my table out of here with corrections and add it to the doc pile.

    Thanks!
Sign In or Register to comment.