1

I've been working on a bankers algorithm implementation in C and it seems to work fine except the allocation matrix isn't adding values correctly. In the request resources function, I use a mutex lock at the beginning and unlock right before returning a value to indicate pass or failure. In the function itself, the allocation matrix updates with what's been requested and given but when another thread comes in and does it's request, the allocation resets and starts adding again. I'm not sure why this is since allocation is global like the other structures being modified in the function and they are updating values properly.

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    #include<semaphore.h>


    /* these may be any values >= 0 */

    #define NUMBER_OF_CUSTOMERS 5
    #define NUMBER_OF_RESOURCES 3

    /* the available amount of each resource */
    int available[NUMBER_OF_RESOURCES];

    /*the maximum demand of each customer */
    int maximum[NUMBER_OF_CUSTOMERS][NUMBER_OF_RESOURCES];

    /* the amount currently allocated to each customer */
    int allocation[NUMBER_OF_CUSTOMERS][NUMBER_OF_RESOURCES];

    /* the remaining need of each customer */
    int need[NUMBER_OF_CUSTOMERS][NUMBER_OF_RESOURCES];


    pthread_mutex_t mutex =
    PTHREAD_MUTEX_INITIALIZER;

    struct threadParams {
        int req[3];
        int threadNum;
    };

        int safe_state(int customer_num){

            int work[NUMBER_OF_RESOURCES];
            int done =0;
            for(int w = 0; w < NUMBER_OF_CUSTOMERS; w++){
                work[w] = available[w];
            }

            int finish[NUMBER_OF_CUSTOMERS];

            for(int i = 0; i < NUMBER_OF_CUSTOMERS; i++){
                finish[i] = 0;
                //setting finish to false
            }

            for(int k = 0; k < NUMBER_OF_CUSTOMERS; k++){
                for(int j = 0; j< NUMBER_OF_RESOURCES; j++){
                    if(finish[k] == 0 && need[customer_num][k] <= work[j]){
                        work[j] += allocation[customer_num][j];
                        finish[k] = 1;
                        //printf("%d\n", finish[k]);
                    }
                }
            }

            for(int x = 0; x < NUMBER_OF_CUSTOMERS; x++){
                if(finish[x] == 1){
                    done = 1;
                }
                else{
                    done = -1;
                }
            }
            if(done == 1){
                printf("\n Granted\n");
                return done;
            }
            printf("\nDenied\n");
            return done;

        }


        void* request_resources(void *arg){
            pthread_mutex_lock(&mutex);
            struct threadParams  *params = arg;
            int customer_num = params->threadNum;
            printf("\nCustomer %d is in critical\n", customer_num+1);           
            int request[3];
            request[0] = params->req[0];
            request[1] = params->req[1];
            request[2] = params->req[2];

            int pass;
            for(int i = 0; i < NUMBER_OF_RESOURCES; i++){
                if(request[i] <= need[customer_num][i] && request[i] <= available[i]){
                    //printf("\nreq: %d, need: %d, avail: %d, alloc: %d\n\t", request[i], need[customer_num][i], available[i],allocation[customer_num][i]);                 
                    int state = safe_state(customer_num);

                    if(state == 1){
                        available[i] -= request[i];
                        allocation[customer_num][i] += request[i];
                        //printf("%d + %d\n", allocation[customer_num][i], request[i]);
                        need[customer_num][i] -= request[i];
                        pass = 1;

                    }
                    else if(state == -1){
                        printf("\nThe request from customer %d results in unsafe state\n", customer_num+1);
                        printf("\nreq: %d, need: %d, avail: %d, alloc: %d\n\t", request[i], need[customer_num][i], available[i],allocation[customer_num][i]);                                                       
                        pass = -1;
                        break;
                    }
                }
                else{
                    printf("\nreq: %d, need: %d, avail: %d\n", request[i], need[customer_num][i], available[i]);
                    printf("\nNot enough resources for customer %d's request or the customer doesn't need this resource.\n", customer_num);
                    pass = -1;
                    break;
                }
                printf("\nResource: %d req: %d, need: %d, avail: %d, alloc: %d\n\t",i+1, request[i], need[customer_num][i], available[i],allocation[customer_num][i]);                              

            }
            //printf("I'm a thread\n");
            pthread_mutex_unlock(&mutex);
            printf("\n Customer %d has left critical\n", customer_num+1);
            return pass;

        } 

        int release_resources(int customer_num, int release[]){

        }




    int main(int argc, char *argv[]){
        pthread_t threads [NUMBER_OF_CUSTOMERS];
        int result;
        unsigned index = 0;


        // for(int ii = 0; ii < NUMBER_OF_CUSTOMERS; ii++){
            // for(int jj = 0; jj < NUMBER_OF_RESOURCES; jj++){
                // allocation[ii][jj] += 0;
            // }
        // }    

        for(index = 0; index < NUMBER_OF_RESOURCES; index++){
            available[index] = strtol(argv[index+1], NULL,10);
        }

        for(int i = 0; i < NUMBER_OF_CUSTOMERS; i++){
            for(int j = 0; j < NUMBER_OF_RESOURCES; j++){
            maximum[i][j] = strtol(argv[j+1], NULL, 10)-4;
            need[i][j] = 2; //strtol(argv[j+1], NULL, 10) - 6;
            //printf("%d\t", maximum[i][j]);
            }
        }


        for(index = 0; index < NUMBER_OF_CUSTOMERS; index++){
            printf("\nCreating customer %d\n", index+1);
            struct threadParams params;
            params.req[0] = 2;
            params.req[1] = 2;
            params.req[2] = 2;
            params.threadNum = index;
            result = pthread_create(&threads[index],NULL,request_resources,&params);    

        }

        for(index = 0; index < NUMBER_OF_CUSTOMERS; ++index){
            pthread_join(threads[index], NULL);
        }

        printf("\nDone");

    }
