Subj : Re: Challenge: Multithreading & Synchronization To : comp.programming.threads From : Uenal Mutlu Date : Thu May 19 2005 01:39 am "doug" wrote > > "Uenal Mutlu" wrote in message > > > BTW, as can been this solution also has a simple and effient method to > > make any class with a standard constructor (incl. STL classes) thread > > safe. > > It's done like this: > > > > Lockable > myvec; > > > > To lock the object within a block of code and to unlock automatically > > when it goes out of scope one uses the following: > > Locker(myvec.m); > > > > or, one can lock and unlocks it also "manually" by: > > myvec.m.lock(); > > ... > > myvec.m.unlock(); > > > > > > > > > > "Uenal Mutlu" wrote > >> /* > >> Here's my solution. > >> > >> It applies the deadlock theorem, and by this it is deadlock-free, and > >> everything is also thread-safe. > >> > >> The right order of the locks is obviously important (it follows the > >> deadlock theorem). > >> > >> Performance is theoretically the fastest possible (unless someone > >> uses hand crafted assembler code). > >> > >> It's that simple by using recursive mutex and applying the deadlock > >> theorem. > >> > >> */ > >> > >> > >> //---------------------------------------------------------- > >> // recursive mutex: > >> class rmutex > >> { > >> public: > >> rmutex() { /*..*/ } > >> ~rmutex() { /*..*/ } > >> void lock() { /*..*/ } > >> void unlock() { /*..*/ } > >> }; > >> > >> //---------------------------------------------------------- > >> // helper classes: > >> > >> class Locker > >> { > >> public: > >> Locker(rmutex& Am) : m(Am) { m.lock(); } > >> ~Locker() { m.unlock(); } > >> private: > >> rmutex& m; > >> }; > >> > >> template class Lockable : public T > >> { public: rmutex m; }; // uses recursive mutex > >> > >> > >> //---------------------------------------------------------- > >> // The "Server" class: > >> > >> #include > >> > >> > >> struct SessionData > >> { > >> int iSessId; > >> //... > >> }; > >> > >> struct JobData > >> { > >> int iJobId; > >> //... > >> }; > >> > >> struct MiscData > >> { > >> int iMiscId; > >> //... > >> }; > >> > >> > >> class Server > >> { > >> public: > >> Server() > >> { > >> } > >> > >> ~Server() > >> { > >> } > >> > >> // assume all public functions can make calls to each other, > >> // and also to internal funcs > >> > >> void f1(void* Ap = 0) > >> { // uses vJD, vMD > >> Locker L1(vJD.m); // the order of the locks is important > >> Locker L2(vMD.m); > >> > >> //... > >> } > >> > >> void f2(void* Ap = 0) > >> { // uses vMD, vSD > >> Locker L1(vSD.m); > >> Locker L2(vMD.m); > >> > >> //... > >> } > >> > >> void f3(void* Ap = 0) > >> { // uses vSD, vMD > >> Locker L1(vSD.m); > >> Locker L2(vMD.m); > >> > >> //... > >> } > >> > >> void f4(void* Ap = 0) > >> { // uses vJD > >> Locker L(vJD.m); > >> > >> //... > >> } > >> > >> void f5(void* Ap = 0) > >> { // uses vMD, vJD, vSD > >> Locker L1(vSD.m); > >> Locker L2(vJD.m); > >> Locker L3(vMD.m); > >> > >> //... > >> } > >> > >> void f6(void* Ap = 0) > >> { // uses vMD > >> Locker L(vMD.m); > >> > >> //... > >> } > >> > >> void f7(void* Ap = 0) > >> { // uses vMD > >> Locker L(vMD.m); > >> > >> //... > >> } > >> > >> void f8(void* Ap = 0) > >> { // uses none > >> > >> //... > >> } > >> > >> void f9(void* Ap = 0) > >> { // uses vMD, vJD, vSD > >> Locker L1(vSD.m); > >> Locker L2(vJD.m); > >> Locker L3(vMD.m); > >> > >> //... > >> } > >> > >> > >> private: > >> > >> // assume all internal functions can make calls to each other. > >> > >> void if1(void* Ap = 0) > >> { // uses vSD, vJD > >> Locker L1(vSD.m); > >> Locker L2(vJD.m); > >> > >> //... > >> } > >> > >> void if2(void* Ap = 0) > >> { // uses vSD, vMD > >> Locker L1(vSD.m); > >> Locker L2(vMD.m); > >> > >> //... > >> } > >> > >> void if3(void* Ap = 0) > >> { // uses vJD, vMD, vSD > >> Locker L1(vSD.m); > >> Locker L2(vJD.m); > >> Locker L3(vMD.m); > >> > >> //... > >> } > >> > >> void if4(void* Ap = 0) > >> { // uses vMD > >> Locker L(vMD.m); > >> > >> //... > >> } > >> > >> void if5(void* Ap = 0) > >> { // uses vSD, vMD > >> Locker L1(vSD.m); > >> Locker L2(vMD.m); > >> > >> //... > >> } > >> > >> > >> private: > >> // shared data: accessed by most member functions: > >> // all data members are independent of each other > >> Lockable > vSD; > >> Lockable > vJD; > >> Lockable > vMD; > >> }; > >> > >> //---------------------------------------------------------- > >> Server gServer; // only 1 Server object exists in application > >> > >> extern bool IsPgmTerminating(); > >> > >> void* thread_proc(void* Ap) // all threads use the same one thread proc > >> { // no need to write code for this func > >> // client gets registered and unregistered in gServer by the calling > >> thread > >> > >> while (!IsPgmTerminating()) > >> { > >> // serve client by calling functions of gServer > >> } > >> > >> return 0; > >> } > >> > >> //---------------------------------------------------------- > > > > > You do know that an 'atomic counter' involves synchronisation, right? Sure, it is the most important thing to implement any mutex. I have even a class AtomicCounter (which is also used in the code I posted). Why do you ask? BTW, in other posting you say that you are disappointed of my mutex implementation, ie. it seems you think it can't work. But it works, it is stress tested and performance tested. It is at least twice faster than CriticalSection, even more the more threads are running. I'm using it successfully for more than 3 months now. Never mind, each his own. Have a g'day. .