Shop OBEX P1 Docs P2 Docs Learn Events
Locks — Parallax Forums

Locks

bob kruggelbob kruggel Posts: 50
edited 2010-06-29 18:35 in Propeller 1
I am a newbee who can't understand locks. I think I understand what they are supposed to do (prevent the corruption of values stored in HUB memory), but I don't see how they accomplish this feat. What defines the locations in HUB memory that I'm trying to protect? Here is a quote from Propeller Manual, page 123:

"
Objects needing a lock to manage a user-defined, mutually exclusive resource should check out a lock using [font=Parallax,Parallax size=2][font=Parallax,Parallax size=2]LOCKNEW [/font][/font]and save the ID returned, we’ll call it [font=Parallax,Parallax size=2][font=Parallax,Parallax size=2]SemID [/font][/font]here. Only one cog should check out this lock. The cog that checked out the lock must communicate [font=Parallax,Parallax size=2][font=Parallax,Parallax size=2]SemID [/font][/font]to all other cogs that will use the resource. "

This sounds like going to the front desk and requesting a lock, knowing which locker I want·to lock, but not knowing how to install the lock.

Can someone help me to understand the LOCK mechanism without me having to understand a complex assembly program?

Comments

  • wjsteelewjsteele Posts: 697
    edited 2010-06-27 16:58
    To be honest with you, Bob, I've never once needed to use a lock! The only thing I ever ran into was with a GPS driver and a display, where I was reading the value more than once to update the display in a couple places. The value would change between reads, so I simply loaded that value into a variable instead.

    It seems to me that it has always been easier to code around the lock issue then to actually implement it.

    Bill
  • TimmooreTimmoore Posts: 1,031
    edited 2010-06-27 17:13
    Look at a lock as a gate, you choose what to put behind the gate and then you must make sure all access to that item goes through the gate.

    an example, you pick a variable, read and write is go to through a lock, but you must write the code and check that every time you read or write the variable to get and release the lock. There are no checks to make sure you get the lock, if you forget to get the lock in one case, you can still read/write the variable.

    As why you need a lock. It is normally more than 1 variable. e.g. you have 2 variables day/month. One cog is writing to them, another cog is reading. The write updates the day from 31 to 1, then updates the month from 3 to 4, i.e. changes the date from 3/31 to 4/1. but the reader reads the day when it is 1 and reads the month before the write updated it, now it read a date of 3/1.

    There are ways round it in this case, the reader could read day, month, day and check that day hasn't changed. but there are times with lots of variables locks are just easier.

    Another example is you have multiple cogs writing to a serial port driver, the characters will be interleaved on the serial port, which may be what you want. You also may want lines to be interleaved, so you want 1 cog to hold the serial port until it has finished the line and then the another cog can write to the serial port - locks make this easy.
  • heaterheater Posts: 3,370
    edited 2010-06-27 17:35
    Firstly some simple concepts about process synchronization and data sharing.

    As wjsteele says in many case one does not need locks to share data between processes. For example:

    1. Process A generates a single value to be read by one or more other processes B, C, D...
    Let's say A generates "temperature" as a single byte, word or long. Then any reads by B, C...will pick up the last value written by A with no error. Note that B,C,D... will not necessarily all have the same temperature value at any instant in time. Depends how they interleave with the writes. Generally this is not an issue.

    2. A single process A generates a complicated data, let's say "date" with "day", "month", "year" components. As you see it is possible for a process B to read "day", then "month", then process A could write "year", then B reads the "year" and now has an incorrect date value.

    This data corruption can be avoided if there is only one writer and one reader (A and B) by adding a flag to the data. Process A only writes the components of "date" when the flag is zero.
    Then it's sets the flag to 1. Meanwhile process B only reads the date components when the flag is one and then sets the flag to zero. And so on.

    I think you can see this fixes the corruption problem for A and B.

    3. It is possible to use buffers of streams of bytes/words/longs between two processes. This is done in such a way that one process writes data to the "head" position if the buffer and the other reads data from the "tail" position. The buffer being cyclical in that both head and tail pointers wrap around to the beginning of the buffer space when they reach the end.
    Have a look in the FullDuplexSerial object for a good example of this in the receive and transmit buffers.

    So when do you need locks?

    Basically when you have a complicated data structure to share between processes and there is more than one reader.

    The first thing to note is that the lock mechanism does not know anything about the data you want to lock. Basically it is a way to hold up progress of one or more threads/processes while another is doing "something" and then allowing those threads/processes to continue when the "something" is done.

    As you say you have the lock but where is the locker?

    In the Propeller manual we see this (Page 128):

    PUB ReadResource | Idx
      repeat until not lockset(SemID) 'wait until we lock the resource
      repeat Idx from 0 to 9          'read all 10 longs of resource
        LocalData[noparse][[/noparse] Idx ] := long[noparse][[/noparse] Idx ]
      lockclr(SemID)                  'unlock the resource
    
    PUB WriteResource | Idx
      repeat until not lockset(SemID) 'wait until we lock the resource
      repeat Idx from 0 to 9          'write all 10 longs to resource
        long[noparse][[/noparse] Idx ] := LocalData[noparse][[/noparse] Idx ]
      lockclr(SemID)                  'unlock the resource
    
    



    As we see the process that calls ReadResource gets stuck in a repeat loop in the first line "repeat until not lockset(SemID) 'wait until we lock the resource"
    It is stuck there until another process calls "lockclr(SemID) " with the same SemID.

    This is symmetrical, any process trying to write data calls "WriteResource" and gets stuck in the first line there, same as for ReadResource.

    Now you can see that the data that is protected by the lock is independently defined by your program. In this example the shared data is whatever long[noparse][[/noparse] Idx ] happens to be.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    For me, the past is not over yet.
  • Cluso99Cluso99 Posts: 18,069
    edited 2010-06-27 23:07
    Never used locks. Most probably you won't either. Give them a miss for now anyway until you understand the rest.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Links to other interesting threads:

    · Home of the MultiBladeProps: TriBlade,·RamBlade,·SixBlade, website
    · Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
    · Prop Tools under Development or Completed (Index)
    · Emulators: CPUs Z80 etc; Micros Altair etc;· Terminals·VT100 etc; (Index) ZiCog (Z80) , MoCog (6809)·
    · Prop OS: SphinxOS·, PropDos , PropCmd··· Search the Propeller forums·(uses advanced Google search)
    My cruising website is: ·www.bluemagic.biz·· MultiBlade Props: www.cluso.bluemagic.biz
  • bob kruggelbob kruggel Posts: 50
    edited 2010-06-28 22:21
    Clusso99 and others,

    I will take your advice and pretend LOCK does not exist.

    Bob
  • RossHRossH Posts: 5,519
    edited 2010-06-28 23:12
    Hi Bob,

    That's not such a good idea. True, you don't need locks when doing simple tasks with the Prop, and some users will never need them at all - but they are very useful for multi-cog programs. For example (as mentioned by heater) they can be used to prevent data corruption when complex data structures need to be accessed by multiple cogs.

    Locks are the Propeller equivalent of a "test and set" instruction. For more information on why a "test and set" (or alternatively a "read modify write") instruction is so vital in solving concurrency issues, see en.wikipedia.org/wiki/Test-and-set).

    As more and more sophisticated programs are created for the Prop, locks will become more and more important. For instance, all the current attempts to create a self-contained operating system for the Prop are likely to (eventually) need locks to do resource managment.

    I would advise that you at least read the general description of locks and then try and remember that they are available - you'll know when you need them.

    Ross.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Catalina - a FREE C compiler for the Propeller - see Catalina
  • heaterheater Posts: 3,370
    edited 2010-06-29 00:31
    Sound advice Ross.

    Perhaps while not actually planning to use locks, it might me a precaution to read around and think about why you may need them in some circumstances.

    You might find yourself creating such a multi-threaded program without locks that actually does need them, without realizing it. When it starts to fail at random, usually when it's in the finished product installed in the field, you will then find yourself spending many hours, days, nights desperately trying to reproduce the failures so you can fix it. Eventually you might see that it really does need locks and see why.

    An awareness of the issues can save a lot of frustration. These faults can be elusive.

    This advice comes from having been in such a situation with a multi-processor system. The software was failing even though it used locks. Eventually I found out from the hardware designers that "Oh, that version of the CPU cards has no locks implemented"....

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    For me, the past is not over yet.
  • ericballericball Posts: 774
    edited 2010-06-29 18:35
    You need locks if you have shared multi-element variables or shared variables used for read/modify/write by multiple writers.*

    Let's put some concrete examples together. The first action which needs to occur is a lock needs to be allocated via the LOCKNEW command. This returns the lock number (0-7) or -1 if all locks are in use. Because the lock is HUB logic it can be used by all COGs, and even shared between SPIN and PASM code. They just need to use the same lock number.

    When a piece of code needs access to the shared variable, it first does a REPEAT WHILE LOCKSET( lock number ) which waits until it gets the lock. The code can then safely do what it needs to do with the shared variable. Then, once it no longer requires access, it does a LOCKCLR( lock number ).

    In the rare case where code dynamically allocates and deallocates locks, the LOCKRET command is used to release a lock.

    * Also watch out for cases where a shared variable needs to be the same for a period of time. Easiest way to handle is to make a temporary read-only copy.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Composite NTSC sprite driver: Forum
    NTSC & PAL driver templates: ObEx Forum
    OnePinTVText driver: ObEx Forum
Sign In or Register to comment.