Guest User

Bee4

a guest
Aug 12th, 2014
450
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.16 KB | None | 0 0
  1. #include <vector>
  2. #include <queue>
  3. #include <atomic>
  4. #include <mutex>
  5. #include <condition_variable>
  6. #include <thread>
  7. #include <random>
  8. #include <chrono>
  9. #include <iostream>
  10. #include <iomanip>
  11. #include <cassert>
  12.  
  13. #ifndef unlikely
  14. #define likely(x)       __builtin_expect((x),1)
  15. #define unlikely(x)     __builtin_expect((x),0)
  16. #endif
  17.  
  18. #if 0
  19. // C++14 shared_timed_mutex, or pthread_rwlock_t can be used instead
  20. #include <boost/thread/shared_mutex.hpp>
  21. #include <boost/thread/shared_lock_guard.hpp>
  22. using shared_mutex = boost::shared_mutex;
  23. template<class T> using shared_lock_guard = boost::shared_lock_guard<T>;
  24. #else
  25. #include <pthread.h>
  26. class shared_mutex {
  27.     pthread_rwlock_t it;
  28. public:
  29.     shared_mutex(const shared_mutex&) = delete;
  30.     shared_mutex& operator = (const shared_mutex&) = delete;
  31.     shared_mutex() {
  32.         pthread_rwlock_init(&it, nullptr); }
  33.     ~shared_mutex() {
  34.         pthread_rwlock_destroy(&it); }
  35.     void lock() {
  36.         pthread_rwlock_wrlock(&it); }
  37.     void unlock() {
  38.         pthread_rwlock_unlock(&it); }
  39.     void lock_shared() {
  40.         pthread_rwlock_rdlock(&it); }
  41.     void unlock_shared() {
  42.         pthread_rwlock_unlock(&it); }
  43. };
  44. template<class T>
  45.   class shared_lock_guard {
  46.     T& it;
  47. public:
  48.     shared_lock_guard(const shared_lock_guard&) = delete;
  49.     shared_lock_guard& operator = (const shared_lock_guard&) = delete;
  50.     shared_lock_guard(T& it): it(it) {
  51.         it.lock_shared(); }
  52.     ~shared_lock_guard() {
  53.         it.unlock_shared(); }
  54. };
  55. #endif
  56.  
  57. using namespace std;
  58. using the_clock = chrono::steady_clock;
  59. using stamp = chrono::time_point<the_clock>;
  60. using ms = chrono::milliseconds;
  61.  
  62. const int P = 15, C = 10;
  63. auto producer_sleep = uniform_int_distribution<int>(10*P,20*P);
  64. auto consume_sleep = uniform_int_distribution<int>(1,3);
  65. auto regular_sleep = uniform_int_distribution<int>(3,10);
  66. const auto regular_timeout = ms(30*C);
  67. atomic<int> produced;
  68. atomic<int> consumed;
  69. atomic<int> regular;
  70. atomic<int> outoforder;
  71. bool done(bool producer = false) {
  72.     int stop = 10000; int p = produced * C;
  73.     return p >= stop && (producer || consumed >= p); }
  74.  
  75. template<class E>
  76.   class event_queue {
  77. public:
  78.     event_queue(size_t nbees = P, size_t bits = 10) {
  79.     //  minimal capacity: 256, default: 1024
  80.     //  must be power of 2 (we rather use and-mask instead of modulo)
  81.         if(bits <= 8) bits = 8;
  82.         size_t size = 1 << bits;
  83.         mask = size-1; buf = a.allocate(size);
  84.         bees = b.allocate(this->nbees = nbees);
  85.         for(int i = 0; i < nbees; i++)
  86.             b.construct(bees + i, *this);
  87.     }
  88.     void push(const E& e) {
  89.         shared lock(sync);  // quick shared lock
  90.         size_t i = wi++;    // reserve the slot
  91.         if(unlikely(i > lr+mask)) { // check full
  92.             push_full(e); return; } // not implemented yet
  93.         a.construct(buf+(i&mask), nbees, e); // emplace
  94.     //  could possibly be implemented as single notify_all()
  95.     //  with global bitfield (e.g. unsigned long log = ~0)
  96.     //  but that would slow bees instead (locking on single mutex)
  97.     //  for busy bee this just increments counter
  98.     //  for sleeping bee this means lock+notify
  99.         for(int i = 0; i < nbees; i++)
  100.             bees[i].notify();
  101.     }
  102.     bool pop(int id, E& e, stamp until) {
  103.         return bees[id].pop(e, until);
  104.     }
  105. private:
  106. //  global synchronization (fast-locking unless we need to grow or manage bees)
  107.     shared_mutex sync;
  108.     typedef shared_lock_guard<shared_mutex> shared;
  109.     typedef lock_guard<shared_mutex> unique;
  110. //  consumed counting
  111.     struct item {
  112.         atomic<size_t> c;   // consumed countdown
  113.         E e;            // the event/element
  114.         item(int nbees, const E& e)
  115.         : c(nbees), e(e) {}};
  116. //  event buffer
  117.     atomic<size_t> wi;  // write index for producers (reservation)
  118.     atomic<size_t> lr;  // lowest read index (for each bee: bee.ri >= lr)
  119.     size_t mask;        // element at buf[index&mask]
  120.     item *buf;          // the buffer
  121.     int nbees;          // number of bees
  122.     struct handle {
  123.         handle(event_queue<E>& q): n(0), ri(0), q(q),
  124.           mx(), cv(), wakeup(false), was_empty(false) {}
  125.         atomic<int> n;  // number of elements available for consumation
  126.         int ri;         // read index
  127.         event_queue<E>& q; // master queue
  128.         mutex mx; condition_variable cv;
  129.         bool wakeup, was_empty;
  130.         void notify() {
  131.             if(++n == 0) {
  132.                 { lock_guard<mutex> lock(mx);
  133.                     wakeup = true; }
  134.                 cv.notify_one(); }}
  135.         bool pop(E& e, stamp until) {
  136.             if(the_clock::now() >= until)
  137.                 return false;
  138.             if(was_empty || --n < 0) {
  139.                 was_empty = true;
  140.                 unique_lock<mutex> lock(mx);
  141.                 if(!cv.wait_until(lock, until, [this] {
  142.                     return wakeup; }))
  143.                     return false;
  144.                 wakeup = false;
  145.                 was_empty = false; }
  146.         //  get the item
  147.             shared lock(q.sync);
  148.             item& i = q.buf[ri++ & q.mask];
  149.             e = i.e;
  150.         //  desctroy and advance lowest read index if whe were last
  151.             if(--i.c == 0) {
  152.                 ++q.lr;
  153.                 q.a.destroy(&i); }
  154.             return true;
  155.         }
  156.     } *bees;
  157.     allocator<item> a;
  158.     allocator<handle> b;
  159.     void push_full(E e) {
  160.     //  not implemented
  161.         assert(false);
  162.     //  unique lock(sync); ...lot of work
  163.         throw -1;
  164.     }
  165. };
  166.  
  167. // the queue (for ints as example)
  168. event_queue<int> work;
  169.  
  170. // example bee doing random sleeps
  171. class bee {
  172.     const int id;
  173.     random_device rd;
  174.     int next = 0;
  175.     priority_queue<int,vector<int>,greater<int> > ahead;
  176. public:
  177.     bee(int id): id(id) {}
  178.     bee(const bee& src): id(src.id) {};
  179.     void consume(int e) {
  180.         if(e == next) {
  181.             next++;
  182.             while(!ahead.empty() && ahead.top() == next) {
  183.                 next++; ahead.pop(); }
  184.         } else {
  185.             ahead.push(e); outoforder++;
  186.             if(ahead.size() > P*3) {
  187.                 for(cout << "bee" << id << " ahead: ";
  188.                     !ahead.empty(); ahead.pop())
  189.                     cout << ahead.top() << " ";
  190.                 cout << endl;
  191.                 assert(("too many skipped elements", false)); }}
  192.         this_thread::sleep_for(ms(consume_sleep(rd)));
  193.         consumed++; }
  194.     void do_regular() {
  195.         this_thread::sleep_for(ms(regular_sleep(rd)));
  196.         regular++; }
  197.     void operator()() {
  198.         stamp next = the_clock::now() + regular_timeout;
  199.         while(!done()) {
  200.             int e;
  201.             if(work.pop(id, e, next))
  202.                 consume(e);
  203.             else {
  204.                 do_regular();
  205.                 next += regular_timeout;
  206.             }
  207.         }
  208.     }
  209. };
  210.  
  211. // example producer
  212. class producer {
  213.     const int id;
  214.     random_device rd;
  215. public:
  216.     producer(int id): id(id) {}
  217.     producer(const producer& src): id(src.id) {};
  218.     void operator()() {
  219.         while(!done(true)) { work.push(produced++);
  220.             this_thread::sleep_for(ms(producer_sleep(rd)));
  221. }}};
  222.  
  223. int main() {
  224.     vector<bee> bees;
  225.     while(bees.size() < C) bees.emplace_back(bees.size());
  226.     vector<producer> pros;
  227.     while(pros.size() < P) pros.emplace_back(pros.size());
  228.     vector<thread> all;
  229.     for(auto&& b : bees) all.push_back(thread([&b] {
  230.         try { b(); } catch(...) { assert(("crash", false)); } }));
  231.     for(auto&& p : pros) all.push_back(thread([&p] {
  232.         try { p(); } catch(...) { assert(("crash", false)); } }));
  233.     while(!done()) {
  234.         this_thread::sleep_for(ms(1000));
  235.         cout << setw(5) << (produced.load() * C)
  236.             << "; " << setw(5) << consumed.load()
  237.             << "; " << setw(5) << regular.load() << endl;
  238.     }
  239.     for(auto&& t : all) t.join();
  240.     int pro = produced, con = consumed, reg = regular;
  241.     cout << "produced:  " << setw(5) << (pro*C)
  242.         << " (" << setw(5) << pro << ")" << endl;
  243.     cout << "consumed:  " << setw(5) << con
  244.         << " (" << setw(5) << (con/C) << ")" << endl;
  245.     cout << "regular:   " << setw(5) << reg << endl;
  246.     cout << "out-of-order: " << outoforder << endl;
  247. }
Advertisement
Add Comment
Please, Sign In to add comment