|
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