Re: Object locking

From: Dmitry A. Kazakov (mailbox_at_dmitry-kazakov.de)
Date: 12/11/04


Date: Sat, 11 Dec 2004 09:53:54 +0100

On Thu, 9 Dec 2004 21:02:37 -0600, Wavemaker wrote:

> "Dave" wrote:
>> Wavemaker wrote:
>>> I was wondering if there is a design pattern or idiom for locking
>>> access to an object. What I need is a way for an object to gain
>>> temporary ownership of another object so that it can read and write
>>> to the object without worrying about it being changed elsewhere in
>>> the application.
>>>
>>> This sounds like a thread-safety issue, and maybe it is, but I've
>>> looked at monitors, semaphores, etc., and I think I need something at
>>> a higher level of abstraction. Ownership of the object needs to
>>> extend across several reads and writes before the object is released.
>>> I'm not sure how to guarantee that using standard threading
>>> synchronization.
>>
>> The first thing that spings to mind is a per-object mutex. Then add
>> lock/unlock methods that delegate to the mutex (If you want to be
>> really cute in C++, you could have just a lock method that returns an
>> object that you can use for RAII). Clients can then lock the object
>> before they start using the object.
>
> That makes sense.

See below.

>> That assumes that all clients cooperate.
>
> There's the rub.
>
> I could adapt your idea to make it transparent to clients, I think. Only
> classes in my library can call the Lock/Unlock methods on the object it
> needs ownership of. These methods would be internal to the library (I'm
> programming in C#, the closest C++ parallel to internal access is
> declaring a class or method a friend), so they would be hidden from
> clients. A boolean variable would be set in the Lock/Unlock methods
> indicating whether the object is locked.
>
> At the beginning of each method would be a guard. Something like this:
>
> if(libraryLocked)
> m1.WaitOne();
>
> where m1 is a mutex object.

In C++ the standard pattern for that is:

class Resource // Here you derive mutexes from
{
public :
   void Seize () = 0;
   void Release () = 0;
};

class Locker // Mutex holder, exception safe
{
   Locker (Resource * What) : Own (What) { Own->Seize (); }
   virtual ~Locker () { Own->Release (); }
protected :
   Resource * Own;
};

Now in a method:

class SafeObject
{
public :
   int Foo ()
   {
      Locker Lock (&Mutex);

      ... // Critical part protected by the Mutex

      return Something;
   }
protected :
   SomeResource Mutex;
};

> Clients could access the object and only encounter the mutex when the
> object has been internally locked by the library.

Note that on a single processor machine you can have one mutex for all
objects. Note also that with more than two mutexes (=objects, if you have
per-object mutexes) the schemata above promptly leads to deadlocks:

1. Safe object A calls something of safe B which calls something of A. If
mutexes are not re-entrant (from the same thread), this will hang.

2. Thread X calls to A which then calls B. Thread Y calls B which then
calls A. X and Y may block each other.

This is why Ada implements that as a language primitive, which excludes
possibility of deadlocks like above.

[Ada Ravenscar profile excludes any deadlocks, but it is another story]

> A second mutex could be used to make the object thread safe so that if a
> client is using the object, the library will have to wait until it's
> done before locking the object. This would make the object thread safe
> for clients in general but give the library special locking ability so
> that it can lock an object indefinitely if it needs to.
>
> Does this make sense?

It is common in C++. You will probably wish to create different sorts of
mutexes: Read-Only, Read-Write to provide a more efficient access for
immutable methods as Ada does. This is not trivial, but possible. We have
implemented that in our commercial library.

Keep also in mind that this model (passive protected objects) is not
suitable for many cases. In Ada people experimented much with different
mechanisms. The common agreement is that in many cases rendezvous are
preferable (provide simpler and clearer design).

A pattern for rendezvous in C++ is more complex. The object (~monitor) has
a thread assigned to it. It also holds a queue of requests. The thread
inspects that queue for a request. Each request is derived from some
abstract request class with the method DoIt. The thread calls DoIt when it
fetches the request from the queue. A caller, another thread, that wishes a
rendezvous, derives from the abstract request class and provides an
implementation for DoIt. It also may add some data necessary for DoIt
(rendezvous parameters). The idea that at the time when DoIt is called both
threads are synchronized and it is safe to access methods of the monitor
object from DoIt. This is three or so lines in Ada. (:-)) Also you will
have a lot of problems with handling exceptions propagated out of
rendezvous, because unlike Ada, C++ does not have any support for routing
exceptions from one thread to another.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


Relevant Pages

  • Re: Object locking
    ... > thread inspects that queue for a request. ... > some abstract request class with the method DoIt. ... I also think I have an Ada book handy around here somewhere... ... > any support for routing exceptions from one thread to another. ...
    (comp.object)
  • Re: localhost is all that will work
    ... What is the ServerBindings configured for this website? ... Bad Request message that I receive here as well. ... > - The client opens a connection to the webserver (works, ... > If this happens for all your clients on the Internet, ...
    (microsoft.public.inetserver.iis)
  • Re: Coworkers for myLinux project
    ... On a request of Michael Tobler, the first poster, I have translated ... Cyrus IMAPD mailbox, a sendmail Alias in LDAP, and the home directory ... there is no package which allows Linux clients to authenticate ... certificates for Apache, sendmail, Cyrus IMAPD and OpenLDAP, and it ...
    (alt.os.linux)
  • RE: process starvation with 2.6 scheduler
    ... The network traffic is of request response type. ... The netperf clients run on an external box, ... A client sends request to a server, ... With an ICE connected to the Palladium (emulator) I have dumped the kernel data structures of the starved process and the active process. ...
    (Linux-Kernel)
  • Re: localhost is all that will work
    ... The client opens a connection to the webserver (works, ... The webserver answers the request, ... If this happens for all your clients on the Internet, ... accessing the website from different parts of the ...
    (microsoft.public.inetserver.iis)