Shop OBEX P1 Docs P2 Docs Learn Events
propgcc and pthreads - strange lockup behavior — Parallax Forums

propgcc and pthreads - strange lockup behavior

bgpbgp Posts: 34
edited 2014-08-24 12:47 in Propeller 1
I've been trying some things with propgcc as part of making a relatively simple device that has a speaker, some buttons, and also accepts commands through the serial console.

In order to run these at the same time, I believe I need to do this in different (p)threads. I imagine would want something like:

* A thread to be reading from the console, waiting for commands and sending back output.
* A thread that is waiting to be told to beep and when it should runs freqout() or whatever
* A thread that is scanning the pins for the buttons and setting variables based on what it finds.
* Perhaps the main program execution is doing the routing and looking for these events and doing stuff with them.

It is also possible that some of these could be combined (like the last I know for example could easily be). But some are not so easy - like the freqout() call.

Long story short, in my test program I seem to easily get strange problems where things lock up. Example code:
// includes, etc. elided

int pinValue(int pin)
{
  uint32_t mask = 1 << pin;
  return (INA & mask) ? 1 : 0;
}


int lastPinValue = 0;


void *watchPins(void *arg) {
  
  while(1) {
    __napuntil(1000);
    
    lastPinValue = pinValue(13);
  }
  
}  




int main()                                    // Main function
{


  
  DIRA &= ~(1 << 13);
  
  printf("Hello, ");


  
  pthread_t t;
  
  pthread_create(&t, NULL, watchPins, (void *)&pinMutex);


  
  printf("World!\n");
  
  while (1) {
    printf("v: %d\n", lastPinValue);
    __napuntil(1000);
  }
}



In that code, the assignment of lastPinValue seems to lock things up (all output halts - for all intents and purposes is dead/sleeping).

Even if the assignment of lastPinValue were not thread-safe, I would not think this would lock the program up.

Am building this with SimpleIDE using the CMM memory model.

Are there some rules of the road regarding how threads should be talking to each other?

Comments

  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-08-24 06:49
    Your problem may be with __naputil(). I would shy away from that and use waitcnt directly. When I tried a simple example:
    int main () {
      while (1) {
        printf("Hello, World!\n");
        __naputil(1000);
      }
    }
    
    It never paused at all. Looking through the propgcc source code, it appears to do different things depending on whether or not you're in a pthread, which this example isn't. In any case... that sounds unpredicatable to me which I don't like.

    Assuming you want to pause for one second:
    #define SECOND CLKFREQ
    #define MILLISECOND SECOND/1000
    #define MICROSECOND MILLISECOND/1000
    
    int main () {
      while (1) {
        printf("Hello, World!\n");
        waitcnt(1000*MILLISECOND + CNT); // or simply waitcnt(SECOND + CNT)
      }
    }
    
  • jazzedjazzed Posts: 11,803
    edited 2014-08-24 09:22
    Hi,

    The pthreads tools are available as a supplement for situations that require more tasks than can be run on available cogs.

    There are many rules to follow for pthreads to work correctly. I highly recommend hobbyists avoiding them entirely if possible.
  • bgpbgp Posts: 34
    edited 2014-08-24 10:39
    Thanks @SwimDude - waitcnt seems to have a different problem where it blocks things in a different way. I ended up trying __naputil() based on this post: http://forums.parallax.com/archive/index.php/t-137776.html

    @jazzed - gotcha, makes sense to me. Is there a simple "cognew"-type way of just calling a function in a new cog? If there is some example code that you could point me to that does something like that, that would be awesome. So far I haven't found such a thing.

    Best, Brad

  • jazzedjazzed Posts: 11,803
    edited 2014-08-24 10:53
    Try this.

    http://learn.parallax.com/multicore-approaches

    It is part of the Parallax Propeller C learn tutorials.

    One thing that should be considered is that printing to the uart from multiple cogs or threads can be difficult with pthreads or multicore methods. There is one simple solution described in the multicore tutorial.
  • bgpbgp Posts: 34
    edited 2014-08-24 10:56
    Thanks! Yeah that's just the thing.

    (I also noticed this goes into quite a bit on this topic: https://code.google.com/p/propgcc/wiki/PropGccInDepth will read up more on that too.)
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-08-24 10:56
    bgp wrote: »
    Thanks @SwimDude - waitcnt seems to have a different problem where it blocks things in a different way. I ended up trying __naputil() based on this post: http://forums.parallax.com/archive/index.php/t-137776.html

    That post is great - I'll definitely be referencing it again later.

    With that said though, your program doesn't use more than 8 threads, so you shouldn't have any problems using waitcnt.

    The following was taken from the Simple libraries (I think... it's been too long) and tweaked to use the libraries in PropWare. It should give you a solid idea how to use cog_run().

    Original source
    /** * @file    Concurrency_Demo.cpp
     *
     * @author  David Zemon
     */
    
    
    // Includes
    #include "Concurrency_Demo.h"
    
    
    volatile int lock = locknew();
    
    
    // NOTE!!! The direction of a pin (or port) is not a member variable of the pin
    // or port. Therefore, if you're going to declare a pin (or port) in one cog
    // and use it in another, you MUST set the direction in the new port. Look at
    // how g_pin2.set_dir() is called within blinkAnLEDSome() instead of the
    // direction being set in the constructor
    PropWare::Pin g_pin1(PropWare::Port::P16, PropWare::Port::OUT);
    PropWare::Pin g_pin2(PropWare::Port::P17);
    const int g_someStackSpace = 64;
    
    
    // Main function
    int main () {
    
    
        cog_run((void (*) (void *)) &blinkAnLEDSome, g_someStackSpace);
    
    
        waitcnt(20*MICROSECOND + CNT);
    
    
        while(lockset(lock));
    
    
        for (int i = 0; i < 40; ++i) {
            waitcnt(50 * MILLISECOND + CNT);
            g_pin1.toggle();
        }
    
    
        lockclr(lock);
    
    
        return 0;
    }
    
    
    void blinkAnLEDSome (void) {
        while(lockset(lock));
    
    
        g_pin2.set_dir(PropWare::Port::OUT);
    
    
        for (int i = 0; i < 40; ++i) {
            waitcnt(50 * MILLISECOND + CNT);
            g_pin2.toggle();
        }
    
    
        lockclr(lock);
    }
    
  • bgpbgp Posts: 34
    edited 2014-08-24 12:47
    The following was taken from the Simple libraries (I think... it's been too long) and tweaked to use the libraries in PropWare. It should give you a solid idea how to use cog_run().

    Cool and thanks!

    Adjusting things to use cog_run() and sharing data with a volatile global variable worked like a charm and was very simple to use.
Sign In or Register to comment.