tsc_Lock

tsc_Lock is a macro used to lock objects against concurrent updates by different threads.  Its use and operation is very similar to the .NET (C#) Lock statement, and is likewise very simple to use.

It provides a platform-independent means for locking objects.

Macro

tsc_Lock (tsc_Object& objectToLock)

{

    ...Code operating upon the locked object...

}

The objectToLock must be a reference to the object to be locked (*this and similar constructions are permissable).  Note that the lock object only has to be some common object to which all locks of the same object refer, and not necessarily the object which is being protected, although using the protected object itself is generally easier to understand.  

The object must be a tsc_Object, or some subclass of it.  When locking a simple type (say, a double), a bare tsc_Object will suffice.  For example:

double Longitude;

tsc_Object  LongitudeLock;

...

void UpdateLongitudeSafely (double newValue)

{

   tsc_Lock (LongitudeLock)

   {

       Longitude = newValue;

   }

}


double GetLongitude() const

{

   tsc_Lock (LongitudeLock)

   {

       return Longitude;

   }

}

Operation

The macro first creates an internal lock object for objectToLock if one does not already exist, and then increments the lock count.  If another thread already has a lock on this object then the tsc_Lock will block the requesting thread until the lock is released by the other thread.  If more than one thread is waiting for the lock to be released, the order in which the threads will obtain the lock is unpredictable, but is generally fair.

The lock is released when program flow passes out of the code block, at the '}' statement.  Locks may be nested for the purpose of locking multiple objects.  It is permissible to break out of the code block using a break or return statement;  either will release the lock.  

It is not normally necessary to lock Survey Core API objects since they are already protected with their own locking system.  Some classes are not however; these include tsc_String, tsc_List, tsc_ArrayOfInt, tsc_XCodeList, tsc_Font, tsc_XmlElement, tsc_Size, tsc_StringList, and all coordinate types such as tsc_Grid.  In addition, many tsc objects are passed by value, so locking the object only locks the passed reference and not the original.  Note that tsc_String, being immutable, does not generally require locking - except against deletion.

Important: A number of common mistakes are made in regard to locking, some of which are documented here:

Notes

Because tsc_Lock is a macro, compiler error messages and debugging can behave a little strangely.  This is "normal".  The macro generates a for statement which defines a tsc_LockInstance, and executes the loop once only.  For this to work the /Za or /Zc compiler option must not be used (ie, compilation must be for-conformant).  Alternatively, non-conformant compiler options can be used if the tsc_LockInstance class is used instead of the tsc_Lock macro.

If an exception occurs in the block, the lock will only be released if the plugin was built with an appropriate compiler /EH option that implements unwinding semantics.

The object being protected (ie, that is specified in the tsc_Lock macro parameter) must continue to exist while it is locked.  If the object is destroyed while a lock to it is held, Survey Core will terminate with an error.

Internally, the tsc_LockInstance class (and therefore tsc_Lock also) makes use of the Windows API Critical Section functions. See the Microsoft Critical Section Web Page for further details.

Example

This example wraps tsc_List and makes a few of the methods thread-safe.  More methods could be added in a similar vein.

class MyData : public tsc_Object 

public: 

    tsc_String SomeInfo; 

    int        SomeNumber; 

}; 


class MyList 

public: 

    void Add (MyData* item) 

    { 

        tsc_Lock (list) 

        { 

            list.Append (item); 

        } 

    } 


    MyData* operator[] (int index) 

    { 

        tsc_Lock (list) 

        { 

            return (MyData*)list[index]; 

        } 

    } 


    void ModifySomething (int index) 

    { 

        tsc_Lock (list)   // Prevent other concurrent changes to the list 

        { 

            MyData* item = list[index]; 


            tsc_Lock (*item)             // Prevent other concurrent changes to the item. 

            { 

                SomeNumber = SomeInfo.Length();   // This is just a random and useless example operation. 

            } 

        } 

    }  


private: 

    tsc_List list; 

};