Jerum
  • 71
  • 2
  • 8

2 Answers2

2

After I fixed some issues I got a running version:

#ifdef __cplusplus
#include <cstdio>
#include <cstdlib>
#include <mutex>
#include <thread>
using namespace std;
#else /* (not) __cplusplus */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#endif /* __cplusplus */

/* these may be any values >= 0 */

#define NUMBER_OF_CUSTOMERS 5
#define NUMBER_OF_RESOURCES 3

/* the available amount of each resource */
static int available[NUMBER_OF_RESOURCES];

/*the maximum demand of each customer */
static int maximum[NUMBER_OF_CUSTOMERS][NUMBER_OF_RESOURCES];

/* the amount currently allocated to each customer */
static int allocation[NUMBER_OF_CUSTOMERS][NUMBER_OF_RESOURCES];

/* the remaining need of each customer */
static int need[NUMBER_OF_CUSTOMERS][NUMBER_OF_RESOURCES];

struct ThreadParams {
  int req[3];
  int threadNum;
};

typedef void* (*PThreadFunc)(void*);

#ifdef __cplusplus
/* multi-threading thin layer for C++ and std::thread */
static mutex mtx;
static inline void lockMutex(mutex *pMtx) { pMtx->lock(); }
static inline void unlockMutex(mutex *pMtx) { pMtx->unlock(); }
typedef std::thread Thread;
static inline int startThread(
  Thread *pThread,
  void* (*pThreadFunc)(ThreadParams*), ThreadParams *pThreadParams)
{
  return (*pThread = Thread(pThreadFunc, pThreadParams)).get_id()
    == Thread::id();
  /* thread creation failed -> thread id == thread::id() -> 1
   * thread creation successful -> thread id != thread::id() -> 0
   */
}
static inline void joinThread(Thread *pThread) { pThread->join(); }
#else /* (not) __cplusplus */
/* multi-threading thin layer for C and pthread */
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void lockMutex(pthread_mutex_t *pMtx)
{
  pthread_mutex_lock(pMtx);
}
static void unlockMutex(pthread_mutex_t *pMtx)
{
  pthread_mutex_unlock(pMtx);
}
typedef pthread_t Thread;
static int startThread(
  Thread *pThread,
  void* (*pThreadFunc)(struct ThreadParams*),
  struct ThreadParams *pThreadParams)
{
  return pthread_create(pThread, NULL,
    (void*(*)(void*))pThreadFunc, pThreadParams);
}
static void joinThread(Thread *pThread) { pthread_join(*pThread, NULL); }
#endif /* __cplusplus */

static int safe_state(int customer_num)
{
  int work[NUMBER_OF_RESOURCES];
  for (int i = 0; i < NUMBER_OF_RESOURCES; ++i) {
    work[i] = available[i];
  }

  int finish[NUMBER_OF_CUSTOMERS];
  for(int i = 0; i < NUMBER_OF_CUSTOMERS; ++i) {
    finish[i] = 0;
    //setting finish to false
  }

  for (int k = 0; k < NUMBER_OF_CUSTOMERS; ++k) {
    for (int j = 0; j < NUMBER_OF_RESOURCES; ++j) {
      if (finish[k] == 0 && need[customer_num][k] <= work[j]) {
        work[j] += allocation[customer_num][j];
        finish[k] = 1;
        //printf("%d\n", finish[k]);
      }
    }
  }

  int done = 0;
  for (int x = 0; x < NUMBER_OF_CUSTOMERS; ++x) {
    done = finish[x] ? 1 : -1;
  }
  if (done) printf("Granted\n");
  else printf("Denied\n");
  return done;
}

