Boost logo

Boost :

From: Vesa Karvonen (vesa.karvonen_at_[hidden])
Date: 2001-07-20 13:06:37


From: "Kevlin Henney" <kevlin_at_[hidden]>
> > From: williamkempf_at_[hidden]
> [...]
> >My
> >problem here, though, is that the nebulous complaint you have against
> >the shorter name "lock" is going to exist with "scoped_lock" as
> >well. There's a conceptual difference between this sort of "lock"
> >and a "spinlock".
>
> Have you considered using the name 'locker'? This is the one I tend to
> use: a locker (the scoped locking object) locks (calls the member
> function) a lock (the mutex).

I'm not native English, but 'locker' sounds like the thing that you use for
storing objects. ;-)

Anyway, this and related idioms have many names such as:
- 'guard' (http://www.cuj.com/experts/1812/alexandr.htm?topic=experts)
- 'sentry' (standard IOS library, TCPL)
- 'proxy' (GoF)
- ...

So, I would propose simply appending lock with one of the above names. For
example 'lock_guard'.

Personally, I have been using a class template 'locked', which is basically a
handle to a lockable or guarded object/resource. It is similar to, but not the
same as, the LockingPtr presented in
http://www.cuj.com/experts/1902/alexandr.htm?topic=experts. The main
implementation difference is that lockable only stores a single pointer.

The following mainly describes guarded, which is a simple template I developed
a long time ago. I haven't noticed the same concept anywhere before, but I
suspect that it can be found in many C++ libraries for concurrent programming.
(I have not been doing concurrent programming for some time now, so I haven't
been looking into concurrent programming libraries.) The reason why I'm
describing guarded here is because I believe it can be quite useful, and I
believe it should not be ignored while implementing locking components, and
also because it demonstrates a solution in which the type of a resource
basically changes when it is acquired (in this case from guarded<interface> to
interface). I'm also interested to know of other implementations and
refinements of this concept.

A lockable object basically has lock() and unlock() member functions (with
certain required semantics). The guarded class template is parameterized by a
lockable type and an arbitrary UDT type. My current implementation does not
work with non-derivable types, but this situation could be easily improved by
using a bit of metaprogramming. The guarded template makes it easy to add
synchronization to an existing type:

    template
    < class interface
    , class lockable = ...default lockable implementation...
>
    struct guarded
      // WARNING: The initialization order is significant!
      : private lockable
      , private interface
    { // ...

      // In locked, the interface is optional and lockable is mandatory,
      // while in guarded, the interface is mandatory and lockable is
      // optional.
      typedef locked<lockable,interface> handle;

      // Make locked a friend, so that it can cast the pointer to the
      // interface/lockable as needed.
      friend struct locked<lockable,interface>;

      // ...
    };

Now it is easy to ensure that an object can not be accessed without locking:

    typedef guarded< std::map<X,Y> > guarded_map_type;
    guarded_map_type guarded_map;

It is now necessary to use either the handle type or the sync() function to
access the object:

    { guarded_map_type::handle guarded_map_handle(guarded_map);

      // The operator-> returns a pointer to the interface.
      guarded_map_handle->find(an_x);
    }

    // sync returns a handle
    sync(guarded_map)->find(an_x);

The guarded class template can also be used to guard an interface:

    struct interface
    { // ...
      virtual r operation(p)=0;
      // ...
    };

by deriving from a guarded<interface>. You can then write code that uses
guarded<interface> references, for example.


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