Priority Queues

Prev: Skiplists and balanced search Next: Scheduling and work distribution

Exercises

Exercise 15.1. Give an example of a quiescently consistent priority queue execution that is not linearizable.


Exercise 15.2. Implement a quiescently consistent Counter with a lock-free implementation of the boundedGetAndIncrement() and boundedGetAndDecrement() methods using a counting network or diffracting tree.


Exercise 15.3. In the SimpleTree algorithm, what would happen if we replace the boundedGetAndDecrement() method with a regular getAndDecrement()?


Exercise 15.4. Use boundedGetAndIncrement() methods in TreeNode counters to devise a SimpleTree algorithm with bounded capacity.


Exercise 15.5. In the SimpleTree class, what would happen if add(), after placing an item in the appropriate Bin, incremented counters in the same top-down manner as in the removeMin() method? Give a detailed example.


Exercise 15.6. Prove that the SimpleTree is a quiescently consistent priority queue implementation.


Exercise 15.7. Modify FineGrainedHeap to allocate new heap nodes dynamically. What are the performance limitations of this approach?


Exercise 15.8. Fig. 15.16 shows a bit-reversed counter. We could use the bit-reversed counter to manage the next field of the FineGrainedHeap class. Prove the following: For any two consecutive insertions, the two paths from the leaves to the root have no common nodes other than the root. Why is this a useful property for the FineGrainedHeap?

public class BitReversedCounter {
  int counter, reverse, highBit;
 
  BitReversedCounter(int initialValue) {
    counter = initialValue;
    reverse = 0;
    highBit = -1;
  }
 
  public int reverseIncrement() {
    if (counter++ == 0) {
      reverse = highBit = 1;
      return reverse;
    }
    int bit = highBit >> 1;
    while (bit != 0) {
      reverse ^= bit;
      if ((reverse & bit) != 0) break;
      bit >>= 1;
    }
    if (bit == 0)
      reverse = highBit <<= 1;
    return reverse;
  }
 
  public int reverseDecrement() {
    counter--;
    int bit = highBit >> 1;
    while (bit != 0) {
      reverse ^= bit;
      if ((reverse & bit) == 0) {
        break;
      }
      bit >>= 1;
    }
    if (bit == 0) {
      reverse = counter;
      highBit >>= 1;
    }
    return reverse;
  }
}

Exercise 15.9. Provide code for PrioritySkipList’s add() and remove() methods.


Exercise 15.10. The PrioritySkipList class used in this chapter is based on the LockFreeSkipList class. Write a PrioritySkipList class based on the LazySkipList class.


Exercise 15.11. Describe a scenario in the SkipQueue implementation in which contention would arise from multiple concurrent removeMin() method calls.


Exercise 15.12. The SkipQueue class is quiescently consistent but not linearizable. Here is one way to make this class linearizable by adding a simple timestamping mechanism. After a node is completely inserted into the SkipQueue, it acquires a timestamp. A thread performing a removeMin() notes the time at which it starts its traversal of the lower level of the SkipQueue, and only considers nodes whose timestamp is earlier than the time at which it started its traversal, effectively ignoring nodes inserted during its traversal. Implement this class and justify why it works.

Prev: Skiplists and balanced search Next: Scheduling and work distribution