Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Mutexes are often used to prevent multiple threads from accessing critical resources at the same time. Sometimes, when locking mutexes, multiple threads hold each other's lock, and the program consequently deadlocks. There are four requirements for deadlock:

  • mutual Mutual exclusion
  • hold Hold and wait
  • no No preemption
  • circular Circular wait

Deadlock requires all four conditions, so to prevent deadlock, prevent any one of the four conditions. This guideline recommends locking the mutexes in a predefined order to prevent circular wait.

...

The solution to the deadlock problem is to use a predefined order for the locks in the deposit() function. In the following compliant solution, each thread will lock on the basis of the bank_account ID, defined in the struct initialization. This solution prevents the circular wait problem.:

Code Block
bgColor#ccccff
langc
typedef struct {
  int balance;
  mtx_t balance_mutex;
  unsigned int id; /* Should never be changed after initialized */
} bank_account;

unsigned int global_id = 1;

void create_bank_account(bank_account **ba, int initial_amount) {
  int result;
  bank_account *nba = malloc(sizeof(bank_account));
  if (nba == NULL) {
    /* Handle error */
  }

  nba->balance = initial_amount;
  result = mtx_init(&nba->balance_mutex, mtx_plain);
  if (result != thrd_success) {
    /* Handle error */
  }

  nba->id = global_id++;
  *ba = nba;
}


void *deposit(void *ptr) {
  deposit_thr_args *args = (deposit_thr_args *)ptr;
  int result;

  if (args->from->id == args->to->id)
		return;

  /* Ensure proper ordering for locking */
  if (args->from->id < args->to->id) {
    if ((result = mtx_lock(&(args->from->balance_mutex))) != thrd_success) {
      /* Handle error */
    }
    if ((result = mtx_lock(&(args->to->balance_mutex))) != thrd_success) {
      /* Handle error */
    }
  } else {
    if ((result = mtx_lock(&(args->to->balance_mutex))) != thrd_success) {
      /* Handle error */
    }
    if ((result = mtx_lock(&(args->from->balance_mutex))) != thrd_success) {
      /* Handle error */
    }
  }

  /* not enough balance to transfer */
  if (args->from->balance < args->amount) {
    if ((result = mtx_unlock(&(args->from->balance_mutex))) != thrd_success) {
      /* Handle error */
    }
    if ((result = mtx_unlock(&(args->to->balance_mutex))) != thrd_success) {
      /* Handle error */
    }
    return;
  }

  args->from->balance -= args->amount;
  args->to->balance += args->amount;

  if ((result = mtx_unlock(&(args->from->balance_mutex))) != thrd_success) {
    /* Handle error */
  }
  if ((result = mtx_unlock(&(args->to->balance_mutex))) != thrd_success) {
    /* Handle error */
  }


  free(ptr);
  return;
}

...