fork(1) download
  1. #include <cstdint>
  2. #include <iostream>
  3. #include <mutex>
  4. #include <thread>
  5.  
  6. typedef long Money; //In minor unit.
  7.  
  8. class Account {
  9. public:
  10. bool transfer(Account& to,const Money amount);
  11. Money get_balance() const;
  12. Account(const Money deposit=0) : balance{deposit} {}
  13. private:
  14. mutable std::mutex lock;
  15. Money balance;
  16. };
  17.  
  18. bool Account::transfer(Account& to,const Money amount){
  19. std::unique_lock<decltype(this->lock)> flock{this->lock,std::defer_lock};
  20. std::unique_lock<decltype(to.lock)> tlock{to.lock,std::defer_lock};
  21. //NON-PORTABLE:BEGIN: using intptr_t AND assuming Total Strict Order.
  22. const auto fi{reinterpret_cast<const std::intptr_t>(static_cast<const void*>(&this->lock))};
  23. const auto ti{reinterpret_cast<const std::intptr_t>(static_cast<const void*>(&to.lock))};
  24. if(fi<ti){
  25. flock.lock();
  26. tlock.lock();
  27. } else if (fi!=ti) {
  28. tlock.lock();
  29. flock.lock();
  30. } else {
  31. flock.lock();
  32. }
  33. //NON-PORTABLE:END
  34. this->balance-=amount;
  35. to.balance+=amount;
  36. return true;
  37. }
  38.  
  39. Money Account::get_balance() const{
  40. const std::lock_guard<decltype(this->lock)> guard{this->lock};
  41. return this->balance;
  42. }
  43.  
  44. void hammer_transfer(Account& from,Account& to,const Money amount, const int tries){
  45. for(int i{1};i<=tries;++i){
  46. from.transfer(to,amount);
  47. }
  48. }
  49.  
  50. int main() {
  51. constexpr Money open_a{ 200000L};
  52. constexpr Money open_b{ 100000L};
  53. constexpr Money tran_ab{10};
  54. constexpr Money tran_ba{3};
  55. constexpr Money tran_aa{7};
  56.  
  57. Account A{open_a};
  58. Account B{open_b};
  59.  
  60. std::cout << "A Open:" << A.get_balance() << '\n';
  61. std::cout << "B Open:" << B.get_balance() << '\n';
  62.  
  63. constexpr long tries{20000};
  64. std::thread TAB{hammer_transfer,std::ref(A),std::ref(B),tran_ab,tries};
  65. std::thread TBA{hammer_transfer,std::ref(B),std::ref(A),tran_ba,tries};
  66. std::thread TAA{hammer_transfer,std::ref(A),std::ref(A),tran_aa,tries};
  67.  
  68. TAB.join();
  69. TBA.join();
  70. TAA.join();
  71.  
  72. const auto close_a{A.get_balance()};
  73. const auto close_b{B.get_balance()};
  74.  
  75. std::cout << "A Close:" << close_a<< '\n';
  76. std::cout << "B Close:" << close_b<< '\n';
  77.  
  78. int errors{0};
  79. if((close_a+close_b)!=(open_a+open_b)){
  80. std::cout << "ERROR: Money Leaked!\n";
  81. ++errors;
  82. }
  83. if(close_a!=(open_a+tries*(tran_ba-tran_ab)) ||
  84. close_b!=(open_b+tries*(tran_ab-tran_ba))
  85. ){
  86. std::cout << "ERROR: 'Lost' Transaction(s)\n";
  87. ++errors;
  88. }
  89. if(errors==0){
  90. std::cout << "* SUCCESS *\n";
  91. }else{
  92. std::cout << "** FAILED **\n";
  93. }
  94. std::cout << std::endl;
  95. return 0;
  96. }
  97.  
Success #stdin #stdout 0s 4352KB
stdin
Standard input is empty
stdout
A Open:200000
B Open:100000
A Close:60000
B Close:240000
* SUCCESS *