165d Subj : Challenge: Multithreading & Synchronization To : comp.programming.threads From : Uenal Mutlu Date : Wed May 18 2005 03:05 pm /* Here's a real-world synchronization challenge for accessing shared data from multiple threads (some call this "thread synchonization"): 1) A server application uses threads to serve clients. 2) It uses a thread per connected client. 3) There can be as many as 1000 or even more clients. 4) It uses a shared instance of a class to serve the clients. 5) The class has three data members: vSD, vJD, vMD (all independent of each other) 6) Each of the member functions can make calls to each other. 7) Each of the member functions can query and/or modify the data (see also 8). 8) The list of data objects queried or modified in each function is given in each function header (for further details see code skelleton below (class Server)). Your task is to make the class thread-safe by adding some synchronization objects and/or methods into appopriate locations of the class or outside the class. The given code skelleton uses C++, but you can use any pseudo-code. You can add more functions and/or data members if necessary, either inside the class or at global scope. You don't need to write the code which actually modifies the data; just the synchronization code only. You can make use of one or both of the following synchronization objects, or even use something totally different: mutex (standard mutex) rmutex (recursive mutex) both have the following methods: void lock(); void unlock(); bool trylock(); bool islocked(); int get_lock_count(); lock() is blocking, ie. it waits until it gets the lock whereas trylock returns immediately. Both mutex and rmutex are initialized at creation, so they don't have a random state. You can also make use of any helper classes or functions of your own; for such an example see the Locker and/or RLocker classes below. You can also repackage or put somewhere else the three shared data objects. Your solution can be pseudo-code or real code. Post your solution here. */ //---------------------------------------------------------- //---------------------------------------------------------- // non-recursive mutex: class mutex { public: mutex() { /*..*/ } ~mutex() { /*..*/ } void lock() { /*..*/ } void unlock() { /*..*/ } bool trylock() { /*..*/ } bool islocked() { /*..*/ } int get_lock_count() { /*..*/ } }; // recursive mutex: class rmutex : public mutex { public: rmutex() { /*..*/ } }; //---------------------------------------------------------- // These helper classes are optional: // intermediate helper for Locker and RLocker below: template class LockerT { public: LockerT(mutexT& Am) : m(Am) { m.lock(); } ~LockerT() { m.unlock(); } private: mutexT& m; }; class Locker : public LockerT { public: Locker(mutex& Am) : LockerT(Am) {} }; class RLocker : public LockerT { public: RLocker(rmutex& Am) : LockerT(Am) {} }; //---------------------------------------------------------- // 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 } void f2(void* Ap = 0) { // uses vMD, vSD } void f3(void* Ap = 0) { // uses vSD, vMD } void f4(void* Ap = 0) { // uses vJD } void f5(void* Ap = 0) { // uses vMD, vJD, vSD } void f6(void* Ap = 0) { // uses vMD } void f7(void* Ap = 0) { // uses vMD } void f8(void* Ap = 0) { // uses none } void f9(void* Ap = 0) { // uses vMD, vJD, vSD } private: // assume all internal functions can make calls to each other. void if1(void* Ap = 0) { // uses vSD, vJD } void if2(void* Ap = 0) { // uses vSD, vMD } void if3(void* Ap = 0) { // uses vJD, vMD, vSD } void if4(void* Ap = 0) { // uses vMD } void if5(void* Ap = 0) { // uses vSD, vMD } private: // shared data: accessed by most member functions: // all data members are independent of each other std::vector vSD; std::vector vJD; std::vector 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; } //---------------------------------------------------------- //---------------------------------------------------------- . 0