ConcurrentLock Description

    Until now, you were told that all class methods should access/update their members
    in a thread safe manner, so like the rest of us sheep, you created a member variable:

        CCriticalSection mCritSec;
        
    and wrapped every class method with:

        CSingleLock mSingleLock(&mCritSec);
        if(!mSingleLock.Lock(timeout))
        {
            // log some error
            return E_FAIL;
        }

    In a really complex multithreaded application, this causes EXCLUSIVE access to that
    class, even if the method does not update any member variables! This results in a
    class implementation that only allows SERIAL access, not really what was meant by
    the multithreading capabilities of OO design.
    
    Indeed, locking is needed for read-only methods that use items such as STL iterators, 
    whereas a call to a destructive class method from another thread may render that 
    iterator invalid.

    What was really needed was a way for you to allow multiple threads CONCURRENT READ
    access to the class, and EXCLUSIVE WRITE... 
    In your class definition, insert this line prefereably at the top of bottom of the class:
        (because it declares a scope of public)

        CONCUR_DECLARE_LOCKABLE;

    To obtain a resource for ANY LOCKABLE class object in you application code,

        CONCUR_DECLARE_RESOURCE_LOCK(object,"any debug string");

    to access a resource in a NON-DESTRUCTIVE manner

        if( !CONCUR_RESOURCE_READ_LOCK(object,timeout) )
        {
            // log some error
        }

    to access a resource in a DESTRUCTIVE manner

        if( !CONCUR_RESOURCE_WRITE_LOCK(object,timeout) )
        {
            // log some error
        }

    to relinqish access to the resource

        CONCUR_RESOURCE_UNLOCK(object);

    To allow locking of specific elements in a class, insert this line prefereably after the element definition:

        CONCUR_DECLARE_ELEMENT_LOCKABLE(element-name);
    
To obtain a resource for ANY LOCKABLE class object in you application code,

        CONCUR_DECLARE_ELEMENT_LOCK(object,element-name,"any debug string");

    to access a resource in a NON-DESTRUCTIVE manner

        if( !CONCUR_ELEMENT_READ_LOCK(object,element-name,timeout) )
        {
            // log some error
        }

    to access a resource in a DESTRUCTIVE manner

        if( !CONCUR_ELEMENT_WRITE_LOCK(object,element-name,timeout) )
        {
            // log some error
        }

    to relinqish access to the resource

        CONCUR_ELEMENT_UNLOCK(object,element-name);

        1) start with read access immediately before referencing a member, 
        2) ask for write access immediately before updating member,
        3) relinquish control when resource is no longer referenced
   ConcurrentLock.h comes with the following macros already defined:

	// for class locking
	zzz_DECLARE_LOCKABLE                          
	zzz_DECLARE_RESOURCE_LOCK(o,desc)   
	zzz_RESOURCE_READ_LOCK(o,timeout)
	zzz_RESOURCE_WRITE_LOCK(o,timeout)
	zzz_RESOURCE_DELETE_LOCK(o,timeout)
	zzz_RESOURCE_UNLOCK(o)                       
	// for element locking
	zzz_DECLARE_ELEMENT_LOCKABLE(e)         
	zzz_DECLARE_ELEMENT_LOCK(o,e,desc)     
	zzz_ELEMENT_READ_LOCK(o,e,timeout)
	zzz_ELEMENT_WRITE_LOCK(o,e,timeout)
	zzz_ELEMENT_DELETE_LOCK(o,e,timeout)
	zzz_ELEMENT_UNLOCK(o,e)                    

   where zzz stands for any of the following locking mechanisms:

	CONCUR    ConcurrentLock
	MUTEX     Standard Mutex
	CRITSECT  Critical Section
	NOOP      No locking

   This makes it easier on the application developer to create logic that can be tweaked
   later to accommodate the appropriate locking mechanism.
example.hpp

#include <list> #include <iterator> typedef unsigned long _t_node; typedef list<_t_node*> _t_list; class example { CONCUR_DECLARE_LOCKABLE; public: example(); ~example(); private: _t_list someList; public: DWORD findElement(_t_node* someElement); };

example.cpp DWORD example::findElement(_t_node* someElement) { // 1) start with read access immediately before referencing a member CONCUR_DECLARE_RESOURCE_LOCK(this,"example function"); if( !CONCUR_RESOURCE_READ_LOCK(this,timeout) ) { // log some error // don't forget to cleanup return E_FAIL; } some_iterator iter; for ( iter = someList.begin(); iter != someList.end(); iter ++ ) { if ( (*iter) == someElement ) { // 2) ask for write access immediately before updating member, if( !CONCUR_RESOURCE_WRITE_LOCK(this,timeout) ) { // log some error // don't forget to cleanup return E_FAIL; } someList.erase(iter); // !!! since list is modified, leave the loop !!! break; } } // 3) relinquish control when resource is no longer referenced CONCUR_RESOURCE_UNLOCK(this); return S_OK; }
    Imagine, if you will, that the ladies' room in your office is broken, and everyone
    must wait in line for using the men's room (which has many urinals, but only one
    stall with a toilet).

    Many men can use the urinals at the same time, but if a woman wants to use the
    toilet, she must wait for all the men to finish their business and leave the room.

    Any remaining men in line must wait for any woman occupying the bathroom to finish 
    her business and leave the room.

    Since there is only one toilet, each woman must wait for the room to be empty before
    entering.

    There are some special conditions though:

      If a man wants to use the urinal, and the only people in there are family members
      (resources from the same thread), he can enter.

      If a man wants to use the toilet, he must go back in line (although he is allowed to
      be placed in front) and wait for everyone else to leave.

    Every use of urinal represents READONLY access, and every use of the toilet represents
    WRITEABLE access to the resource.
    
    (Draw it on paper, it works.)


SourceForge Logo