Versions Compared

Key

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

...

Noncompliant Code Example

Based on The following code has behavior which is dependend on the runtime environment and the scheduler on the operating system, the following code will have different behaviorsplatform's scheduler. However, with proper timing, the main() function will deadlock when running thr1 and thr2 in which thr1 tries to lock ba2's mutex while thr2 tries to lock on ba1's mutex in the deposit() function and the program will not progress.

Code Block
bgColor#ffcccc
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

typedef struct {
  int balance;
  pthread_mutex_t balance_mutex; 
} bank_account;

typedef struct {
  bank_account *from;
  bank_account *to;
  int amount;
} deposit_thr_args;

/* return negative on error */
int void create_bank_account(bank_account **ba, int initial_amount) {

  int retresult;
  bank_account *nba = malloc(sizeof(bank_account));
  if (nba == NULL) {
    /* return -1;Handle Error */
  }

  nba->balance = initial_amount;
  retresult = pthread_mutex_init(&nba->balance_mutex, NULL);
  if (retresult) {
    /* Handle Error */
  exit(ret);}

  *ba = nba;

  return 0;
}


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

  if ((result = pthread_mutex_lock(&(args->from->balance_mutex))); != 0) {
    /* Handle Error */
  }

  /* not enough balance to transfer */
  if (args->from->balance < args->amount) {
    if ((result = pthread_mutex_unlock(&(args->from->balance_mutex));) != 0) {
      /* Handle Error  */
    }
    return NULL;
  }

  if ((result = pthread_mutex_lock(&(args->to->balance_mutex));)) != 0) {
    /* Handle Error */
  }

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

  if ((result = pthread_mutex_unlock(&(args->from->balance_mutex)));
   != 0) {
    /* Handle Error */
  }
  if ((result = pthread_mutex_unlock(&(args->to->balance_mutex))); != 0) {
    /* Handle Error */
  }


  free(ptr);
  return NULL;
}


int main(void) {

  pthread_t thr1, thr2;
  int errresult;

  bank_account *ba1,;
  bank_account *ba2;

  err = create_bank_account(&ba1, 1000);
  if (err < 0) 
    exit(err);

  err = create_bank_account(&ba2, 1000);
  if (err < 0) 
    exit(err);

  deposit_thr_args *arg1 = malloc(sizeof(deposit_thr_args));
  if (arg1 == NULL) {
    /*  exit(-1);
Handle Error */
  }
  deposit_thr_args *arg2 = malloc(sizeof(deposit_thr_args));
  if (arg2 == NULL) {
    exit(-1);/* Handle Error */
  }

  arg1->from = ba1;
  arg1->to = ba2;
  arg1->amount = 100;

  arg2->from = ba2;
  arg2->to = ba1;
  arg2->amount = 100;

  /* perform the depositdeposits */
  if err((result = pthread_create(&thr1, NULL, deposit, (void *)arg1);) != 0) {
  if (err)
 /* Handle  exit(err);
Error */
  }
  errif ((result = pthread_create(&thr2, NULL, deposit, (void *)arg2);)) != 0) {
  if (err)
    exit(err);  /* Handle Error */
  }

  pthread_exit(NULL);

  return 0;
}

Compliant Solution

The solution to the deadlock problem is to lock in use a predefined order for the locks in the deposit() function. In the following examplecompliant solution, each thread will lock based on the id of bank_account's id defined in the struct initialization. This way prevents the circular wait problem is avoided and when one thread requires a lock will guarantee it will require the next lock.

Code Block
bgColor#ccccff


typedef struct {
  int balance;
  pthread_mutex_t balance_mutex; 
  unsigned int id; /* read only and should never be changed after initialized */
} bank_account;

unsigned int global_id = 1;

/* return negative on error */
int void create_bank_account(bank_account **ba, int initial_amount) {

  int retresult;
  bank_account *nba = malloc(sizeof(bank_account));

  if (nba == NULL) {
    /* Handle return -1;Error */
  }

  nba->balance = initial_amount;
  retresult = pthread_mutex_init(&nba->balance_mutex, NULL);
  if (ret)
result != 0) {
    /*  exit(ret);Handle Error */
  }

  nba->id = global_id++;

  *ba = nba;

  return 0;
}


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

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

  /* ensure proper ordering for locking */
  if (args->from->id < args->to->id) {
    if ((result = pthread_mutex_lock(&(args->from->balance_mutex));) != 0) {
      /* Handle Error */
    }
    if ((result = pthread_mutex_lock(&(args->to->balance_mutex));)) != 0) {
      /* Handle Error */
    }
  } else {
    if ((result = pthread_mutex_lock(&(args->to->balance_mutex))));
 != 0) {
      /* Handle Error */
    }
    if ((result = pthread_mutex_lock(&(args->from->balance_mutex));) != 0) {
      /* Handle Error */
    }
  }

  /* not enough balance to transfer */
  if (args->from->balance < args->amount) {
    if ((result = pthread_mutex_unlock(&(args->from->balance_mutex))));
    != 0) {
      /* Handle Error */
    }
    if ((result = pthread_mutex_unlock(&(args->to->balance_mutex));))) != 0) {
      /* Handle Error */
    }
    return NULL;
  }

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

  if ((result = pthread_mutex_unlock(&(args->from->balance_mutex));
 )) != 0) {
    /* Handle Error */
  }
  if ((result = pthread_mutex_unlock(&(args->to->balance_mutex));)) != 0) {
    /* Handle Error */
  }


  free(ptr);

  return NULL;
}

Risk Assessment

Deadlock causes multiple threads to become unable to progress and thus halts the executing program. This is a potential denial-of-service attack because the attacker can force deadlock situations. It is likely for deadlock to occur in multi-threaded programs that manage multiple shared resources.

...