Answer to Question 5 -- 23 points

There are really three things wrong with this implementation. First is that it can deadlock. Suppose one machine tries to copy a file from disk one to disk two, and at the same time, another machine tries to copy a file from disk two to disk one. You'll likely get deadlock. This needs to be fixed.

The second problem is a simple bug -- the student has forgotten to update tmp2 to point to the next free item on the list.

The third problem is one of efficiency. If the file sectors on disk one and the free sectors on disk two are randomly ordered, then the disk will spend far more time performing seeks than it needs to.

These problems can all be fixed rather easily. The first problem can be fixed by killing the circular wait condition. Instead of allocating disk 2 then disk 1, you allocate the one with the smaller id number first, and then allocate the second. That way the circular wait condition of deadlock will be prevented.

The second problem is just a bug that must be corrected (i.e. add ``tmp2 = tmp2->flink to the code).

The third problem can be fixed by ordering the sectors. First order the sectors of disk 1 in ascending order, and then order the free sectors of disk 2 in ascending order. Do the copying in this order to minimize the disk seek times, and then figure out the proper order of the sectors to return from the procedure. This last part is actually a little tricky, but it can be done.

Dlist copy_file(int diskid1, Dlist sectors1, int diskid2)
{
  Dlist d, tmp1, tmp2, sectors2;
  int sector1, sector2;
  Rb_tree f1tree, freetree;

  /* Allocate the disks and get the free sectors of disk 2 */

  if (diskid1 < diskid2) {
    allocate_disk(diskid1);
    allocate_disk(diskid2);
  } else {
    allocate_disk(diskid2);
    allocate_disk(diskid1);
  }
  sectors2 = get_free_sectors(diskid2);

  /* Order the sectors of the file in f1tree */

  f1tree = make_rb();
  dl_traverse(tmp1, sectors1) {
    rb_inserti(f1tree, (int) tmp1->val);
  }
  
  /* Order the free sectors of the file in freetree */

  freetree = make_rb();
  dl_traverse(tmp1, sectors2) {
    rb_inserti(freetree, (int) tmp1->val, NULL);
  }
  
  /* Traverse both trees simultaneously, copying blocks as you go.
     Put the copied sector id into the val field of the f1tree node.  */

  rtmp2 = rb_first(freetree);
  rb_traverse(rtmp1, f1tree) {
    sector1 = (int) rtmp1->k.ikey;
    sector2 = (int) rtmp2->k.ikey;
    disk_to_disk_copy(diskid1, sector1, diskid2, sector2);
    rtmp1->val = sector2;
    rtmp2 = rb_next(freetree);
  }

  /* Deallocate the disks */

  deallocate_disk(diskid2);
  deallocate_disk(diskid1);

  /* Now, build the dlist of the copied file -- this can be done more
     efficiently, but this way appears to be the clearest */

  d = make_dl();
  dl_traverse(tmp1, sectors1) {
    rtmp1 = rb_find_key(f1tree, tmp1->val);
    dl_insert_b(d, rtmp1->v.val);
  }

  rb_free_tree(f1tree);
  rb_free_tree(freetree);

  return d;
}
Wed May 7 16:55:42 EDT 1997