Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Coding style conformance; correcting examples to compile

...

Code Block
bgColor#ffcccc
langc
#include <mutex>
#include <thread>
 
class bank_accountBankAccount {
  int balance;
public:
  std::mutex balance_mutexbalanceMutex;
  bank_accountBankAccount() = delete;
  explicit bank_accountBankAccount(int initial_amountinitialAmount) : balance(initial_amountinitialAmount) {}
  int get_balance() const { return balance; }
  void set_balance(int amount) { balance = amount; }
};
 
int deposit(bank_accountBankAccount *from, bank_accountBankAccount *to, int amount) {
  std::lock_guard<std::mutex> from_lock(from->balance_mutex>balanceMutex);
 
  // Not enough balance to transfer.
  if (from->get_balance() < amount) {
    return -1; // Indicate error
  }
  std::lock_guard<std::mutex> to_lock(to->balance_mutex>balanceMutex);
 
  from->set_balance(from->get_balance() - amount);
  to->set_balance(to->get_balance() + amount);
 
  return 0;
}
 
void f(bank_accountBankAccount *ba1, bank_accountBankAccount *ba2) {
  // Perform the deposits.
  std::thread thr1(deposit, ba1, ba2, 100);
  std::thread thr2(deposit, ba2, ba1, 100);
  thr1.join();
  thr2.join();
}

...

This compliant solution eliminates the circular wait condition by establishing a predefined order for locking in the deposit() function. Each thread will lock on the basis of the bank_account BankAccount ID, which is set when the bank_account BankAccount object is initialized.

Code Block
bgColor#ccccff
langc
#include <atomic>
#include <mutex>
#include <thread>
 
class bank_accountBankAccount {
  static std::atomic<unsigned int> global_idglobalId;
  const unsigned int id;
  int balance;
public:
  std::mutex balance_mutexbalanceMutex;
  bank_accountBankAccount() = delete;
  explicit bank_accountBankAccount(int initial_amountinitialAmount) : id(global_idglobalId++), balance(initial_amountinitialAmount) {}
  unsigned int get_id() const { return id; }
  int get_balance() const { return balance; }
  void set_balance(int amount) { balance = amount; }
};

std::atomic<unsigned int> bank_accountBankAccount::global_idglobalId(1);
 
int deposit(bank_accountBankAccount *from, bank_accountBankAccount *to, int amount) {
  std::mutex *first;
  std::mutex *second;
 
  if (from->get_id() == to->get_id()) {
    return -1; // Indicate error
  }
 
  // Ensure proper ordering for locking.
  if (from->get_id() < to->get_id()) {
    first = &from->balance_mutex>balanceMutex;
    second = &to->balance_mutex>balanceMutex;
  } else {
    first = &to->balance_mutex>balanceMutex;
    second = &from->balance_mutex>balanceMutex;
  }
  std::lock_guard<std::mutex> first_lockfirstLock(*first);
  std::lock_guard<std::mutex> second_locksecondLock(*second);
 
  // Check for enough balance to transfer.
  if (from->get_balance() >= amount) {
    from->set_balance(from->get_balance() - amount);
    to->set_balance(to->get_balance() + amount);
    return 0;
  }
  return -1;
}
 
void f(bank_accountBankAccount *ba1, bank_accountBankAccount *ba2) {
  // Perform the deposits.
  std::thread thr1(deposit, ba1, ba2, 100);
  std::thread thr2(deposit, ba2, ba1, 100);
  thr1.join();
  thr2.join();
}

...

Code Block
bgColor#ccccff
langc
#include <mutex>
#include <thread>
 
class bank_accountBankAccount {
  int balance;
public:
  std::mutex balance_mutexbalanceMutex;
  bank_accountBankAccount() = delete;
  explicit bank_accountBankAccount(int initial_amountinitialAmount) : balance(initial_amountinitialAmount) {}
  int get_balance() const { return balance; }
  void set_balance(int amount) { balance = amount; }
};
 
int deposit(bank_accountBankAccount *from, bank_accountBankAccount *to, int amount) {
  // Create lock objects but defer locking them until later.
  std::unique_lock<std::mutex> lk1(from->balance_mutex>balanceMutex, std::defer_lock);
  std::unique_lock<std::mutex> lk2(to->balance_mutex>balanceMutex, std::defer_lock);

  // Lock all of the lock objects simultaneously.
  std::lock(lk1, lk2);

  if (from->get_balance() >= amount) {
    from->set_balance(from->get_balance() - amount);
    to->set_balance(to->get_balance() + amount);
    return 0;
  }
  return -1;
}
 
void f(bank_accountBankAccount *ba1, bank_accountBankAccount *ba2) {
  // Perform the deposits.
  std::thread thr1(deposit, ba1, ba2, 100);
  std::thread thr2(deposit, ba2, ba1, 100);
  thr1.join();
  thr2.join();
}

...