static void* request_resources(struct ThreadParams *params)
{
  lockMutex(&mtx);
  int customer_num = params->threadNum;
  printf("Customer %d is in critical\n", customer_num+1);           
  int request[3];
  request[0] = params->req[0];
  request[1] = params->req[1];
  request[2] = params->req[2];
  int pass;
  for (int i = 0; i < NUMBER_OF_RESOURCES; ++i) {
    if (request[i] <= need[customer_num][i] && request[i] <= available[i]) {
      //printf("\nreq: %d, need: %d, avail: %d, alloc: %d\n\t", request[i], need[customer_num][i], available[i],allocation[customer_num][i]);                 
      int state = safe_state(customer_num);

      if (state == 1) {
        available[i] -= request[i];
        allocation[customer_num][i] += request[i];
        //printf("%d + %d\n", allocation[customer_num][i], request[i]);
        need[customer_num][i] -= request[i];
        pass = 1;
      } else if (state == -1) {
        printf("The request from customer %d results in unsafe state\n",
          customer_num + 1);
        printf("req: %d, need: %d, avail: %d, alloc: %d\n",
          request[i], need[customer_num][i], available[i],
          allocation[customer_num][i]);
        pass = -1;
        break;
      }
    } else {
      printf("req: %d, need: %d, avail: %d\n",
        request[i], need[customer_num][i], available[i]);
      printf("Not enough resources for customer %d's request"
        " or the customer doesn't need this resource.\n", customer_num);
      pass = -1;
      break;
    }
    printf("Resource: %d req: %d, need: %d, avail: %d, alloc: %d\n",
      i + 1, request[i], need[customer_num][i], available[i],
      allocation[customer_num][i]);
  }
  //printf("I'm a thread\n");
  printf("Customer %d is about to left critical\n", customer_num + 1);
  unlockMutex(&mtx);
  return (void*)pass;
} 

static int release_resources(int customer_num, int release[]) {

}

int main(int argc, char *argv[])
{
  int input[NUMBER_OF_RESOURCES];
  /* default input */
  for (int i = 0; i < NUMBER_OF_RESOURCES; ++i) input[i] = 10;
  /* override default input with command line arguments if provided */
  if (argc > NUMBER_OF_RESOURCES) {
    for (int i = 0; i < NUMBER_OF_RESOURCES; ++i) {
      input[i] = strtol(argv[i + 1], NULL, 10);
    }
  }

  int result;
        // for(int ii = 0; ii < NUMBER_OF_CUSTOMERS; ii++){
            // for(int jj = 0; jj < NUMBER_OF_RESOURCES; jj++){
                // allocation[ii][jj] += 0;
            // }
        // }    

  for (int i = 0; i < NUMBER_OF_RESOURCES; ++i) {
    available[i] = input[i];
  }

  for(int i = 0; i < NUMBER_OF_CUSTOMERS; ++i) {
    for (int j = 0; j < NUMBER_OF_RESOURCES; ++j) {
      maximum[i][j] = input[j] - 4;
      need[i][j] = 2; //input[j] - 6;
      //printf("%d\t", maximum[i][j]);
    }
  }

  Thread threads[NUMBER_OF_CUSTOMERS];
  struct ThreadParams params[NUMBER_OF_CUSTOMERS];
  for (int i = 0; i < NUMBER_OF_CUSTOMERS; ++i) {
    printf("Creating customer %d\n", i + 1);
    params[i].req[0] = 2;
    params[i].req[1] = 2;
    params[i].req[2] = 2;
    params[i].threadNum = i;
    result = startThread(threads + i, &request_resources, params + i);
  }

  for (int i = 0; i < NUMBER_OF_CUSTOMERS; ++i) {
    joinThread(threads + i);
  }

  printf("Done\n");
  return 0;
}

Notes:

  1. I was afraid of the life-time of struct threadParams param (in main()). Thus I made it an array and moved its declaration to a scope where it lives longer than the threads. (This was my first recommendation.)

  2. A minor issue: I do not use post-fix inc./dec. if not necessary - a matter of taste.

  3. I introduced some #ifdef __cplusplus and a thin-layer to be able to switch between pthread (C) and std::thread (C++). I did this to be able to compile and debug it in VS2013 with its nice debugger.

  4. A minor issue: I prefixed all globals with static. I did this to prevent any potential issues with externs which might be declared in any header beyond my knowledge.

  5. A minor issue: I provided some default values for missing command line arguments to simplify tests.

  6. The VS2013 debugger uncovered another issue int work[NUMBER_OF_RESOURCES]; (in safe_state(), out of range access) which I fixed.

  7. A minor issue: I re-formatted the output a little bit to make it more compact.

