Question 4

Suppose you have 10 printers, 10 tape drives, and 10 disks. All jobs (which will be represented by threads) will require some subset of these. We will represent a resource by a struct():
typedef struct {
  char type;   /* 'p', 't' or 'd' */
  int id;
  void *ptr;
} Resource;
We have three global lists, three global counters, a mutex and three condition variables:
Dllist           p_flist, t_flist, d_flist;
int              p_nfree, t_nfree, d_nfree;
pthread_mutex_t  lock;
pthread_cond_t   p_cv,    t_cv,    d_cv;
These are lists of free printers, tape drives and disks respectively. The counters represent the size of these lists. Initially, obviously, the counters are each 10, and the lists each have 10 resources in them.

A job allocates resources by calling allocate():

Dllist allocate(int nprinter, int ntape, int ndisk);
This routine should return only when all of these resources are allocated, and it returns a list of the allocated resources. Moreover, a job releases resources by calling release().
void release(Dllist resource_list);
On the next page is an implementation of allocate() and release().

Suppose that your thread system is preemptive, and that there are no guarantees about the order in which threads wake up from mutexes and condition variables.

void get_resource(Dllist d, int n, Dllist flist, int *nfree, 
                  pthread_cond_t *cv) {
  Dllist tmp;

  pthread_mutex_lock(&lock);
  while (n > 0) {
    if (*nfree > 0) {
      tmp = dll_first(flist);
      dll_append(d, tmp->val);
      dll_delete_node(tmp);
      *nfree--;
      n--;
    } else {
      pthread_cond_wait(cv, &lock);
    }
  }
  pthread_mutex_unlock(&lock);
}

Dllist allocate(int nprinter, int ntape, int ndisk)
{
  Dllist d;

  d = new_dllist();

  get_resource(d, nprinter, p_flist, &p_nfree, &p_cv);
  get_resource(d, ntape, t_flist, &t_nfree, &t_cv);
  get_resource(d, ndisk, d_flist, &d_nfree, &d_cv);

  return d;

void release(Dllist resource_list)
{
  Dllist tmp;
  Resource *r;
  
  pthread_mutex_lock(&lock);
  while (!dll_empty(resource_list)) {
    tmp = dll_first(resource_list);
    r = (Resource *) tmp->val.v;
    dll_delete_node(tmp);
    if (r->type == 'p') {
      dll_append(p_flist, new_jval_v((void *) r));
      p_nfree++;
      pthread_cond_signal(&p_cv);
    } else if (r->type == 't') {
      dll_append(t_flist, new_jval_v((void *) r));
      t_nfree++;
      pthread_cond_signal(&t_cv);
    } else if (r->type == 't') {
      dll_append(d_flist, new_jval_v((void *) r));
      d_nfree++;
      pthread_cond_signal(&d_cv);
    }
  }
  pthread_mutex_unlock(&lock);
  free_dllist(resource_list);
}