Lab 03: Token Processing using Synchronisation Primitives


In this lab, you will write code for the following application. There is a Railway Reservation Office containing four entrances (D1 ... D4) and 5 counters (C1 ... C5) as shown in the diagram below.
No description has been provided for this image
There are four token issuing machines (T1, T2, T3, T4), one at each entrance. A person entering the Reservation Office picks a token at the nearest token issuing machine. The counters display the token number for processing.

Implementing the application requires you to create token issuing machine threads and counters threads. Your task is to fill the gaps in the code given below. All the token issuing machines are interconnected in that they issue token numbers in a synchronised fashion. A token issuing machine must issue a token number in strict ascending order without skipping any number. A maximum of MAXTOKENS tokens will be issued per day.

A counter must process the tokens also in strict ascending order. No token number can be called out of sequence and it must remain synchronised between all counters. No counter can call a token number that has not yet been issued.

The application terminates when all MAXTOKENS have been issued and processed successfully.


Submission Instructions

  • You are free to do this lab task either in a jupyter notebook or in your favourite programming environment.
  • The code below is generic, i.e. you can specify the number of token issuing machines and number of counters as command line arguments.
  • Once you are done, submit your code and the output from the program for the case when number of token issuing machines = 4 and counters = 5.
  • Zip all your files into a file called <Your roll number>.zip and email it to me at
    chakravarthybhagvati@uohyd.ac.in
    with the subject line containing AOSLab2026 Lab 03.
In [ ]:
%%file token_processing.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define MAXTOKENS       5000
#define TOKENISSUERS    300
#define COUNTERS        300
#define MIN(A, B)       ((A) < (B)) ? (A) : (B)

int num_issuers, num_counters;
struct {
     int token_table[MAXTOKENS];
     int token_issued;
     int token_handled;
} tokens;

struct {
     pthread_mutex_t mutex;
     pthread_mutex_t cmutex;
     pthread_cond_t cond;
} synchs = {
     PTHREAD_MUTEX_INITIALIZER,
     PTHREAD_MUTEX_INITIALIZER,
     PTHREAD_COND_INITIALIZER
};

void *issuer(void *);
void *counter(void *);
void verifier(void);

int main(int argc, char *argv[])
{
     int i, sum=0;
     extern int num_issuers, num_counters;
     pthread_t tid[TOKENISSUERS];
     pthread_t cid[COUNTERS];
     int numtok[COUNTERS];

     /**** Initialise all relevant parameters ****/
     tokens.token_issued = 0;
     tokens.token_handled = 0;
     for (i=0; i<MAXTOKENS; i++)
          tokens.token_table[i] = 0;
     if (argc != 3) {
          fprintf(stderr, "Usage\n%s <num token issuers> <num counters>\n",
                  argv[0]);
          exit(1);
     }
     num_issuers = MIN(TOKENISSUERS, atoi(argv[1]));
     num_counters = MIN(COUNTERS, atoi(argv[2]));

     /**** Create necessary threads for token issuers T1, T2, ...  ****/

     /**** Create necessary counters C1, C2, ... ****/
     /**** numtok[i] keeps track of number of tokens processed
           at the ith counter                                  ****/
     for (i=0; i<num_counters; i++) {
          numtok[i] = 0;
          if (pthread_create(&cid[i], NULL, counter, &numtok[i]) != 0) {
               fprintf(stderr, "Error in creating counter thread %d\n", i);
               exit(1);
          }
     }

     /**** Wait for all threads to complete ****/

     /**** Print counter-wise numbers of tokens processed ****/
     for (i=0; i<num_counters; i++) {
          printf("Counter No. %d processed %d tokens\n", i, numtok[i]);
          sum += numtok[i];
     }

     /**** Verifies that tokens are not issued or processed 
           multiple times (see code below)                   ****/
     verifier();

     /**** Print some numbers so that we can be convinced 
           that things are fine!                          ****/
     printf("Last Token Issued: %d\n", tokens.token_issued);
     printf("Last Token Handled: %d\n", tokens.token_handled);
     if (tokens.token_issued < MAXTOKENS-1)
          printf("Not all tokens processed\n");
     if (tokens.token_issued != sum)
          printf("Mismatch between tokens issued (%d) and handled (%d)\n",
                 tokens.token_issued, sum);
     
     exit(0);
}

void *issuer(void *arg)
{
     /**** Write code for token issuer function ****/

     return (NULL);
}

void *counter(void *arg)
{
     /**** Write code for counters to process tokens ****/

     return(NULL);
}

void verifier(void)
{
     /**** This code sees that no token is issued multiple times
           and no token is processed multiple times              ****/
     for (int i=0; i<MAXTOKENS; i++) {
          if (tokens.token_table[i] > 0)
               printf("------ Error: Token %d issued %d times\n",
                      i, tokens.token_table[i]+1);
          if (tokens.token_table[i] < 0) {
               printf("------ Error: Token %d either not issued or called ",
                      i);
               printf("many times  (%d)\n", -(tokens.token_table[i]-1));
          }
     }

     return;
}
In [ ]:
%%bash
gcc token_processing.c -o lab03 -lpthread
In [ ]:
%%bash
./lab03 4 5

THE END