Fix Bug in Advanced Multithreaded Bank Account Simulation
You are given a broken multithreaded C++ program simulating deposits and withdrawals on a bank account. The code uses mutexes to protect the balance but still produces inconsistent results due to subtle bugs. Your task is to identify and fix these concurrency and logic bugs to ensure the balance always stays correct after concurrent operations.
Challenge prompt
Given the provided C++ code simulating multiple threads calling deposit and withdraw on a shared BankAccount object, the program sometimes shows incorrect fund totals or crashes due to race conditions or deadlocks. Identify the concurrency bugs and logic errors, fix them so that the final balance is always consistent with the performed operations, and the program runs safely across threads.
Guidance
- • Carefully examine the locking mechanism and ensure the balance updates use correct synchronization.
- • Check that the balance does not go negative and that no data races or deadlocks occur.
- • Use std::lock_guard or std::unique_lock properly to simplify mutex management.
- • Consider potential issues with unlocking mutexes too early or multiple mutexes causing lock order problems.
Hints
- • Look for places where the mutex is not properly locked or unlocked around balance updates.
- • Check if multiple mutexes in different threads can cause deadlock due to inconsistent lock order.
- • Consider atomic operations or scoped locks where appropriate.
Starter code
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
class BankAccount {
private:
int balance;
std::mutex m1;
std::mutex m2;
public:
BankAccount() : balance(0) {}
void deposit(int amount) {
m1.lock();
balance += amount;
m1.unlock();
}
bool withdraw(int amount) {
m2.lock();
if (balance >= amount) {
balance -= amount;
m2.unlock();
return true;
} else {
m2.unlock();
return false;
}
}
int getBalance() {
int bal;
m1.lock();
m2.lock();
bal = balance;
m2.unlock();
m1.unlock();
return bal;
}
};
void depositFunc(BankAccount& account) {
for (int i = 0; i < 1000; i++) {
account.deposit(1);
}
}
void withdrawFunc(BankAccount& account) {
for (int i = 0; i < 1000; i++) {
account.withdraw(1);
}
}
int main() {
BankAccount account;
std::thread t1(depositFunc, std::ref(account));
std::thread t2(withdrawFunc, std::ref(account));
t1.join();
t2.join();
std::cout << "Final balance: " << account.getBalance() << std::endl;
return 0;
}
Expected output
Final balance: 0
Core concepts
Challenge a Friend
Send this duel to someone else and see if they can solve it.