Shop OBEX P1 Docs P2 Docs Learn Events
Managing a shared resource without locks — Parallax Forums

Managing a shared resource without locks

bobr_bobr_ Posts: 17
edited 2008-11-07 22:06 in Propeller 1
I've got 3 or more sensor cogs (CNe,HNe,SNe) that want to
send commands and data to a common worker cog (HNf)
whose job it is to manage a shared resource
like an SDcard or eeProm...

The commands are simple get/put to the resource
readL(DEVaddr, HUBloc);
writeL(DEVaddr, HUBloc);
I may add more commands in the future. Each sender will
wait for a previous command to complete or an error before sending again.

To communicate, I will need to pass 2 longs and a command (minimum).
I don't care if the command is a long or a byte. There could be many ways to error
but, more or less, only one way to signal success [noparse][[/noparse]typically by zeroing the command byte].
CNe (See no evil), HNe (Hear no evil) and SNe (Speak no evil)
each separately 'own' some hub memory as does HNf.


Can communications with HNf
be done without involving a Lock?

It should work with more than three callers too...
I do not want to have to change the code to HNf every time I add a sensor.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-bobR

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-11-06 17:03
    As long as the cogs are communicating with HNf via different hub memory locations, I see no reason to involve locks. These are only necessary when accessing a shared resource without a common administrator, such as your HNf. The locks themselves provide a low-level administration, which is not needed in your case. You do need to take care about the order in which you send data to the hub: write your arguments first, then the command.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Just a few PropSTICK Kit bare PCBs left!
  • bobr_bobr_ Posts: 17
    edited 2008-11-06 19:21
    Ok so can I generalize this into some kind of rule?

    I am asking because I do see counter examples of this
    in which the recipient owns the memory locations
    and I guess those are limited to only single-sender situations.

    For multiple senders -
    When N cogs are sending (multi-long) messages to a single receiver,
    each must own their own buffer for the message
    and write the command or 'do-this' portion last
    so that arguments are not fetched before they are ready.

    Can this be made to work when the recipient publishes
    only a single 'mailbox' in which to receive pointers to messages?

    Is there an example of this in the library?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -bobR
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-11-06 20:06
    If the recipient receives messages from multiple senders using a single mailbox, you will need to use locks. This is because each sender will need to "own" the mailbox while that sender is using it. Otherwise, collisions with other senders, vying for the same resource, can occur.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Just a few PropSTICK Kit bare PCBs left!
  • bobr_bobr_ Posts: 17
    edited 2008-11-07 04:36
    Ok now if I were writing a library routine in C
    to code up the [noparse][[/noparse]CHS]Ne side to do this, it would look like:

    /* given address of mailbox in hub memory, use stack as unique buffer
     * to talk to another waiting cog and get a reply 
     * Note: 
     *   -doesnt need to know cog id's
     *   -any number of cogs can send to single recipient
     */
    long  sendrecv (long **mbx, long cmd, long arg1, long arg2) {
      long *msg = &cmd;  // assumes stack grows downward as longs
       
      do {
        while (*mbx)          // look at long at mbx addy  (not byte)
            { ; }                // wait till not busy w someone elses buffer
        *mbx = msg;      // multiple cogs may do this, last one wins
        while (msg == *mbx)   // check if you were the last one
            { ; }                      // maybe blink LED here or check own inbox
        } while ( msg[noparse][[/noparse]0] ) ;    // check command completed
      return msg[noparse][[/noparse]1 ] ;         // error code or count of bytes transferred
    } 
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -bobR
  • Dennis FerronDennis Ferron Posts: 480
    edited 2008-11-07 14:36
    There's an article on Dr. Dobb's Journal about lock-free code: www.ddj.com/cpp/210600279

    It isn't exactly the same problem you're working on, but it might give you some ideas. The basic concept for lock-free concurrency is summed up in this quote: "The producer and consumer always work in separate parts of the underlying list, so that their work won't conflict."

    Of course, the article assumes only 1 producer and 1 consumer, and in fact the article is about how *hard* or *dangerous* lock free code is - but, the fascinating part IMO is that it is at least possible to write code that is both lock-free and works correctly, at least for some cases. I just thought that was very interesting because I've read a lot about multithreading elsewhere, and never did anyone mention the kind of lock-free techniques talked about in the article.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2008-11-07 16:12
    A good example of lock-free code where two processes access the same memory is an input queue, such as you'd find in a serial receiver. The producer is the (background) process that reads the input pin, deserializes the data stream, and writes the characters to the input queue. The consumer is the (foreground) process that retrieves characters from the queue when requested by a calling program. Both processes have access to the queue memory, along with both the enqueue and dequeue pointers. But here's the important part: the producer can only write to the queue and to the enqueue pointer and read the dequeue pointer; the consumer can only read the queue and enqueue pointer and write to the dequeue pointer. So none of the memory is written to by both processes, and they stay out of each other's way without requiring locks. But, the sequence in which data are written to this memory is very important, too. For example, the producer must always write data to the queue before changing the enqueue pointer. Likewise, the consumer must read data from the queue before changing the dequeue pointer.

    -Phil

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Just a few PropSTICK Kit bare PCBs left!
  • bobr_bobr_ Posts: 17
    edited 2008-11-07 22:06
    Yes I saw threads about producer/consumer lists posted here a while back.

    I want to stay focused on M:1, 1:N and M:N solutions if possible. I have
    examples of 1:1 messaging solutions provided by Andre in the hx512 code.

    My hope is to come up with a verified standard in both C and prop asm
    that many here can use/reuse. I will reuse them too for some of the
    even bigger things I have planned to release to this forum.

    I will start with simple stuff like datalogging from multiple sensors
    because that more or less parallels the problem set of an operating
    system which has to service requests from many concurrent applications.

    I had constrained the HNf module to not have to be extended every time
    a new sensor was added because [noparse][[/noparse]in an operating system] you probably
    don't want to have the device drivers to have knowledge
    of every application you plan to run.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -bobR
Sign In or Register to comment.