Skip to content

Simple C++ library for CONCURRENT READ and EXCLUSIVE WRITE access to a class or its members

License

CodeChex/ConcurrentLock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ConcurrentLock

Simple C++ library for CONCURRENT READ and EXCLUSIVE WRITE access to a class or its members


These SyncObjects allow true multi-threaded 
    CONCURRENT READ and EXCLUSIVE WRITE access to any class.

HISTORY:
-------

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 ementation 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... 

Hence, "ConcurrentSemaphore" and "ConcurrentLock" !

MACRO DEFINITIONS:
-----------------

*** Use of these macros is highly recommended because if you decide to revert back
    to using critical sections, then "#define USE_CRITSECT" before including this
    header, and the macros translate to use CCriticalSection / CSingleLock !!!

MACRO USAGE:
-----------

In your class, insert this line prefereably at the top of bottom of the class:
    (because it declares a scope of public)

    DECLARE_LOCKABLE;
 
to obtain a resource for ANY LOCKABLE class object:

    DECLARE_RESOURCE_LOCK(object,"any debug string");

to access a resource in a NON-DESTRUCTIVE manner

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

to access a resource in a DESTRUCTIVE manner

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

to relinqish access to the resource

    RESOURCE_UNLOCK(object);

NATIVE USAGE:
------------

In your class, insert these lines:

    public: // use "private" or "protected" if no other classes lock this resource
        ConcurrentSemaphore mCritSec;
    
to obtain a resource for the CURRENT class object:

    ConcurrentLock mSingleLock(&mCritSec);

to obtain a resource for ANOTHER class object:

    ConcurrentLock mSingleLock(&(object->mCritSec)); // only if variable is declared public

to access a resource in a NON-DESTRUCTIVE manner

    if(mSingleLock.Lock(access_shared,timeout) != WAIT_OBJECT_0)
    {
        // log some error
    }

to access a resource in a DESTRUCTIVE manner

    if(mSingleLock.Lock(access_thread_exclusive,timeout) != WAIT_OBJECT_0)
    {
        // log some error
    }

to relinqish access to the resource

    mSingleLock.UnLock();

TIPS:   to be most efficient,
----

    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

EXAMPLE:
-------

    //
    // 1) start with read access immediately before referencing a member
    //
    DECLARE_RESOURCE_LOCK(this,"example function");
    if( !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( !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
    //
    RESOURCE_UNLOCK(this);
    return S_OK;

HOW does it work?
----------------

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.

(Draw it on paper, it works.)

Every use of urinal represents READONLY access, and every use of the toilet represents
WRITEABLE access to the resource.

About

Simple C++ library for CONCURRENT READ and EXCLUSIVE WRITE access to a class or its members

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published