1#include "../Condition.h" 2#include "../Mutex.h" 3#include "../Thread.h" 4 5#include <boost/bind.hpp> 6 7#include <signal.h> 8 9// Interface of Waiter, also takes care of mutex_ & cond_ init and destroy. 10class Waiter : boost::noncopyable 11{ 12 public: 13 virtual void wait() = 0; 14 virtual void signal() = 0; 15 16 protected: 17 Waiter() 18 { 19 pthread_mutex_init(&mutex_, NULL); 20 pthread_cond_init(&cond_, NULL); 21 } 22 23 ~Waiter() 24 { 25 pthread_mutex_destroy(&mutex_); 26 pthread_cond_destroy(&cond_); 27 } 28 29 pthread_mutex_t mutex_; 30 pthread_cond_t cond_; 31}; 32 33// Version 1: orininal from the book 34// Incorrect, could lose signal 35class Waiter1 : public Waiter 36{ 37 public: 38 void wait() override 39 { 40 pthread_mutex_lock(&mutex_); 41 pthread_cond_wait(&cond_, &mutex_); 42 pthread_mutex_unlock(&mutex_); 43 } 44 45 void signal() override 46 { 47 pthread_cond_signal(&cond_); 48 } 49}; 50 51// Version 2: signal in lock 52// Incorrect, could lose signal 53class Waiter2 : public Waiter 54{ 55 public: 56 void wait() override 57 { 58 pthread_mutex_lock(&mutex_); 59 pthread_cond_wait(&cond_, &mutex_); 60 pthread_mutex_unlock(&mutex_); 61 } 62 63 void signal() override 64 { 65 pthread_mutex_lock(&mutex_); 66 pthread_cond_signal(&cond_); 67 pthread_mutex_unlock(&mutex_); 68 } 69}; 70 71// Version 3: add a boolean member 72// Incorrect, spurious wakeup 73class Waiter3 : public Waiter 74{ 75 public: 76 void wait() override 77 { 78 pthread_mutex_lock(&mutex_); 79 if (!signaled_) 80 { 81 pthread_cond_wait(&cond_, &mutex_); 82 } 83 pthread_mutex_unlock(&mutex_); 84 } 85 86 void signal() override 87 { 88 pthread_mutex_lock(&mutex_); 89 signaled_ = true; 90 pthread_cond_signal(&cond_); 91 pthread_mutex_unlock(&mutex_); 92 } 93 94 private: 95 bool signaled_ = false; 96}; 97 98// Version 4: wait in while-loop 99// Correct, signal before unlock 100class Waiter4 : public Waiter 101{ 102 public: 103 void wait() override 104 { 105 pthread_mutex_lock(&mutex_); 106 while (!signaled_) 107 { 108 pthread_cond_wait(&cond_, &mutex_); 109 } 110 pthread_mutex_unlock(&mutex_); 111 } 112 113 void signal() override 114 { 115 pthread_mutex_lock(&mutex_); 116 signaled_ = true; 117 pthread_cond_signal(&cond_); 118 pthread_mutex_unlock(&mutex_); 119 } 120 121 private: 122 bool signaled_ = false; 123}; 124 125// Version 5: wait in while-loop 126// Correct, signal after unlock 127class Waiter5 : public Waiter 128{ 129 public: 130 void wait() override 131 { 132 pthread_mutex_lock(&mutex_); 133 while (!signaled_) 134 { 135 pthread_cond_wait(&cond_, &mutex_); 136 } 137 pthread_mutex_unlock(&mutex_); 138 } 139 140 void signal() override 141 { 142 pthread_mutex_lock(&mutex_); 143 signaled_ = true; 144 pthread_mutex_unlock(&mutex_); 145 pthread_cond_signal(&cond_); 146 } 147 148 private: 149 bool signaled_ = false; 150}; 151 152// Note: version 4 is as efficient as version 5 because of "wait morphing" 153 154// Version 6: signal before set boolean flag 155// Correct or not? 156class Waiter6 : public Waiter 157{ 158 public: 159 void wait() override 160 { 161 pthread_mutex_lock(&mutex_); 162 while (!signaled_) 163 { 164 pthread_cond_wait(&cond_, &mutex_); 165 } 166 pthread_mutex_unlock(&mutex_); 167 } 168 169 void signal() override 170 { 171 pthread_mutex_lock(&mutex_); 172 pthread_cond_signal(&cond_); 173 signaled_ = true; 174 pthread_mutex_unlock(&mutex_); 175 } 176 177 private: 178 bool signaled_ = false; 179}; 180 181// Version 8: modify signaled_ without lock 182// Incorrect, data-race and could lose signal 183class Waiter8 : public Waiter 184{ 185 public: 186 void wait() override 187 { 188 pthread_mutex_lock(&mutex_); 189 while (!signaled_) 190 { 191 pthread_cond_wait(&cond_, &mutex_); 192 } 193 pthread_mutex_unlock(&mutex_); 194 } 195 196 void signal() override 197 { 198 signaled_ = true; 199 pthread_cond_signal(&cond_); 200 } 201 202 private: 203 bool signaled_ = false; 204}; 205 206// Version 7: broadcast to wakeup multiple waiting threads 207// Probably the best version among above. 208class Waiter7 : public Waiter 209{ 210 public: 211 void wait() override 212 { 213 pthread_mutex_lock(&mutex_); 214 while (!signaled_) 215 { 216 pthread_cond_wait(&cond_, &mutex_); 217 } 218 pthread_mutex_unlock(&mutex_); 219 } 220 221 void signal() override // Sorry, bad name in base class, poor OOP 222 { 223 broadcast(); 224 } 225 226 void broadcast() 227 { 228 pthread_mutex_lock(&mutex_); 229 pthread_cond_broadcast(&cond_); 230 signaled_ = true; 231 pthread_mutex_unlock(&mutex_); 232 } 233 234 private: 235 bool signaled_ = false; 236}; 237 238// Same as version 7, with muduo library 239class WaiterInMuduo : boost::noncopyable 240{ 241 public: 242 WaiterInMuduo() 243 : cond_(mutex_) 244 { 245 } 246 247 void wait() 248 { 249 muduo::MutexLockGuard lock(mutex_); 250 while (!signaled_) 251 { 252 cond_.wait(); 253 } 254 } 255 256 void broadcast() 257 { 258 muduo::MutexLockGuard lock(mutex_); 259 signaled_ = true; 260 cond_.notifyAll(); 261 } 262 263 private: 264 muduo::MutexLock mutex_; 265 muduo::Condition cond_; 266 bool signaled_ = false; 267}; 268 269void initAndRefresh(Waiter* waiter) 270{ 271 printf("init: running\n"); 272 273 struct timespec ts = { 0, 500*1000*1000 }; 274 nanosleep(&ts, NULL); // initialization takes 500ms 275 276 waiter->signal(); 277 printf("init: signaled\n"); 278} 279 280void runServer(Waiter* waiter, int sec) 281{ 282 muduo::Thread initThread(boost::bind(initAndRefresh, waiter)); 283 initThread.start(); 284 printf("main: init thread started\n"); 285 286 struct timespec ts = { sec, 0 }; 287 nanosleep(&ts, NULL); // do some work before calling wait() 288 289 printf("main: waiting\n"); 290 waiter->wait(); 291 printf("main: done\n"); 292} 293 294void sigalarm(int) 295{ 296 write(1, "\nFAILED\n", 8); 297 exit(1); 298} 299 300int main(int argc, char* argv[]) 301{ 302 signal(SIGALRM, sigalarm); 303 alarm(5); 304 305 Waiter1 w1; 306 Waiter2 w2; 307 Waiter3 w3; 308 Waiter4 w4; 309 Waiter5 w5; 310 Waiter6 w6; 311 Waiter7 w7; 312 Waiter8 w8; 313 WaiterInMuduo waiterInMuduo; 314 315 Waiter* waiters[] = { NULL, &w1, &w2, &w3, &w4, &w5, &w6, &w7, &w8 }; 316 int whichWaiter = argc > 1 ? atoi(argv[1]) : 5; 317 if (!(whichWaiter > 0 && whichWaiter <= 8)) 318 { 319 printf("Unknown waiter, must between 1 and 8, inclusive.\n"); 320 return 1; 321 } 322 Waiter* waiter = waiters[whichWaiter]; 323 324 int sec = 0; 325 printf("test 1: wait() before signal().\n"); 326 runServer(waiter, sec); 327 328 printf("\ntest 2: wait() after signal().\n"); 329 sec = 1; 330 runServer(waiter, sec); 331 332 printf("\nPASSED!\n"); 333} 334