Waiter_test.cc revision 9b063ed9
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