Skip to main content

Overview

The Library scenario simulates students (readers) accessing a catalog while a librarian (writer) periodically updates it. This demonstrates the readers-writer problem using a monitor to coordinate concurrent read access while ensuring exclusive write access.

Real-World Problem

Consider a library database system:
  • Multiple students can read the catalog simultaneously
  • When the librarian needs to update the catalog, no readers should be accessing it
  • While an update is in progress, new readers must wait
  • Readers should not block other readers
This is the classic readers-writer problem: optimizing for concurrent reads while ensuring safe exclusive writes.

Shared Resources

The shared resource is a library catalog that supports:
  • Concurrent read access by multiple students
  • Exclusive write access by the librarian
  • Version tracking to show catalog updates
Protected by: Readers-Writer Monitor

Synchronization Algorithm

This scenario uses a Monitor implementing the readers-writer pattern:
1

Reader Entry

Student calls enterRead(). If no writer is active, multiple readers can proceed concurrently.
2

Reader Operation

Student reads from the catalog (e.g., searches for a book)
3

Reader Exit

Student calls exitRead(), decrementing the reader count
4

Writer Entry

Librarian calls enterWrite(), blocking until all readers finish and no other writer is active
5

Writer Operation

Librarian exclusively updates the catalog (increments version)
6

Writer Exit

Librarian calls exitWrite(), allowing waiting readers or writers to proceed

Scenario Setup

libraryScenario.js
import { Thread } from "../core/thread.js";
import { Instructions } from "../core/instructions.js";
import { LibraryMonitor } from "../core/monitorLibrary.js";

export function createLibraryScenario(engine, readerCount, writerUpdates) {
  const safeReaders = Math.max(1, Number(readerCount) || 1);
  const safeUpdates = Math.max(1, Number(writerUpdates) || 1);

  const library = {
    monitor: new LibraryMonitor(),
    catalogVersion: 1,
    totalReads: 0,
    totalWrites: 0,
  };

  // Create reader threads (students)
  for (let i = 1; i <= safeReaders; i++) {
    const readInstructions = [
      { type: Instructions.ENTER_READ },
      { type: Instructions.READ_BOOK, title: `Libro-${i}` },
      { type: Instructions.EXIT_READ },
      { type: Instructions.END },
    ];
    const reader = new Thread(`Estudiante-${i}`, readInstructions);
    reader.role = "reader";
    reader.targetBook = `Libro-${i}`;
    engine.addThread(reader);
  }

  // Create writer thread (librarian)
  const writerInstructions = [];
  for (let i = 0; i < safeUpdates; i++) {
    writerInstructions.push({ type: Instructions.ENTER_WRITE });
    writerInstructions.push({ type: Instructions.UPDATE_CATALOG });
    writerInstructions.push({ type: Instructions.EXIT_WRITE });
  }
  writerInstructions.push({ type: Instructions.END });

  const writer = new Thread("Bibliotecario", writerInstructions);
  writer.role = "writer";
  engine.addThread(writer);

  return { library, writerThread: writer };
}

Configuration Options

ParameterDescriptionDefault
readerCountNumber of student (reader) threadsMinimum 1
writerUpdatesNumber of catalog updates by librarianMinimum 1

Example Execution Flow

Catalog Version: 1

Time 0:
  Estudiante-1: ENTER_READ (readers: 0→1) ✓
  Estudiante-2: ENTER_READ (readers: 1→2) ✓
  Bibliotecario: ENTER_WRITE → BLOCKED (readers > 0)

Time 1:
  Estudiante-1: READ_BOOK "Libro-1"
  Estudiante-2: READ_BOOK "Libro-2"
  Estudiante-3: ENTER_READ (readers: 2→3) ✓

Time 2:
  Estudiante-1: EXIT_READ (readers: 3→2)
  Estudiante-2: EXIT_READ (readers: 2→1)

Time 3:
  Estudiante-3: READ_BOOK "Libro-3"
  Estudiante-3: EXIT_READ (readers: 1→0)
  Bibliotecario: UNBLOCKED → ENTER_WRITE ✓

Time 4:
  Bibliotecario: UPDATE_CATALOG (version: 1→2)
  Bibliotecario: EXIT_WRITE
  Bibliotecario: ENTER_WRITE ✓

Time 5:
  Bibliotecario: UPDATE_CATALOG (version: 2→3)
  Bibliotecario: EXIT_WRITE
  Bibliotecario: END

Thread Instructions

Student (Reader) threads:
  1. ENTER_READ - Request read access (concurrent with other readers)
  2. READ_BOOK - Search for and read a specific book
  3. EXIT_READ - Release read access
  4. END - Terminate
Librarian (Writer) thread:
  1. ENTER_WRITE - Request exclusive write access (blocks until no readers/writers)
  2. UPDATE_CATALOG - Modify catalog (increment version)
  3. EXIT_WRITE - Release write access
  4. Repeat for each update
  5. END - Terminate
The monitor ensures that:
  • Multiple readers can access the catalog simultaneously
  • Writers have exclusive access (no concurrent readers or writers)
  • Readers and writers don’t starve (fairness policies apply)

Readers-Writer Patterns

Reader Priority

Readers never wait for other readers
Writer may starve if readers keep arriving

Writer Priority

Once a writer requests access, no new readers admitted
Prevents writer starvation

Fair Policy

First-come, first-served
Balances reader and writer access

Monitor vs Mutex

AspectMutexReaders-Writer Monitor
Concurrent ReadsNoYes
Write AccessExclusiveExclusive
ComplexitySimpleComplex
Use CaseAll operations exclusiveRead-heavy workloads

Key Learning Points

  • Concurrent Reads: Multiple students can read simultaneously
  • Exclusive Writes: Librarian needs exclusive access for updates
  • Monitor Pattern: Encapsulates synchronization logic
  • Version Tracking: Updates increment catalog version
  • Fairness: Implementation determines reader/writer priority
  • Efficiency: Maximizes concurrency for read operations

Performance Comparison

3 readers finish simultaneously
Total time: 10 cycles
Concurrency: High