Chris_M_Thomasson

Hashed Locking: Multex...

Feb 15th, 2020
357
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.04 KB | None | 0 0
  1. /*
  2.      The Multex, simple deadlock free locking abstraction
  3.      By Chris M. Thomasson
  4. ____________________________________________________________*/
  5.  
  6.  
  7. #include <iostream>
  8. #include <functional>
  9. #include <algorithm>
  10. #include <mutex>
  11. #include <thread>
  12. #include <cassert>
  13. #include <cstddef>
  14. #include <cstdint>
  15. #include <vector>
  16.  
  17.  
  18. #define THREADS 7
  19. #define N 123456
  20.  
  21.  
  22. // Allows one to lock multiple addresses
  23. // at once, and never hit a deadlock.
  24. // It is an experiment.
  25. namespace ct_multex
  26. {
  27.      // A table of locks
  28.      struct mutex_table
  29.      {
  30.          std::vector<std::mutex> m_locks;
  31.  
  32.          mutex_table(std::size_t size) : m_locks(size) { assert(size > 0); }
  33.  
  34.          // totally contrived simple hash...
  35.          std::size_t hash(void const* ptr)
  36.          {
  37.              std::uintptr_t ptr_uint = (std::uintptr_t)ptr;
  38.              return (std::size_t)(((ptr_uint << 9) * 103) % m_locks.size());
  39.          }
  40.      };
  41.  
  42.      // A threads local indices into a table of locks
  43.      struct local_locks
  44.      {
  45.          std::vector<std::size_t> m_lock_idxs;
  46.          mutex_table& m_mtx_tbl;
  47.  
  48.          local_locks(mutex_table& mtx_tbl) :
  49.              m_lock_idxs(), m_mtx_tbl(mtx_tbl) {}
  50.  
  51.          // Add an address to be locked
  52.          void push_ptr(void const* ptr)
  53.          {
  54.              std::size_t idx = m_mtx_tbl.hash(ptr);
  55.              m_lock_idxs.push_back(idx);
  56.          }
  57.  
  58.          // Deadlock free, baby! ;^)
  59.          void ensure_locking_order()
  60.          {
  61.              // sort and remove duplicates...
  62.              std::sort(m_lock_idxs.begin(), m_lock_idxs.end());
  63.              m_lock_idxs.erase(std::unique(m_lock_idxs.begin(),
  64. m_lock_idxs.end()), m_lock_idxs.end());
  65.          }
  66.  
  67.          // Take all of the locks
  68.          void lock()
  69.          {
  70.              // there can be a flag to minimize this...
  71.              ensure_locking_order();
  72.  
  73.              std::size_t n = m_lock_idxs.size();
  74.  
  75.              for (std::size_t i = 0; i < n; ++i)
  76.              {
  77.                  m_mtx_tbl.m_locks[m_lock_idxs[i]].lock();
  78.              }
  79.          }
  80.  
  81.          // Unlock everything
  82.          void unlock()
  83.          {
  84.              std::size_t n = m_lock_idxs.size();
  85.  
  86.              for (std::size_t i = 0; i < n; ++i)
  87.              {
  88.                  m_mtx_tbl.m_locks[m_lock_idxs[n - i - 1]].unlock();
  89.              }
  90.          }
  91.      };
  92.  
  93.  
  94.      // RAII scoped lock: Allows a thread to actualy take the locks
  95.      // It locks a threads local lock indices
  96.      struct scoped_lock
  97.      {
  98.          local_locks& m_locks;
  99.  
  100.          scoped_lock(local_locks& locks) : m_locks(locks)
  101.          {
  102.              m_locks.lock();
  103.          }
  104.  
  105.          ~scoped_lock() throw()
  106.          {
  107.              m_locks.unlock();
  108.          }
  109.      };
  110. }
  111.  
  112.  
  113.  
  114.  
  115. // Test program...
  116. //______________________________________
  117.  
  118. // Shared data
  119. struct ct_shared
  120. {
  121.      ct_multex::mutex_table& m_multex;
  122.  
  123.      ct_shared(ct_multex::mutex_table& multex)
  124.          : m_multex(multex), data_0(1), data_1(2), data_2(3), data_3(4) { }
  125.  
  126.      unsigned long data_0;
  127.      unsigned long data_1;
  128.      unsigned long data_2;
  129.      unsigned long data_3;
  130. };
  131.  
  132.  
  133.  
  134. // a thread
  135. void ct_thread(ct_shared& shared)
  136. {
  137.      // Create our local locks
  138.      ct_multex::local_locks locks(shared.m_multex);
  139.  
  140.      // Add some addresses
  141.      locks.push_ptr(&shared.data_2);
  142.      locks.push_ptr(&shared.data_0);
  143.  
  144.      // Do some work
  145.      for (unsigned long i = 0; i < N / 2; ++i)
  146.      {
  147.          {
  148.              ct_multex::scoped_lock slock(locks);
  149.              // locked for data_0 and data_2
  150.              shared.data_0 += i;
  151.              shared.data_2 += i;
  152.          }
  153.  
  154.          std::this_thread::yield();
  155.  
  156.          {
  157.              ct_multex::scoped_lock slock(locks);
  158.              // locked for data_0 and data_2
  159.              shared.data_0 -= i;
  160.              std::this_thread::yield(); // for fun...
  161.              shared.data_2 -= i;
  162.          }
  163.      }
  164.  
  165.      // Add some other addresses
  166.      locks.push_ptr(&shared.data_1);
  167.      locks.push_ptr(&shared.data_3);
  168.  
  169.      // Do some more work...
  170.      for (unsigned long i = 0; i < N / 2; ++i)
  171.      {
  172.          {
  173.              ct_multex::scoped_lock slock(locks);
  174.              // locked for data_0, data_1, data_2 and data_3
  175.              shared.data_0 += i;
  176.              std::this_thread::yield(); // for fun...
  177.              shared.data_1 += i;
  178.              shared.data_2 += i;
  179.              shared.data_3 += i;
  180.          }
  181.  
  182.          std::this_thread::yield();
  183.  
  184.          {
  185.              ct_multex::scoped_lock slock(locks);
  186.              // locked for data_0, data_1, data_2 and data_3
  187.              shared.data_0 -= i;
  188.              shared.data_1 -= i;
  189.              shared.data_2 -= i;
  190.              std::this_thread::yield(); // for fun...
  191.              shared.data_3 -= i;
  192.          }
  193.      }
  194. }
  195.  
  196.  
  197. int main(void)
  198. {
  199.      {
  200.          // Our mutex table
  201.          ct_multex::mutex_table multex_tbl(42);
  202.  
  203.          // Our shared data
  204.          ct_shared shared(multex_tbl);
  205.  
  206.          // Launch...
  207.          {
  208.              std::thread threads[THREADS];
  209.  
  210.              // Create threads...
  211.              for (unsigned long i = 0; i < THREADS; ++i)
  212.              {
  213.                  threads[i] = std::thread(ct_thread, std::ref(shared));
  214.              }
  215.  
  216.              std::cout << "processing...\n\n";
  217.              std::cout.flush();
  218.  
  219.              // Join threads...
  220.              for (unsigned long i = 0; i < THREADS; ++i)
  221.              {
  222.                  threads[i].join();
  223.              }
  224.          }
  225.  
  226.          // Verify the shared data...
  227.          std::cout << "shared.data_0 = " << shared.data_0 << "\n";
  228.          std::cout << "shared.data_1 = " << shared.data_1 << "\n";
  229.          std::cout << "shared.data_2 = " << shared.data_2 << "\n";
  230.          std::cout << "shared.data_3 = " << shared.data_3 << "\n";
  231.  
  232.          assert(shared.data_0 == 1);
  233.          assert(shared.data_1 == 2);
  234.          assert(shared.data_2 == 3);
  235.          assert(shared.data_3 == 4);
  236.      }
  237.  
  238.      std::cout << "\n\nfin!\n\n";
  239.  
  240.      return 0;
  241. }
Advertisement
Add Comment
Please, Sign In to add comment