19b063ed9SShuo Chen#include "../Condition.h" 29b063ed9SShuo Chen#include "../Mutex.h" 39b063ed9SShuo Chen#include "../Thread.h" 49b063ed9SShuo Chen 59b063ed9SShuo Chen#include <boost/bind.hpp> 69b063ed9SShuo Chen 79b063ed9SShuo Chen#include <signal.h> 89b063ed9SShuo Chen 99b063ed9SShuo Chen// Interface of Waiter, also takes care of mutex_ & cond_ init and destroy. 109b063ed9SShuo Chenclass Waiter : boost::noncopyable 119b063ed9SShuo Chen{ 129b063ed9SShuo Chen public: 139b063ed9SShuo Chen virtual void wait() = 0; 149b063ed9SShuo Chen virtual void signal() = 0; 159b063ed9SShuo Chen 169b063ed9SShuo Chen protected: 179b063ed9SShuo Chen Waiter() 189b063ed9SShuo Chen { 199b063ed9SShuo Chen pthread_mutex_init(&mutex_, NULL); 209b063ed9SShuo Chen pthread_cond_init(&cond_, NULL); 219b063ed9SShuo Chen } 229b063ed9SShuo Chen 239b063ed9SShuo Chen ~Waiter() 249b063ed9SShuo Chen { 259b063ed9SShuo Chen pthread_mutex_destroy(&mutex_); 269b063ed9SShuo Chen pthread_cond_destroy(&cond_); 279b063ed9SShuo Chen } 289b063ed9SShuo Chen 299b063ed9SShuo Chen pthread_mutex_t mutex_; 309b063ed9SShuo Chen pthread_cond_t cond_; 319b063ed9SShuo Chen}; 329b063ed9SShuo Chen 339b063ed9SShuo Chen// Version 1: orininal from the book 349b063ed9SShuo Chen// Incorrect, could lose signal 359b063ed9SShuo Chenclass Waiter1 : public Waiter 369b063ed9SShuo Chen{ 379b063ed9SShuo Chen public: 389b063ed9SShuo Chen void wait() override 399b063ed9SShuo Chen { 409b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 419b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 429b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 439b063ed9SShuo Chen } 449b063ed9SShuo Chen 459b063ed9SShuo Chen void signal() override 469b063ed9SShuo Chen { 479b063ed9SShuo Chen pthread_cond_signal(&cond_); 489b063ed9SShuo Chen } 499b063ed9SShuo Chen}; 509b063ed9SShuo Chen 519b063ed9SShuo Chen// Version 2: signal in lock 529b063ed9SShuo Chen// Incorrect, could lose signal 539b063ed9SShuo Chenclass Waiter2 : public Waiter 549b063ed9SShuo Chen{ 559b063ed9SShuo Chen public: 569b063ed9SShuo Chen void wait() override 579b063ed9SShuo Chen { 589b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 599b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 609b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 619b063ed9SShuo Chen } 629b063ed9SShuo Chen 639b063ed9SShuo Chen void signal() override 649b063ed9SShuo Chen { 659b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 669b063ed9SShuo Chen pthread_cond_signal(&cond_); 679b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 689b063ed9SShuo Chen } 699b063ed9SShuo Chen}; 709b063ed9SShuo Chen 719b063ed9SShuo Chen// Version 3: add a boolean member 729b063ed9SShuo Chen// Incorrect, spurious wakeup 739b063ed9SShuo Chenclass Waiter3 : public Waiter 749b063ed9SShuo Chen{ 759b063ed9SShuo Chen public: 769b063ed9SShuo Chen void wait() override 779b063ed9SShuo Chen { 789b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 799b063ed9SShuo Chen if (!signaled_) 809b063ed9SShuo Chen { 819b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 829b063ed9SShuo Chen } 839b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 849b063ed9SShuo Chen } 859b063ed9SShuo Chen 869b063ed9SShuo Chen void signal() override 879b063ed9SShuo Chen { 889b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 899b063ed9SShuo Chen signaled_ = true; 909b063ed9SShuo Chen pthread_cond_signal(&cond_); 919b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 929b063ed9SShuo Chen } 939b063ed9SShuo Chen 949b063ed9SShuo Chen private: 959b063ed9SShuo Chen bool signaled_ = false; 969b063ed9SShuo Chen}; 979b063ed9SShuo Chen 989b063ed9SShuo Chen// Version 4: wait in while-loop 999b063ed9SShuo Chen// Correct, signal before unlock 1009b063ed9SShuo Chenclass Waiter4 : public Waiter 1019b063ed9SShuo Chen{ 1029b063ed9SShuo Chen public: 1039b063ed9SShuo Chen void wait() override 1049b063ed9SShuo Chen { 1059b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1069b063ed9SShuo Chen while (!signaled_) 1079b063ed9SShuo Chen { 1089b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 1099b063ed9SShuo Chen } 1109b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1119b063ed9SShuo Chen } 1129b063ed9SShuo Chen 1139b063ed9SShuo Chen void signal() override 1149b063ed9SShuo Chen { 1159b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1169b063ed9SShuo Chen signaled_ = true; 1179b063ed9SShuo Chen pthread_cond_signal(&cond_); 1189b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1199b063ed9SShuo Chen } 1209b063ed9SShuo Chen 1219b063ed9SShuo Chen private: 1229b063ed9SShuo Chen bool signaled_ = false; 1239b063ed9SShuo Chen}; 1249b063ed9SShuo Chen 1259b063ed9SShuo Chen// Version 5: wait in while-loop 1269b063ed9SShuo Chen// Correct, signal after unlock 1279b063ed9SShuo Chenclass Waiter5 : public Waiter 1289b063ed9SShuo Chen{ 1299b063ed9SShuo Chen public: 1309b063ed9SShuo Chen void wait() override 1319b063ed9SShuo Chen { 1329b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1339b063ed9SShuo Chen while (!signaled_) 1349b063ed9SShuo Chen { 1359b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 1369b063ed9SShuo Chen } 1379b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1389b063ed9SShuo Chen } 1399b063ed9SShuo Chen 1409b063ed9SShuo Chen void signal() override 1419b063ed9SShuo Chen { 1429b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1439b063ed9SShuo Chen signaled_ = true; 1449b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1459b063ed9SShuo Chen pthread_cond_signal(&cond_); 1469b063ed9SShuo Chen } 1479b063ed9SShuo Chen 1489b063ed9SShuo Chen private: 1499b063ed9SShuo Chen bool signaled_ = false; 1509b063ed9SShuo Chen}; 1519b063ed9SShuo Chen 1529b063ed9SShuo Chen// Note: version 4 is as efficient as version 5 because of "wait morphing" 1539b063ed9SShuo Chen 1549b063ed9SShuo Chen// Version 6: signal before set boolean flag 1559b063ed9SShuo Chen// Correct or not? 1569b063ed9SShuo Chenclass Waiter6 : public Waiter 1579b063ed9SShuo Chen{ 1589b063ed9SShuo Chen public: 1599b063ed9SShuo Chen void wait() override 1609b063ed9SShuo Chen { 1619b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1629b063ed9SShuo Chen while (!signaled_) 1639b063ed9SShuo Chen { 1649b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 1659b063ed9SShuo Chen } 1669b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1679b063ed9SShuo Chen } 1689b063ed9SShuo Chen 1699b063ed9SShuo Chen void signal() override 1709b063ed9SShuo Chen { 1719b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1729b063ed9SShuo Chen pthread_cond_signal(&cond_); 1739b063ed9SShuo Chen signaled_ = true; 1749b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1759b063ed9SShuo Chen } 1769b063ed9SShuo Chen 1779b063ed9SShuo Chen private: 1789b063ed9SShuo Chen bool signaled_ = false; 1799b063ed9SShuo Chen}; 1809b063ed9SShuo Chen 1819b063ed9SShuo Chen// Version 8: modify signaled_ without lock 1829b063ed9SShuo Chen// Incorrect, data-race and could lose signal 1839b063ed9SShuo Chenclass Waiter8 : public Waiter 1849b063ed9SShuo Chen{ 1859b063ed9SShuo Chen public: 1869b063ed9SShuo Chen void wait() override 1879b063ed9SShuo Chen { 1889b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 1899b063ed9SShuo Chen while (!signaled_) 1909b063ed9SShuo Chen { 1919b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 1929b063ed9SShuo Chen } 1939b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 1949b063ed9SShuo Chen } 1959b063ed9SShuo Chen 1969b063ed9SShuo Chen void signal() override 1979b063ed9SShuo Chen { 1989b063ed9SShuo Chen signaled_ = true; 1999b063ed9SShuo Chen pthread_cond_signal(&cond_); 2009b063ed9SShuo Chen } 2019b063ed9SShuo Chen 2029b063ed9SShuo Chen private: 2039b063ed9SShuo Chen bool signaled_ = false; 2049b063ed9SShuo Chen}; 2059b063ed9SShuo Chen 2069b063ed9SShuo Chen// Version 7: broadcast to wakeup multiple waiting threads 2079b063ed9SShuo Chen// Probably the best version among above. 2089b063ed9SShuo Chenclass Waiter7 : public Waiter 2099b063ed9SShuo Chen{ 2109b063ed9SShuo Chen public: 2119b063ed9SShuo Chen void wait() override 2129b063ed9SShuo Chen { 2139b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 2149b063ed9SShuo Chen while (!signaled_) 2159b063ed9SShuo Chen { 2169b063ed9SShuo Chen pthread_cond_wait(&cond_, &mutex_); 2179b063ed9SShuo Chen } 2189b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 2199b063ed9SShuo Chen } 2209b063ed9SShuo Chen 2219b063ed9SShuo Chen void signal() override // Sorry, bad name in base class, poor OOP 2229b063ed9SShuo Chen { 2239b063ed9SShuo Chen broadcast(); 2249b063ed9SShuo Chen } 2259b063ed9SShuo Chen 2269b063ed9SShuo Chen void broadcast() 2279b063ed9SShuo Chen { 2289b063ed9SShuo Chen pthread_mutex_lock(&mutex_); 2299b063ed9SShuo Chen pthread_cond_broadcast(&cond_); 2309b063ed9SShuo Chen signaled_ = true; 2319b063ed9SShuo Chen pthread_mutex_unlock(&mutex_); 2329b063ed9SShuo Chen } 2339b063ed9SShuo Chen 2349b063ed9SShuo Chen private: 2359b063ed9SShuo Chen bool signaled_ = false; 2369b063ed9SShuo Chen}; 2379b063ed9SShuo Chen 2389b063ed9SShuo Chen// Same as version 7, with muduo library 2399b063ed9SShuo Chenclass WaiterInMuduo : boost::noncopyable 2409b063ed9SShuo Chen{ 2419b063ed9SShuo Chen public: 2429b063ed9SShuo Chen WaiterInMuduo() 2439b063ed9SShuo Chen : cond_(mutex_) 2449b063ed9SShuo Chen { 2459b063ed9SShuo Chen } 2469b063ed9SShuo Chen 2479b063ed9SShuo Chen void wait() 2489b063ed9SShuo Chen { 2499b063ed9SShuo Chen muduo::MutexLockGuard lock(mutex_); 2509b063ed9SShuo Chen while (!signaled_) 2519b063ed9SShuo Chen { 2529b063ed9SShuo Chen cond_.wait(); 2539b063ed9SShuo Chen } 2549b063ed9SShuo Chen } 2559b063ed9SShuo Chen 2569b063ed9SShuo Chen void broadcast() 2579b063ed9SShuo Chen { 2589b063ed9SShuo Chen muduo::MutexLockGuard lock(mutex_); 2599b063ed9SShuo Chen signaled_ = true; 2609b063ed9SShuo Chen cond_.notifyAll(); 2619b063ed9SShuo Chen } 2629b063ed9SShuo Chen 2639b063ed9SShuo Chen private: 2649b063ed9SShuo Chen muduo::MutexLock mutex_; 2659b063ed9SShuo Chen muduo::Condition cond_; 2669b063ed9SShuo Chen bool signaled_ = false; 2679b063ed9SShuo Chen}; 2689b063ed9SShuo Chen 2699b063ed9SShuo Chenvoid initAndRefresh(Waiter* waiter) 2709b063ed9SShuo Chen{ 2719b063ed9SShuo Chen printf("init: running\n"); 2729b063ed9SShuo Chen 2739b063ed9SShuo Chen struct timespec ts = { 0, 500*1000*1000 }; 2749b063ed9SShuo Chen nanosleep(&ts, NULL); // initialization takes 500ms 2759b063ed9SShuo Chen 2769b063ed9SShuo Chen waiter->signal(); 2779b063ed9SShuo Chen printf("init: signaled\n"); 2789b063ed9SShuo Chen} 2799b063ed9SShuo Chen 2809b063ed9SShuo Chenvoid runServer(Waiter* waiter, int sec) 2819b063ed9SShuo Chen{ 2829b063ed9SShuo Chen muduo::Thread initThread(boost::bind(initAndRefresh, waiter)); 2839b063ed9SShuo Chen initThread.start(); 2849b063ed9SShuo Chen printf("main: init thread started\n"); 2859b063ed9SShuo Chen 2869b063ed9SShuo Chen struct timespec ts = { sec, 0 }; 2879b063ed9SShuo Chen nanosleep(&ts, NULL); // do some work before calling wait() 2889b063ed9SShuo Chen 2899b063ed9SShuo Chen printf("main: waiting\n"); 2909b063ed9SShuo Chen waiter->wait(); 2919b063ed9SShuo Chen printf("main: done\n"); 2929b063ed9SShuo Chen} 2939b063ed9SShuo Chen 2949b063ed9SShuo Chenvoid sigalarm(int) 2959b063ed9SShuo Chen{ 2969b063ed9SShuo Chen write(1, "\nFAILED\n", 8); 2979b063ed9SShuo Chen exit(1); 2989b063ed9SShuo Chen} 2999b063ed9SShuo Chen 3009b063ed9SShuo Chenint main(int argc, char* argv[]) 3019b063ed9SShuo Chen{ 3029b063ed9SShuo Chen signal(SIGALRM, sigalarm); 3039b063ed9SShuo Chen alarm(5); 3049b063ed9SShuo Chen 3059b063ed9SShuo Chen Waiter1 w1; 3069b063ed9SShuo Chen Waiter2 w2; 3079b063ed9SShuo Chen Waiter3 w3; 3089b063ed9SShuo Chen Waiter4 w4; 3099b063ed9SShuo Chen Waiter5 w5; 3109b063ed9SShuo Chen Waiter6 w6; 3119b063ed9SShuo Chen Waiter7 w7; 3129b063ed9SShuo Chen Waiter8 w8; 3139b063ed9SShuo Chen WaiterInMuduo waiterInMuduo; 3149b063ed9SShuo Chen 3159b063ed9SShuo Chen Waiter* waiters[] = { NULL, &w1, &w2, &w3, &w4, &w5, &w6, &w7, &w8 }; 3169b063ed9SShuo Chen int whichWaiter = argc > 1 ? atoi(argv[1]) : 5; 3179b063ed9SShuo Chen if (!(whichWaiter > 0 && whichWaiter <= 8)) 3189b063ed9SShuo Chen { 3199b063ed9SShuo Chen printf("Unknown waiter, must between 1 and 8, inclusive.\n"); 3209b063ed9SShuo Chen return 1; 3219b063ed9SShuo Chen } 3229b063ed9SShuo Chen Waiter* waiter = waiters[whichWaiter]; 3239b063ed9SShuo Chen 3249b063ed9SShuo Chen int sec = 0; 3259b063ed9SShuo Chen printf("test 1: wait() before signal().\n"); 3269b063ed9SShuo Chen runServer(waiter, sec); 3279b063ed9SShuo Chen 3289b063ed9SShuo Chen printf("\ntest 2: wait() after signal().\n"); 3299b063ed9SShuo Chen sec = 1; 3309b063ed9SShuo Chen runServer(waiter, sec); 3319b063ed9SShuo Chen 3329b063ed9SShuo Chen printf("\nPASSED!\n"); 3339b063ed9SShuo Chen} 334