...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h><mutex> #include <threads.h><thread> typedef struct { int balance; mtx_tstd::mutex balance_mutex; } bank_account; typedef struct { bank_account *from; bank_account *to; int amount; } transaction; void create_bank_account(bank_account **ba, int initial_amount) { bank_account *nba = (new bank_account *)malloc( sizeof(bank_account) ); if (nba == NULL) { /* Handle error */ } nba->balance = initial_amount; if (thrd_success != mtx_init(&nba->balance_mutex, mtx_plain)) { /* Handle error */ } *ba = nba; } int deposit(voidbank_account *ptr) { transaction *args = (transaction *)ptr; if (thrd_success != mtx_lock(&args->from->balance_mutex)) { /* Handle error */ } from, bank_account *to, int amount) { std::lock_guard<std::mutex> from_guard(from->balance_mutex); /* Not enough balance to transfer */ if (args->fromfrom->balance < args->amount) { if (thrd_success != mtx_unlock(&args->from->balance_mutex)amount) { /* Handle error */ } return -1; /* Indicate error */ } if (thrd_success != mtx_lock(&args->to->balance_mutex)) { /* Handle error */ } args->from-std::lock_guard<std::mutex> to_guard(to->balance_mutex); from->balance -= args->amountamount; args->toto->balance += args->amount; if (thrd_success != mtx_unlock(&args->from->balance_mutex)) { /* Handle error */ } if (thrd_success != mtx_unlock(&args->to->balance_mutex)) { /* Handle error */ } free(ptr);amount; return 0; } int main(void) { thrd_t thr1, thr2; transaction *arg1; transaction *arg2; bank_account *ba1; bank_account *ba2; create_bank_account(&ba1, 1000); create_bank_account(&ba2, 1000); arg1 = (transaction *)malloc(sizeof(transaction)); if (arg1 == NULL) { /* HandlePerform the errordeposits */ } arg2 = (transaction *)malloc(sizeof(transaction)); if (arg2 == NULL) { /* Handle error */ } arg1->from = ba1; arg1->to = ba2; arg1->amount = 100; arg2->from = ba2; arg2->to = ba1; arg2->amount = 100; /* Perform the deposits */ if (thrd_success != thrd_create(&thr1, deposit, (void *)arg1)) { /* Handle error */ } if (thrd_success != thrd_create(&thr2, deposit, (void *)arg2)) { /* Handle error */ } std::thread thr1(deposit, ba1, ba2, 100); std::thread thr2(deposit, ba2, ba1, 100); thr1.join(); thr2.join(); return 0; } |
Compliant Solution
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
ID, which is set when the bank_account struct
is initialized.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h><mutex> #include <threads.h><thread> typedef struct { int balance; mtx_tstd::mutex balance_mutex; /* Should not change after initialization */ unsigned int id; } bank_account; typedef struct { bank_account *from; bank_account *to; int amount; } transaction; unsigned int global_id = 1; void create_bank_account(bank_account **ba, int initial_amount) { bank_account *nba = (new bank_account *)malloc( sizeof(bank_account) ); if (nba == NULL) { /* Handle error */ } nba->balance = initial_amount; if (thrd_success != mtx_init(&nba->balance_mutex, mtx_plain)) { /* Handle error */ } nba->id = global_id++; *ba = nba; } int deposit(voidbank_account *ptr) { transaction *args = (transaction *)ptr;from, bank_account *to, int amount) { int result = -1; mtx_tstd::mutex *first; mtx_tstd::mutex *second; if (argsfrom->from->id == args->toto->id) { return -1; /* Indicate error */ } /* Ensure proper ordering for locking */ if (args->fromfrom->id < argsto->to->id) { first = &args->fromfrom->balance_mutex; second = &argsto->to->balance_mutex; } else { first = &argsto->to->balance_mutex; second = &argsfrom->from->balance_mutex; } if (thrd_success != mtx_lock(first)) { /* Handle error */ } if (thrd_success != mtx_lock(second)) { /* Handle error */ } std::lock_guard<std::mutex> first_guard(*first); std::lock_guard<std::mutex> second_guard(*second); /* Not enough balance to transfer */ if (argsfrom->from->balance >= args->amountamount) { args->fromfrom->balance -= args->amountamount; args->toto->balance += args->amountamount; result = 0; } if (thrd_success != mtx_unlock(second)) { /* Handle error */ } if (thrd_success != mtx_unlock(first)) { /* Handle error */ } free(ptr); return result; } |
Risk Assessment
Deadlock prevents multiple threads from progressing, halting program execution. A denial-of-service attack is possible if the attacker can create the conditions for deadlock.
...