I compiled and tested it in VS2013 on Windows 10 (64 bit) and got this output (usage of std::thread):

Creating customer 1
Customer 1 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 8, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 8, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 8, alloc: 2
Customer 1 is about to left critical
Creating customer 2
Customer 2 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 6, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 6, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 6, alloc: 2
Customer 2 is about to left critical
Creating customer 3
Customer 3 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 4, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 4, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 4, alloc: 2
Customer 3 is about to left critical
Creating customer 4
Customer 4 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 2, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 2, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 2, alloc: 2
Customer 4 is about to left critical
Creating customer 5
Customer 5 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 0, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 0, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 0, alloc: 2
Customer 5 is about to left critical
Done
Drücken Sie eine beliebige Taste . . .

I compiled and tested it also with gcc on cygwin (using pthread):

$ gcc -std=c11 -x c bankers.cc -o bankers -pthread

$ ./bankers
Creating customer 1
Creating customer 2
Customer 1 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 8, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 8, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 8, alloc: 2
Customer 1 is about to left critical
Creating customer 3
Customer 2 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 6, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 6, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 6, alloc: 2
Customer 2 is about to left critical
Creating customer 4
Customer 3 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 4, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 4, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 4, alloc: 2
Customer 3 is about to left critical
Creating customer 5
Customer 4 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 2, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 2, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 2, alloc: 2
Customer 4 is about to left critical
Customer 5 is in critical
Granted
Resource: 1 req: 2, need: 0, avail: 0, alloc: 2
Granted
Resource: 2 req: 2, need: 0, avail: 0, alloc: 2
Granted
Resource: 3 req: 2, need: 0, avail: 0, alloc: 2
Customer 5 is about to left critical
Done
Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • Hmm @Scheff, it seems the matrix still isn't updating properly after implementing this change. Perhaps I should make the struct global or move my pthread_create outside of the loop? – Jerum Mar 28 '17 at 09:45
  • 1
    @Jerum I believe both is not necessary. I'm afraid there are other issues also. I could have a look at it later again. (I'm currently at work.) – Scheff's Cat Mar 28 '17 at 09:47
  • thanks. It's still confusing why only the allocation matrix is the one not updating when the others are. – Jerum Mar 28 '17 at 13:52
  • Yes I'm testing with 10 10 10 , the program takes 3 arguments. It should allocate with those but for values 9 9 9 and below it should deny the request and cause an unsafe state but still allocates the request. – Jerum Mar 28 '17 at 17:27
  • ah I see. Hmm after fixing that it's still not properly updating my allocation matrix. – Jerum Mar 28 '17 at 18:32
  • @Jerum Can you please compare the posted output with your one and specify what's expected (instead)? – Scheff's Cat Mar 29 '17 at 05:51
  • So yes, my ouput is the same. What should be happening however, is that allocation should be increasing by 2 each time. Because all the resources run and asks for two, so at the end allocation should be 10. I've made major changes to my code however. I'll post an updated version later this afternoon when I'm free. – Jerum Mar 29 '17 at 13:39
  • here's the latest code @Scheff , avail and alloc aren't working properly. Upon further notice and printing the values when operation on the structures I got large int values that shouldn't be there. This leads me to believe there's an overflow occurring somewhere. – Jerum Mar 29 '17 at 21:15
  • @Jerum Sorry, where can I find the code? (Did I miss something?) – Scheff's Cat Mar 30 '17 at 05:25
1

Finally, I found some time to handle this seriously:

