Lecture 07: Concurrent Objects 2

Announcements

  1. Quiz 2 posted
  2. Homework 2 posted in next couple days
  3. Accountability group survey linked to today

Outline

  1. Linked Lists, Again
  2. Blocking and consistency

Last Time

Linked list with concurrent insertions:

  • Lock individual nodes
  • Can perform concurrent insertions when nodes don’t overlap

Concurrent Insertions

Acquiring Locks

Both Insert

Both Release

What Happens with Contention?

Red Acquires Locks (Blue Waits)

Red Inserts & Releases Locks

Blue Finally Acquires Locks

Blue Inserts & Releases Locks

A Slightly Different Scenario

Question

Are multiple concurrent insertions guaranteed to eventually succeed?

Blocking and Consistency

Morals from Previous Examples

  1. Locking whole object (linked list)
    • easy to reason about correctness
    • may give poor performance
  2. Locking individual parts
    • may give better performance
    • more challenging to reason about correctness

A Queue

public class LockedQueue<T> {
    int head, tail;
    T[] contents;
    Lock lock;
	
    ...
	
    public void enq(T x) {
        lock.lock();
        try {
            items[tail} = x;
            tail++;
        } finally {
            lock.unlock();
        }
    }

    public T deq() {
        lock.lock();
        try {
            T x = items[head];
            head++;
            return x;
        } finally {
            lock.unlock();
        }
    }
}

Question

What makes this queue implementation easy to reason about?

Executions are Essentially Sequential!

If multiple threads access the queue

  • only one thread actually modifies queue at a time
  • other threads must wait
  • this property is called blocking:
    • some method calls cannot make progress while others perform a task

Another Question

Queues are first-in first-out (FIFO) data structures

What does FIFO even mean if objects can be enqueued concurrently?

Blocking Execution

Consider:

  • Two threads A, B concurrently call enq(x), enq(y), respectively
  • Then thread A calls deq()

What is expected behavior?

What Happens?

Depends on how concurrent operations are resolved!

Equivalent Sequential Execution

In Sequential Execution

Method calls are linearly sorted:

  • Method calls:
    • invocation
    • response
  • Each call’s response preceeds next call’s invocation

Reasoning About Sequential Executions

  1. Assume object in some state
    • precondition
  2. Method specifies
    • postcondition
      • return value
      • change of internal state (side effect)

Method calls performed sequentially $\implies$ state well defined between method calls.

  • Specifying pre/post-conditions for each method define object’s sequential specification

Reasoning About Concurrent Executions

“Correct” behavior no longer well defined!

  • call to deq() could return either x or y
    • both reasonable!

A Reasonable Goal

A concurrent execution of a data structure is “correct” if it is consistent with some sequential execution of the data structure.

Response to each method call in concurrent execution is the same as the sequential execution.

  • What other features of concurrent execution can/should the sequential execution maintain?

Our Goal

Define sensible qualities for how executions should behave:

  1. Sequential consistency
  2. Linearizability

These are less rigid requirements than being essentially sequential

  • May allow for less synchronization (locking) between threads
  • Tradeoff: more lenient behavioral guarantees

Sequential Consistency

A Sensible Feature

Consider all method calls made by all threads

  • Each method has precondition, postcondition

Behavior of execution should be consistent with some sequential execution of the method calls.

Is This Enough?

Behavior of execution should be consistent with some sequential execution of the method calls.

Probably Not!

Queue with multiple threads:

  • thread 1 calls enq(1) then enq(2)
  • other threads enqueue stuff, not 1 or 2
  • thread 1 calls deq() a bunch of times

Should have:

  • thread 1 dequeues 1 before 2

Another Sensible Feature

Method calls should appear to take effect in program order

  • if a single thread calls methodOne() before methodTwo(), then methodOne() should take effect before methodTwo() in sequential execution.

Sequential Consistency

An execution is sequentially consistent if all method calls can be ordered such that:

  1. they are consistent with program order
  2. they meet object’s sequential specification

An implementation of an object is sequentially consistent if

  1. it guarantees every execution is sequentially consistent

Example

What are possible outcomes of deq() calls in a sequentially consistent execution?