Boost logo

Boost :

From: Eric D Crahen (crahen_at_[hidden])
Date: 2002-05-18 14:55:57


> To use an analogy that's been in use for decades to save human lives:
> At large construction sites where various electrical work will be going
> on, there is a shutoff which can be locked _open_ simply by putting a
> common padlock on it. The "hole" for the padlock to secure is large
> enough
> for many locks. When an electrician comes to work and needs the power
> killed, s/he simply adds his/her personal lock. When done, one removes
> one's own lock and leaves. The power cannot be restored until ALL the
> locks are gone.
> Certainly this model can be explained even to a newbie (especially if
> they've ever managed to be on the receiving end of an electrical shock)
> and
> is reliable, and considered foolproof enough to have lives depend on
> it. Modelling it for use in multi-threading/tasking seems natural to
> me.

I think this example is good, but the way you describe it makes things
easy to misunderstand. You describe the workers in this scenario (threads)
as all owning thier own lock, and coming along an placing it on the power
supply (resource). It makes it seem as though the threads are in charge of
the locks and not the resource. However, the real point of control is at
the resource - that must be synchronized (all the workers cannot put
locks on and off simultaneously for this to work - there would be a race
condition to quickly lock your padlock before another guy so you don't
get electrocuted).

The way this would normally be modeled in a real program would be for the
resource to own a 'lock' (I use the term lock here as a general
synchronization control, semaphore or mutex with a count in the resource,
a latch, or whatever - its not important, you can implement the locking
mechanism in a number of ways). This 'lock' would be used by the threads
that need to be coordinated. The workers really don't own a 'lock', thier
padlock is more of a token. They come to the resource and place thier
token (padlock) in it, the resource controls access to it - the resource
controls the lock, not the threads. (I'll use code to explain this more
clearly below, I'm a programmer - its easier to communicate that way :)

> Note this _may_ imply a requirement that more than one thread may need
> access to the same "hole" and may necessitate a _real_ semaphore (i.e.
> signalable by a thread other than it's "owner") rather than "thread safe"
> mutexes.

I think a semaphore would seem like a good choice at first just because it
keeps a count associated with it, but you would still need another
synchronization control to ensure that thigns work out. A semaphore would
work better alone if you wanted to say something like no more than 5 guys
can work here at any time.

> My above analogy to locking the electrical power off _may_ show that you
> not only need to transfer locks to functions, but, I believe, that you
> may need to either share or transfer them to other
> threads/processes/tasks/whatever. The analogy for transfer is, of
> course, handing the key to _your_ lock to someone else

If you approach the problem from a different angle this is not needed
at all. I think most problems can be dealt with like this. In my
expirience, if you find yourself in the situation where you begin to need
things as complicated as a sharing & transfer locks then its an indication
you may need to change your design.

// I'm typing this in, so there could be typo's & I'm using ZThreads
// constructs since I am most familiar with those - but I think its
// clear enough what is happening even if you aren't familiar with that
// library.

class PowerSupply {

  Mutex lock;
  int count;

public:

  PowerSupply() : count(0) { }

  void addPadlock() {

    Guard<Mutex> guard(lock);
    count++;

  }

  void removePadlock() {

    Guard<Mutex> guard(lock);
    count--;

  }

  void powerOn() {

    Guard<Mutex> guard(lock);
    if(count)
      throw please_dont_eletrocute_someone_today;

  }

};

With something along these lines, you don't need to transfer locks. You
have to place the focus of your synchronization in the correct place -
on the thing you want to control access to, not on the things that need
to be controlled. Trying to approach these kinds of problems in the later
way is only going to become more complicated and more error prone. I'm
not sure I see the benefit to doing something that way. (you'll still need
a single point of control to manage a set of locks correctly anyway,
except now since the resource doesn't control its own acces it is much
more difficult to deal with).

- Eric
http://www.cse.buffalo.edu/~crahen


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk