Chris_M_Thomasson

fast-pathed std c++ semaphore...

Mar 25th, 2017
2,580
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.87 KB | None | 0 0
  1. // Fast-Semaphore by Joe Seigh
  2. // C++ Implementation by Chris Thomasson
  3. // Modeled as a semaphore-as-a-mutex test...
  4.  
  5.  
  6. #include <cstdio>
  7. #include <deque>
  8. #include <condition_variable>
  9. #include <mutex>
  10. #include <memory>
  11. #include <thread>
  12. #include <atomic>
  13. #include <algorithm>
  14. #include <cassert>
  15.  
  16.  
  17.  
  18. #define mb_relaxed std::memory_order_relaxed
  19. #define mb_consume std::memory_order_consume
  20. #define mb_acquire std::memory_order_acquire
  21. #define mb_release std::memory_order_release
  22. #define mb_acq_rel std::memory_order_acq_rel
  23. #define mb_seq_cst std::memory_order_seq_cst
  24.  
  25. #define mb_fence(mb) std::atomic_thread_fence(mb)
  26.  
  27.  
  28. #define THREADS 7
  29. #define N 1000000
  30.  
  31.  
  32. static std::mutex g_std_out_mutex;
  33.  
  34.  
  35. // Basic, bare bones C++ Semaphore
  36. struct cpp_sem
  37. {
  38.     int m_count;
  39.     std::mutex m_mutex;
  40.     std::condition_variable m_cond;
  41.  
  42.     cpp_sem(int count = 0) : m_count(count)
  43.     {
  44.         assert(count > -1);
  45.     }
  46.  
  47.     void dec() // wait
  48.     {
  49.         std::unique_lock<std::mutex> lock(m_mutex);
  50.         while (m_count == 0) m_cond.wait(lock);
  51.         --m_count;
  52.     }
  53.  
  54.     void inc() // post
  55.     {
  56.         {
  57.             std::unique_lock<std::mutex> lock(m_mutex);
  58.             ++m_count;
  59.         }
  60.  
  61.         m_cond.notify_one();
  62.     }
  63. };
  64.  
  65.  
  66. // A faster semaphore using a little spice wrt
  67. // "bolting on" C++ atomics and membars
  68. struct fsem
  69. {
  70.     std::atomic<int> m_count; // atomic sema counter
  71.     cpp_sem m_cpp_sem;
  72.  
  73.     fsem(int count = 0) : m_count(count) {}
  74.  
  75.     void dec() // wait
  76.     {
  77.         int count = m_count.fetch_sub(1, mb_relaxed);
  78.         if (count < 1) m_cpp_sem.dec(); // conditional wait
  79.  
  80.         // we acquired a signal, acquire membar
  81.         mb_fence(mb_acquire);
  82.     }
  83.  
  84.     void inc() // post
  85.     {
  86.         // we are going to release a signal, release membar
  87.         mb_fence(mb_release);
  88.  
  89.         int count = m_count.fetch_add(1, mb_relaxed);
  90.         if (count < 0) m_cpp_sem.inc();
  91.     }
  92. };
  93.  
  94.  
  95. // Some generic user state
  96. struct shared_user_state
  97. {
  98.     fsem m_sema; // acts as mutex sema with init count of 1
  99.     int m_user_state;
  100.  
  101.     shared_user_state() : m_sema(1), m_user_state(0) {}
  102. };
  103.  
  104.  
  105. void producer_thread(
  106.     unsigned int id,
  107.     shared_user_state& sustate
  108. ) {
  109.     {
  110.         std::unique_lock<std::mutex> lock(g_std_out_mutex);
  111.         std::printf("producer_thread(%u)::sustate(%p) - Entry\n", id, (void*)&sustate);
  112.     }
  113.  
  114.     for (unsigned int i = 0; i < N; ++i)
  115.     {
  116.         // atomic state mutation
  117.         sustate.m_sema.dec();
  118.         int user_state = ++sustate.m_user_state;
  119.         sustate.m_sema.inc();
  120.  
  121.         if (!(i % 1003))
  122.         {
  123.             {
  124.                 std::unique_lock<std::mutex> lock(g_std_out_mutex);
  125.                 std::printf("producer_thread(%u)::sustate(%p)::user_state(%d)\n",
  126.                     id, (void*)&sustate, user_state);
  127.             }
  128.         }
  129.     }
  130.  
  131.     {
  132.         std::unique_lock<std::mutex> lock(g_std_out_mutex);
  133.         std::printf("producer_thread(%u)::sustate(%p) - Exit\n", id, (void*)&sustate);
  134.     }
  135. }
  136.  
  137.  
  138. void consumer_thread(
  139.     unsigned int id,
  140.     shared_user_state& sustate
  141. ) {
  142.     {
  143.         std::unique_lock<std::mutex> lock(g_std_out_mutex);
  144.         std::printf("consumer_thread(%u)::sustate(%p) - Entry\n", id, (void*)&sustate);
  145.     }
  146.  
  147.     for (unsigned int i = 0; i < N; ++i)
  148.     {
  149.         // atomic state mutation
  150.         sustate.m_sema.dec();
  151.         int user_state = --sustate.m_user_state;
  152.         sustate.m_sema.inc();
  153.  
  154.         if (!(i % 1003))
  155.         {
  156.             {
  157.                 std::unique_lock<std::mutex> lock(g_std_out_mutex);
  158.                 std::printf("consumer_thread(%u)::sustate(%p)::user_state(%d)\n",
  159.                     id, (void*)&sustate, user_state);
  160.             }
  161.         }
  162.     }
  163.  
  164.     {
  165.         std::unique_lock<std::mutex> lock(g_std_out_mutex);
  166.         std::printf("consumer_thread(%u)::sustate(%p) - Exit\n", id, (void*)&sustate);
  167.     }
  168. }
  169.  
  170.  
  171.  
  172. int main(void)
  173. {
  174.     {
  175.         shared_user_state sustate;
  176.  
  177.         std::thread consumers[THREADS];
  178.         std::thread producers[THREADS];
  179.  
  180.         for (unsigned int i = 0; i < THREADS; ++i)
  181.         {
  182.             consumers[i] = std::thread(
  183.                 consumer_thread,
  184.                 i + 0,
  185.                 std::ref(sustate)
  186.             );
  187.  
  188.             producers[i] = std::thread(
  189.                 producer_thread,
  190.                 i + 1,
  191.                 std::ref(sustate)
  192.             );
  193.         }
  194.  
  195.         for (unsigned int i = 0; i < THREADS; ++i)
  196.         {
  197.             producers[i].join();
  198.             consumers[i].join();
  199.         }
  200.  
  201.         std::printf("sustate(%p)::m_user_state(%d)\n",
  202.             (void*)&sustate, sustate.m_user_state);
  203.  
  204.         assert(sustate.m_user_state == 0);
  205.     }
  206.  
  207.     std::printf("\nComplete, hit <ENTER> to exit...\n");
  208.     std::fflush(stdout);
  209.     std::getchar();
  210.  
  211.     return 0;
  212. }
Advertisement
Add Comment
Please, Sign In to add comment