liveBook · Manning

Deadlock

As explained in Functional Programming in Scala, Second Edition

A deadlock is a state in which concurrent computations wait indefinitely for one another to release resources or provide progress.

A deadlock occurs when concurrent computations wait indefinitely for one another to release resources or make progress. In functional parallelism, a common cause is submitting a computation from a worker that blocks while waiting for another computation, leaving no worker available to run it. section 7.3.3

A single-thread pool

Create a fixed thread pool with one thread and compare a computation with its forked version. The outer computation occupies the only worker, submits an inner computation, and blocks on its result. Because the inner computation cannot start, both computations wait indefinitely. The same pattern generalizes: a fixed-size pool can deadlock when nested fork operations outnumber available threads. section 7.3.3 section 7.9

val a = lazyUnit(42 + 1)
val es = Executors.newFixedThreadPool(1)
println(Par.equal(es)(a, fork(a)))

Use the listing to inspect the minimal failure: a one-thread executor evaluates a computation alongside its forked form, exposing the difference between logical parallelism and available worker capacity. section 7.3.3

Preventing or reasoning about deadlock requires:

  • Checking the laws of parallel combinators, because an unsound fork implementation can make reusable operations such as parMap dangerous. section 7.3.3
  • Tracking blocking dependencies: every occupied worker may wait for another computation that is queued behind it. section 7.3.3 section 7.9

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Explains the worker-exhaustion mechanism and generalizes it to nested forks, but does not develop a complete prevention strategy.
Examples
Uses a one-thread fixed pool, a forked computation, and nested expressions such as fork(fork(fork(x))).
What it assumes
Assumes familiarity with fork, Futures, ExecutorService, and fixed-size thread pools.
What you can do after
Diagnose/debug with it by tracing blocked workers and queued computations in bounded executors.
How it connects
Connects deadlock to fork, Future, ExecutorService, thread pools, parallel combinators, and compositionality.

Also explained in

Compare how 15 books explain deadlock →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.