Boost logo

Boost :

From: Eric D Crahen (crahen_at_[hidden])
Date: 2002-05-17 15:07:17


> This doesn't transfer ownership of a lock across scopes. It overlaps
> locks on two different mutexes

As far as the need to transfer ownership goes, I'm not entirely convinced
there is a real need for this. The only use this seems to have is to
create a wrapper that provides serialized access to each function on some
object right? You could use it to easily create a thread-safe list or
vector, but this is the only use I can see for that. Using it for classes
designed to be used with multiple threads already (ones manging thier own
synchronization) would only lead to a bunch of other problems. (Ones using
condition variables internally, for example)

As soon as you used this with a function that is intended to block the
caller until some other function releases it you've got a great potential
for deadlock. If your authoring a new class, it could be a composite or
contain methods that are immutable and synchronizing every function would
be more of a drawback.

Now if you design a program to use say a vector, shouldn't it be the
responsibility of the programmer to correctly synchronize the code using it?
Typically, these non-thread safe objects would end up being a member of some
class, and the person writting that class could easily synchronize access
to that object. If you do this, there really isn't that much of a need
for a wrapper. Plus, you get the ability to include a much finer grain
synchronization for that object - if it has two large vectors, maybe you'd
want to have a separate lock for each, all of this is encapsulated in the
class your writting so there is no way for an external proxy to deal with
that w/o breaking that encapsulation. In general, I don't thinks its a
good idea to remove the synchronization controls too far from the data
they are synchronizing, and if you're sharing one lock so much that you
really need to move its ownership it could be an indication you need to
use another lock.

If you want to create a wrapper that would allow any arbitrary object to
have some object level lock placed on it. Why not create a true wrapper
for it, one that actually wraps the object rather than trying to
distribute a wrapper among a set of pointers. Something like this,

template <class T, class LockType>
class GuardedObject : private NonCopyable {

  T* _object;
  LockType _lock;

  friend class Proxy;
  class Proxy : public Guard<T> {

    T* _ptr;

    Proxy(T* ptr, LockType& lock) : Guard<T>(lock), _ptr(ptr) {}

    T* operator->() {
      return _ptr;
    }

  }

public:

  GuardedObject(T* object) : _object(object) {}
  ~GuardedObject() { delete _object; }

  Proxy operator->() {
    return Proxy(_object, _lock);
  }

};

{
   // Create a safe vector
   GuardedObject<vector<int> > safe_vector(new vector<int>());

}

The ownership issue dissappears and the lock lives just as long as the
thing it guards. You can use auto_ptr, smart_ptrs or whatever is
appropriate if you want to share this object.

> (where you use LockType Boost.Threads uses Mutex... locks are not
> mutexes but are locks on a mutex).

I look at a mutex as a kind of lock. Just like the lock on your door
or a safe, you lock and unlock those things to control access to
something. They're built up of other things that really do the
locking and unlocking (tumbers in padlocks, or lower level synchonization
primatives in mutexes) but they effectively endup acting as a lock, so I
call em' that. Maybe its my OO nature :). Do most people see it that way,
maybe its just me?

Also, I think there should be explict lock/unlock functions provided. Its
enough to provide constructs like the ScopedPtr or Guards that make thier
use optional. I'd rather not impose restrictions if they don't add
anything to the functionality or thread model you want to use. It leaves
things more flexible.

- 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