CSCI-UA.0202 Spring 2015 Homework 4

Handed out Thursday, Feburary 19, 2015
Due 10:00 AM, Wednesday, February 25, 2015

Homework 4

These problems should be done on your own. We're not going to be grading them strictly (we'll mainly look at whether you attempted them). But they will be reinforcing knowledge and skills, so you should totally work through them carefully.

Time-of-check-to-time-of-use (TOCTTOU) bugs

Alice and Bob each have an account in a bank. Bob wants to transfer money to Alice. We are using the synchronization primitives in Lab 3.
// assume all the variables are initialized correctly
double alice_balance, bob_balance;
smutex_t mtx;

bool
transferBob2Alice(double trans) {
  if (bob_balance > trans) {
    smutex_lock(&mtx);
    bob_balance = bob_balance - trans;
    alice_balance = alice_balance + trans;
    smutex_unlock(&mtx);
    return true;
  }
  return false;
}
The implementation of function transferBob2Alice is not correct.
  1. What's wrong? (Give a problematic interleaving.)
  2. State the fix in one sentence.

Deadlock

The bank decides to use fine-grained locking. Here is its implementation:
// assume all the variables are initialized correctly
double balance[2]; // 0 for alice, 1 for bob
smutex_t mtx[2];    // 0 for alice, 1 for bob

bool transfer(int from, int to, double trans) {
  smutex_lock(&mtx[from]);
  smutex_lock(&mtx[to]);

  bool result = false;
  if (balance[from] > trans) {
    /* 
     * EDIT: corrected code is below. here is the original, typo'ed
     * version
     *   balance[from] = balance[to] - trans;
     *   balance[from] = balance[to] + trans;
     */
    balance[from] = balance[from] - trans;
    balance[to] = balance[to] + trans;
    result = true;
  }

  smutex_unlock(&mtx[to]);
  smutex_unlock(&mtx[from]);
  return result;
}
  1. Write down an interleaving that results in deadlock.
  2. Keeping the same data structures, rewrite transfer() to eliminate the possibility of deadlock

Reader Writer Spinlock (Challenge)

This problem is intended to be somewhat harder than the others. You will implement a multiple-reader, single-writer lock as a spinlock. Here is the description:
  struct sharedlock {
    int value; // when the lock is created, value is initialized to 0
  };
You will likely need to call two atomic primitives, described below: (We also include their pseudocode and inline assembly implementations in an appendix. However, you do not need this appendix material to do the problem.)
  // we are giving you the code for the first of the four functions:
  void reader_acquire(struct sharedlock* lock) {
    int curr_val;
    while (1) {

      // spin while a writer owns the lock
      while ((curr_val = lock->value) == -1) {}

      assert(curr_val >= 0);

      // try to atomically increment the count, based on our best
      // guess of how many readers there had been. if we were
      // wrong, keep looping. if we got it right, then we
      // succeeded in incrementing the count atomically, and we
      // can proceed.
      if (cmpxchg_val(&lock->value, curr_val, curr_val + 1) == curr_val)
        break;
    }
    // lock->value now contains curr_val + 1
  }
Write the other three functions! (Again, each needs only a single line of code.)

Priority Inversion

In this problem, the system has three tasks: one at high priority, one at medium priority, and one at low priority. Assume that the intent is to schedule according to strict priority (although we will see that this intent will be thwarted). Some assumptions: Here are the three tasks:
smutex_t res;

void highPriority() {
  ... // do something
  smutex_lock(&res);
  ... // handle resource
  smutex_unlock(&res);
  printf("A ");
}

void mediumPriority() {
  ... // do something
  printf("B ");
}

void lowPriority() {
  smutex_lock(&res);
  ... // handle resource
  smutex_unlock(&res);
  ... // do something
  printf("C ");
}
  1. Which of the following outputs are possible?

  2. A B C
    A C B
    B A C
    C B A

  3. Explain.

Handing in the homework

Use NYU Classes; there's an entry for this homework.

Appendix: Implementation of atomic primitives

Here is pseudocode and actual x86 assembly code for the implementations of the atomic primitives in Question 3.
cmpxchg val()

  /* pseudocode */
  int cmpxchg_val(int* addr, int oldval, int newval) {
    LOCK: // remember, this is pseudocode
    int was = *addr;
    if (*addr == oldval)
      *addr = newval;
    return was;
  }

  /* inline assembly */
  int cmpxchg_val(int* addr, int oldval, int newval) {
      int was;
      asm volatile("lock cmpxchg %3, %0"
        : "+m" (*addr), "=a" (was)
        : "a" (oldval), "r" (newval)
        : "cc");
       return was;
  }

atomic decrement()

  /* pseudocode */
  void atomic_decrement(int* arg) {
    LOCK: // remember, this is pseudocode
    *arg = *arg - 1;
  }

  /* inline assembly */
  void atomic_decrement(int* arg) {
    asm volatile("lock decl %0" : "+m" (*arg) : "m" (arg));
  }

Last updated: Mon May 04 11:24:46 -0400 2015 [validate xhtml]