I still have problems to understand your implementation in general. (I spent some hours to first understand Edsger Dijkstras Banker's algorithm itself before I came back.)

Thus, I do not understand why you distinguish need[] and threadParams.req[]. When I got it right this should actually be the same (i.e. one of them should not be there).

However, I want to show you what I got so far (which might be a help). After I studied and compared multiple samples I finally made a modified version of Banker's Algorithm on rosettacode.org:

#ifdef __cplusplus
#include <cassert>
#include <cstdio>
#include <cstring>
using namespace std;
#else /* (not) __cplusplus */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#endif /* __cplusplus */

enum { nResources = 4 };
enum { nCustomers = 3 };

struct System {
  /* the total amount of resources */
  int total[nResources];
  /* the available amount of each resource */
  int available[nResources];
  /* currently allocated resources */
  int allocation[nCustomers][nResources];
  /* the maximum demand of each customer */
  int maximum[nCustomers][nResources];
};

static struct System testSetSafe1 = {
  /* the total amount of resources */
  { 6, 5, 7, 6 },
  /* the available amount of each resource */
  { },
  /* currently allocated resources */
  {
    { 1, 2, 2, 1 },
    { 1, 0, 3, 3 },
    { 1, 2, 1, 0 }
  },
  /* the maximum demand of each customer */
  {
    { 3, 3, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 3, 5, 0 }
  }
};

static struct System testSetSafe2 = {
  /* the total amount of resources */
  { 6, 5, 7, 6 },
  /* the available amount of each resource */
  { },
  /* currently allocated resources */
  {
    { 1, 0, 0, 1 },
    { 1, 0, 3, 3 },
    { 1, 2, 1, 0 }
  },
  /* the maximum demand of each customer */
  {
    { 5, 3, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 3, 5, 0 }
  }
};

static struct System testSetUnsafe = {
  /* the total amount of resources */
  { 6, 5, 7, 6 },
  /* the available amount of each resource */
  { },
  /* currently allocated resources */
  {
    { 1, 2, 2, 1 },
    { 1, 0, 3, 3 },
    { 1, 2, 1, 0 }
  },
  /* the maximum demand of each customer */
  {
    { 5, 3, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 3, 5, 0 }
  }
};

void initSystem(struct System *pSystem)
{
  for (int j = 0; j < nResources; ++j) {
    pSystem->available[j] = pSystem->total[j];
    for (int i = 0; i < nCustomers; ++i) {
      pSystem->available[j] -= pSystem->allocation[i][j];
    }
  }
}

void printR(const char *title, int table[nResources])
{
  printf("%s:\n", title);
  for (int j = 0; j < nResources; ++j) printf("\t%c", 'A' + j);
  printf("\n");
  for (int j = 0; j < nResources; ++j) printf("\t%d", table[j]);
  printf("\n");
}

void printCR(const char *title, int table[nCustomers][nResources])
{
  printf("%s:\n", title);
  for (int j = 0; j < nResources; ++j) printf("\t%c", 'A' + j);
  printf("\n");
  for (int i = 0; i < nCustomers; ++i) {
    printf("C%d", i + 1);
    for (int j = 0; j < nResources; ++j) printf("\t%d", table[i][j]);
    printf("\n");
  }
}

int run(struct System *pSystem)
{
  initSystem(pSystem);
  printR("Total resources in system", pSystem->total);
  printR("Available resources", pSystem->available);
  printCR("Customers (currently allocated resources)",
    pSystem->allocation);
  printCR("Customers (maximum required resources", pSystem->maximum);
  int running[nCustomers];
  for (int count = nCustomers, safe; count;) {
    safe = 0;
    for (int i = 0; i < nCustomers; ++i) {
      if (running[i]) {
        int needed[nResources], blocked = 0;
        for (int j = 0, block; j < nResources; ++j) {
          needed[j]
            = pSystem->maximum[i][j] - pSystem->allocation[i][j];
          if ((block = needed[j] > pSystem->available[j])) {
            printf("Customer %d blocked due to resource %c\n",
              i + 1, 'A' + j);
          }
          blocked |= block;
        }
        if (!blocked) {
          printf("Customer %d is served.\n", i + 1);
          /* allocate resources */
          for (int j = 0; j < nResources; ++j) {
            pSystem->available[j] -= needed[j];
            pSystem->allocation[i][j] += needed[j];
            assert(pSystem->allocation[i][j] == pSystem->maximum[i][j]);
          }
          /* perform customer */
          printR("Allocated resources", pSystem->allocation[i]);
          running[i] = 0;
          printf("Customer %d is done.\n", i + 1);
          --count; /* customer finished */
          safe = 1; /* processes still safe (no deadlock) */
          /* free resources */
          for (int j = 0; j < nResources; ++j) {
            pSystem->available[j] += pSystem->allocation[i][j];
            pSystem->allocation[i][j] = 0;
          }
          break; /* bail out of inner loop */
        }
      }
    }
    if (!safe) {
      printf("Unsafe state (i.e. dead lock).\n");
      printR("Total resources in system", pSystem->total);
      printR("Available resources", pSystem->available);
      printCR("Customers (currently allocated resources)",
        pSystem->allocation);
      printCR("Customers (maximum required resources",
        pSystem->maximum);
      return -1;
    }
    printR("Available resources", pSystem->available);
  }
  return 0;
}

int main()
{
  /* 1st try: all requests can be granted soon */
  printf(
    "1st Run:\n"
    "========\n"
    "\n");
  run(&testSetSafe1);
  printf("\n");
  /* 2nd try: all requests can be granted by changing order */
  printf("2nd Run:\n"
    "========\n"
    "\n");
  run(&testSetSafe2);
  printf("\n");
  /* 3rd try: unsafe state */
  printf("3rd Run:\n"
    "========\n"
    "\n");
  run(&testSetUnsafe);
  printf("\n");
  /* done */
  printf("Done.\n");
  return 0;
}

(Debugged in VS2013 but) compiled and tested with gcc in cygwin on Windows 10 (64 bit):

$ gcc -std=c11 -x c bankers.cc -o bankers

$ ./bankers
1st Run:
========

Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       1       1       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       2       2       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      3       3       2       2
C2      1       2       3       4
C3      1       3       5       0
Customer 1 is served.
Allocated resources:
        A       B       C       D
        3       3       2       2
Customer 1 is done.
Available resources:
        A       B       C       D
        4       3       3       3
Customer 2 is served.
Allocated resources:
        A       B       C       D
        1       2       3       4
Customer 2 is done.
Available resources:
        A       B       C       D
        5       3       6       6
Customer 3 is served.
Allocated resources:
        A       B       C       D
        1       3       5       0
Customer 3 is done.
Available resources:
        A       B       C       D
        6       5       7       6

2nd Run:
========

Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       3       3       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       0       0       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      5       3       2       2
C2      1       2       3       4
C3      1       3       5       0
Customer 1 blocked due to resource A
Customer 2 is served.
Allocated resources:
        A       B       C       D
        1       2       3       4
Customer 2 is done.
Available resources:
        A       B       C       D
        4       3       6       5
Customer 1 is served.
Allocated resources:
        A       B       C       D
        5       3       2       2
Customer 1 is done.
Available resources:
        A       B       C       D
        5       3       6       6
Customer 3 is served.
Allocated resources:
        A       B       C       D
        1       3       5       0
Customer 3 is done.
Available resources:
        A       B       C       D
        6       5       7       6

3rd Run:
========

Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       1       1       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       2       2       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      5       3       2       2
C2      1       2       3       4
C3      1       3       5       0
Customer 1 blocked due to resource A
Customer 2 blocked due to resource B
Customer 3 blocked due to resource C
Unsafe state (i.e. dead lock).
Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       1       1       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       2       2       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      5       3       2       2
C2      1       2       3       4
C3      1       3       5       0

Done.

$

This looks quite good (for me).

Now, I made a derived sample introducing multi-threading.

A note about this:

The Wikipedia article Banker's algorithm states that the algorithm is intended for OS resource management like e.g. memory, semaphores and interface access. Sounds for me like, these algorithm is intended to manage things like muteces. Thus, a mutex should/can not be used for it and multi-threading makes not so much sense.

However, let forget about my concern and say this is for educational/understanding purpose:

#ifdef __cplusplus
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <thread>
using namespace std;
#else /* (not) __cplusplus */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#endif /* __cplusplus */

enum { nResources = 4 };
enum { nCustomers = 3 };

struct System {
  /* the total amount of resources */
  int total[nResources];
  /* the available amount of each resource */
  int available[nResources];
  /* currently allocated resources */
  int allocation[nCustomers][nResources];
  /* the maximum demand of each customer */
  int maximum[nCustomers][nResources];
  /* customers to serve, blocked customers */
  int running, blocked;
};

static struct System testSetSafe1 = {
  /* the total amount of resources */
  { 6, 5, 7, 6 },
  /* the available amount of each resource */
  { },
  /* currently allocated resources */
  {
    { 1, 2, 2, 1 },
    { 1, 0, 3, 3 },
    { 1, 2, 1, 0 }
  },
  /* the maximum demand of each customer */
  {
    { 3, 3, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 3, 5, 0 }
  }
};

static struct System testSetSafe2 = {
  /* the total amount of resources */
  { 6, 5, 7, 6 },
  /* the available amount of each resource */
  { },
  /* currently allocated resources */
  {
    { 1, 0, 0, 1 },
    { 1, 0, 3, 3 },
    { 1, 2, 1, 0 }
  },
  /* the maximum demand of each customer */
  {
    { 5, 3, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 3, 5, 0 }
  }
};

static struct System testSetUnsafe = {
  /* the total amount of resources */
  { 6, 5, 7, 6 },
  /* the available amount of each resource */
  { },
  /* currently allocated resources */
  {
    { 1, 2, 2, 1 },
    { 1, 0, 3, 3 },
    { 1, 2, 1, 0 }
  },
  /* the maximum demand of each customer */
  {
    { 5, 3, 2, 2 },
    { 1, 2, 3, 4 },
    { 1, 3, 5, 0 }
  }
};

void initSystem(struct System *pSystem)
{
  for (int j = 0; j < nResources; ++j) {
    pSystem->available[j] = pSystem->total[j];
    for (int i = 0; i < nCustomers; ++i) {
      pSystem->available[j] -= pSystem->allocation[i][j];
    }
  }
  pSystem->running = nCustomers; pSystem->blocked = 0;
}

void printR(const char *title, int table[nResources])
{
  printf("%s:\n", title);
  for (int j = 0; j < nResources; ++j) printf("\t%c", 'A' + j);
  printf("\n");
  for (int j = 0; j < nResources; ++j) printf("\t%d", table[j]);
  printf("\n");
}

void printCR(const char *title, int table[nCustomers][nResources])
{
  printf("%s:\n", title);
  for (int j = 0; j < nResources; ++j) printf("\t%c", 'A' + j);
  printf("\n");
  for (int i = 0; i < nCustomers; ++i) {
    printf("C%d", i + 1);
    for (int j = 0; j < nResources; ++j) printf("\t%d", table[i][j]);
    printf("\n");
  }
}

struct Customer {
  int i;
  struct System *pSystem;
  int blocked;
};

static void initCustomer(
  struct Customer *pCustomer, int i, struct System *pSystem)
{
  pCustomer->i = i;
  pCustomer->pSystem = pSystem;
  pCustomer->blocked = 0;
}

#ifdef __cplusplus
/* multi-threading thin layer for C++ and std::thread */
static mutex mtx;
static inline void lockMutex(mutex *pMtx) { pMtx->lock(); }
static inline void unlockMutex(mutex *pMtx) { pMtx->unlock(); }
typedef std::thread Thread;
static inline int startThread(
  Thread *pThread,
  void* (*pThreadFunc)(Customer*), Customer *pCustomer)
{
  return (*pThread = Thread(pThreadFunc, pCustomer)).get_id()
    == Thread::id();
  /* thread creation failed -> thread id == thread::id() -> 1
   * thread creation successful -> thread id != thread::id() -> 0
   */
}
static inline void joinThread(Thread *pThread) { pThread->join(); }
#else /* (not) __cplusplus */
/* multi-threading thin layer for C and pthread */
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void lockMutex(pthread_mutex_t *pMtx)
{
  pthread_mutex_lock(pMtx);
}
static void unlockMutex(pthread_mutex_t *pMtx)
{
  pthread_mutex_unlock(pMtx);
}
typedef pthread_t Thread;
static int startThread(
  Thread *pThread,
  void* (*pThreadFunc)(struct Customer*),
  struct Customer *pCustomer)
{
  return pthread_create(pThread, NULL,
    (void*(*)(void*))pThreadFunc, pCustomer);
}
static void joinThread(Thread *pThread) { pthread_join(*pThread, NULL); }
#endif /* __cplusplus */

void* runCustomer(struct Customer *pCustomer)
{
  int i = pCustomer->i;
  struct System *pSystem = pCustomer->pSystem;
  int needed[nResources];
  for (int j = 0; j < nResources; ++j) {
    needed[j] = pSystem->maximum[i][j] - pSystem->allocation[i][j];
  }
  for (int done = 0; !done;) {
    lockMutex(&mtx); /* thread-safe access to shared system */
    if (pCustomer->blocked) --pSystem->blocked;
    pCustomer->blocked = 0;
    for (int j = 0, block; j < nResources; ++j) {
      if ((block = needed[j] > pSystem->available[j])) {
        printf("Customer %d blocked due to resource %c\n",
          i + 1, 'A' + j);
      }
      pCustomer->blocked |= block;
    }
    if (!pCustomer->blocked) {
      printf("Customer %d is served.\n", i + 1);
      /* allocate resources */
      for (int j = 0; j < nResources; ++j) {
        pSystem->available[j] -= needed[j];
        pSystem->allocation[i][j] += needed[j];
        assert(pSystem->allocation[i][j] == pSystem->maximum[i][j]);
      }
      /* perform customer */
      printR("Allocated resources", pSystem->allocation[i]);
      --pSystem->running;
      done = 1; /* customer finished */
      printf("Customer %d is done.\n", i + 1);
      /* free resources */
      for (int j = 0; j < nResources; ++j) {
        pSystem->available[j] += pSystem->allocation[i][j];
        pSystem->allocation[i][j] = 0;
      }
    } else {
      ++pSystem->blocked;
      if ((done = pSystem->running <= pSystem->blocked)) {
        printf("Customer %d exited (due to dead-lock).\n", i + 1);
      }
    }
    unlockMutex(&mtx);
  }
  return 0;
}

int run(struct System *pSystem)
{
  initSystem(pSystem);
  printR("Total resources in system", pSystem->total);
  printR("Available resources", pSystem->available);
  printCR("Customers (currently allocated resources)",
    pSystem->allocation);
  printCR("Customers (maximum required resources", pSystem->maximum);
  /* created threads for customers */
  lockMutex(&mtx); /* force concurrency a little bit */
  Thread threads[nCustomers];
  struct Customer customers[nCustomers];
  for (int i = 0; i < nCustomers; ++i) {
    printf("Creating customer %d\n", i + 1);
    initCustomer(customers + i, i, pSystem);
    if (startThread(threads + i, &runCustomer, customers + i)) {
      printf("ERROR: Failed to start thread for customer %d!\n", i + 1);
    }
  }
  /* unlock mutex to let threads compete */
  printf("Ready, steady, go...\n");
  unlockMutex(&mtx);
  /* join all threads */
  for (int i = 0; i < nCustomers; ++i) joinThread(threads + i);
  /* report */
  if (pSystem->blocked) {
    printf("Unsafe state (i.e. dead lock).\n");
    printR("Total resources in system", pSystem->total);
    printR("Available resources", pSystem->available);
    printCR("Customers (currently allocated resources)",
      pSystem->allocation);
    printCR("Customers (maximum required resources",
      pSystem->maximum);
    return -1;
  }
  return 0;
}

int main()
{
  /* 1st try: all requests can be granted soon */
  printf(
    "1st Run:\n"
    "========\n"
    "\n");
  run(&testSetSafe1);
  printf("\n");
  /* 2nd try: all requests can be granted by changing order */
  printf("2nd Run:\n"
    "========\n"
    "\n");
  run(&testSetSafe2);
  printf("\n");
  /* 3rd try: unsafe state */
  printf("3rd Run:\n"
    "========\n"
    "\n");
  run(&testSetUnsafe);
  printf("\n");
  /* done */
  printf("Done.\n");
  return 0;
}

(Again debugged in VS2013 but) compiled and tested with gcc in cygwin on Windows 10 (64 bit):

$ gcc -std=c11 -x c bankersMT.cc -o bankersMT -pthread

$ ./bankersMT
1st Run:
========

Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       1       1       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       2       2       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      3       3       2       2
C2      1       2       3       4
C3      1       3       5       0
Creating customer 1
Creating customer 2
Creating customer 3
Ready, steady, go...
Customer 1 is served.
Allocated resources:
        A       B       C       D
        3       3       2       2
Customer 1 is done.
Customer 2 is served.
Allocated resources:
        A       B       C       D
        1       2       3       4
Customer 2 is done.
Customer 3 is served.
Allocated resources:
        A       B       C       D
        1       3       5       0
Customer 3 is done.

2nd Run:
========

Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       3       3       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       0       0       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      5       3       2       2
C2      1       2       3       4
C3      1       3       5       0
Creating customer 1
Creating customer 2
Creating customer 3
Ready, steady, go...
Customer 1 blocked due to resource A
Customer 2 is served.
Allocated resources:
        A       B       C       D
        1       2       3       4
Customer 2 is done.
Customer 3 is served.
Allocated resources:
        A       B       C       D
        1       3       5       0
Customer 3 is done.
Customer 1 is served.
Allocated resources:
        A       B       C       D
        5       3       2       2
Customer 1 is done.

3rd Run:
========

Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       1       1       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       2       2       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      5       3       2       2
C2      1       2       3       4
C3      1       3       5       0
Creating customer 1
Creating customer 2
Creating customer 3
Ready, steady, go...
Customer 1 blocked due to resource A
Customer 2 blocked due to resource B
Customer 3 blocked due to resource C
Customer 3 exited (due to dead-lock).
Customer 1 blocked due to resource A
Customer 1 exited (due to dead-lock).
Customer 2 blocked due to resource B
Customer 2 exited (due to dead-lock).
Unsafe state (i.e. dead lock).
Total resources in system:
        A       B       C       D
        6       5       7       6
Available resources:
        A       B       C       D
        3       1       1       2
Customers (currently allocated resources):
        A       B       C       D
C1      1       2       2       1
C2      1       0       3       3
C3      1       2       1       0
Customers (maximum required resources:
        A       B       C       D
C1      5       3       2       2
C2      1       2       3       4
C3      1       3       5       0

Done.

$

Although, this looks quite good also, I'm not sure about whether the dead-lock detection is 100 % correct. Because multi-threading introduces non-deterministics, this is hard to test. I tried to "debug it by head" but ended up with headache...

Note about implementation:

I used the thin-layer for multi-threading, again. Thus, these samples can be compiled in C++ (std::thread, VS2013/g++) as well as in C (pthread, gcc).

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45