Shop OBEX P1 Docs P2 Docs Learn Events
semaphore question — Parallax Forums

semaphore question

Don PomplunDon Pomplun Posts: 116
edited 2007-01-22 17:04 in Propeller 1
I understand that if a process in one cog wants to send multi-Long data to another there is a need for coordination. So cog2 sits in a loop waiting on a global variable that cog1 sets with the ID of his assigned semaphore. Cog2 then watches that semaphore to coordinate his data reads. When cog1 is done he returns the semaphore and clears the global variable ID. Right?

It seems that you could accomplish the same effect by just setting agreed-upon values in that same global variable and forget about the semaphores, saving some amount of time & potential trouble.

What am I missing?

-- Don

Comments

  • asterickasterick Posts: 158
    edited 2007-01-22 06:27
    Absolutely nothing. except you don't have to waste main ram.
  • Don PomplunDon Pomplun Posts: 116
    edited 2007-01-22 06:35
    OK, I'm probably getting closer to what I'm ACTUALLY missing . . .
    From the tutorial examples, I understood that inter-cog communication was via global variables (those declared in the Var section), which resided in hub (main ram) memory.
    Closer?

    -- Don
  • asterickasterick Posts: 158
    edited 2007-01-22 14:57
    Semaphores exist in the hub, using things called 'LOCKS'. Locks are not enforced on the processor, and there are only 8 of them. Essentially, there are 4 HUBOP's (like coginit and cogstop). The instructions are LOCKNEW LOCKSET LOCKCLR and LOCKRET

    Locks can best be thought of as 2 8bit values. one flags the availability of a lock, and the other is the status of it.

    Lock new return the index of a semaphor
    Lock set sets the lock hi, and returns the status of the lock before you tried to alter the value (in carry I believe)
    Lock clear sets the lock low, and returns the status of the lock before you tried to alter the value
    Lock ret takes the index and returns it to the lock pool, allowing someone else to allocate it.

    the MAIN reason why it's better to use locks than a byte in main ram (where there is handshanking and maintaining values) is that it both reads and writes it at the same time... so you can do stuff like this:

    WaitLock LOCKSET LockID WC
                  IF_C MOV Delay, CNT
                  IF_C ADD Delay, #100
                  IF_C WAITCNT Delay, #0
                  IF_C #WaitLock
                  RET
    
    



    Otherwise, you can hit the conditions where both cogs read the status byte on the same hub rotation, and they both think it is safe. It's simply impossible for the code to hit a race condition using locks.
  • Don PomplunDon Pomplun Posts: 116
    edited 2007-01-22 16:50
    Ok, enough belaboring the semaphore operation. BTW, thanks for your patience & explanations. There still seems to be a need for cog 1 to tell cog 2 which semaphore to watch. The Spin Tutorial Example for (one method for) communicating a data value between 2 cogs uses a global variable, so that's one way. I suppose another would be to obtain a semaphore and pass the ID as a parameter when the new cog was started. Are there other, more preferable options?

    Another Q: The elementary examples for doing something (blinking an LED) using a new cog are one-time deals. i.e. the LED blinks 10 times, then runs out of code. I assume that this fres up that cog and returns it to the "available cog pool", yes?

    More to follow, I'm sure ;=)

    -- Don
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-22 16:58
    Don,
    Most inter-cog communication is indeed via shared global variables (you can also communicate via shared I/O pins). Semaphores are used to prevent two or more cogs from trying to change the same variables at the same time.

    There are a few special cases (like a ring buffer) where you don't need semaphores because only one cog actually changes a variable although a 2nd cog will "read" it. In this case, the "boundary conditions" are such that, if a mistake is made and one cog is changing the variable (buffer pointer) while the other is looking at it, no invalid assumption will be made. Look at the keyboard driver in the Propeller Tool's library to see how it can be done.

    The other special case is a one entry buffer, kind of a communication between cogs of "here - do this", "ok - I got it, thanks" where one cog puts a command or other information to be handled into a variable only if the variable is zero. The other cog waits for a non-zero value in the variable and sets the variable to zero when it's either finished working on the information or at least started working on it. You can use something like this to "protect" a larger area for passing information from one cog to another. If the variable is zero, the first cog can do anything it wants to with the common area. Once it sets the variable to non-zero, the common area "belongs to" the second cog and the first one may not touch it. Similarly, the second cog can do anything it wants to with the common area when the variable is non-zero, but, once it sets the variable to zero, the second cog may not touch the common area.

    The semaphores work somewhat like this last case in that they act as a lock on a larger common area. Semaphores are more general and can be used where multiple cogs may need to access the same area. These special cases only work between two different cogs. On the other hand, there are only 8 semaphores in the Propeller, so they're a limited resource. You should use these special cases when they apply so that the semaphores are available for those cases where the special cases won't fit.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-22 17:04
    The elementary examples you mentioned allow the cog to stop after it finishes. This does free up the cog and returns it to the "pool". It's rare in real code for that to be the case. Most cogs once started run continuously as a kind of command interpreter primarily because the overhead in restarting the cog is too high (a couple of hundred microseconds) to perform multiple single actions. Look at the floating point routines as an example of something purely computational that just sits there waiting for a request for something to do (as opposed to waiting for some I/O event).
Sign In or Register to comment.