Hone logo
Hone
Problems

Implementing Write Barriers in Go for Concurrent Map Access

Write barriers are crucial for maintaining data consistency in concurrent programs, particularly when dealing with shared data structures like maps. This challenge focuses on implementing a simple write barrier in Go to prevent race conditions when multiple goroutines concurrently modify a map. The goal is to ensure that readers of the map see a consistent view of the data, even during modifications.

Problem Description

You are tasked with creating a write barrier for a shared map in Go. The write barrier should intercept writes to the map and ensure that any readers of the map are notified of the change before the write is committed. This prevents readers from accessing stale data while the map is being modified. The barrier should be implemented as a middleware function that wraps the standard map write operation.

What needs to be achieved:

  1. Implement a WriteBarrier type that encapsulates the shared map.
  2. Provide a Store method on the WriteBarrier type that acts as a write barrier. This method should:
    • Acquire a lock before modifying the map.
    • Notify any waiting readers (using a channel) that a write has occurred.
    • Perform the map write operation.
    • Release the lock.
  3. Provide a Load method on the WriteBarrier type that allows reading from the map. This method should:
    • Acquire a read lock.
    • If a write is in progress (indicated by a signal on the notification channel), wait for the write to complete.
    • Release the read lock.
    • Return the value associated with the key.
  4. The WriteBarrier should use a sync.Mutex for mutual exclusion and a buffered channel to signal write events to readers.

Key Requirements:

  • Thread Safety: The Store and Load methods must be thread-safe.
  • Data Consistency: Readers should always see a consistent view of the map, even during concurrent writes.
  • Minimal Blocking: The write barrier should minimize blocking of readers and writers.

Expected Behavior:

  • Multiple readers can concurrently access the map without blocking each other.
  • A writer will block readers while performing the write operation.
  • Once the write operation is complete, all waiting readers will be unblocked and receive a consistent view of the map.

Edge Cases to Consider:

  • Multiple writers attempting to write concurrently.
  • Readers attempting to read while a writer is actively writing.
  • The map being accessed by a large number of concurrent goroutines.
  • What happens if a reader is waiting on a write that never completes (e.g., due to a deadlock)? While a full deadlock detection isn't required, consider how your design handles this scenario gracefully (e.g., a timeout).

Examples

Example 1:

Input:
  - Shared map: map[string]int{"a": 1, "b": 2}
  - Goroutine 1:  WriteBarrier.Store("c", 3)
  - Goroutine 2:  WriteBarrier.Load("a")
Output:
  - Goroutine 1 completes the write.
  - Goroutine 2 returns 1.
Explanation: Goroutine 2 waits for Goroutine 1 to finish writing before reading "a".

Example 2:

Input:
  - Shared map: map[string]int{"a": 1}
  - Goroutine 1: WriteBarrier.Store("a", 2)
  - Goroutine 2: WriteBarrier.Load("a")
Output:
  - Goroutine 1 completes the write.
  - Goroutine 2 returns 2.
Explanation: Goroutine 2 waits for Goroutine 1 to finish writing "a" and then returns the updated value.

Example 3: (Edge Case - Concurrent Writes)

Input:
  - Shared map: map[string]int{"a": 1}
  - Goroutine 1: WriteBarrier.Store("a", 2)
  - Goroutine 2: WriteBarrier.Store("b", 3)
Output:
  - Goroutine 1 completes the write to "a".
  - Goroutine 2 completes the write to "b".
Explanation: The writes are serialized due to the mutex, ensuring consistency.

Constraints

  • The map will only contain string keys and integer values.
  • The number of concurrent readers and writers will be limited to 100.
  • The write barrier should introduce minimal overhead to the map operations. Avoid unnecessary copying or complex data structures.
  • The buffered channel should have a capacity of 1.

Notes

  • Consider using a sync.Mutex to protect the map from concurrent access.
  • A buffered channel can be used to signal write events to waiting readers.
  • The Load method should block until a write operation is complete.
  • Think about how to handle the case where a reader is waiting indefinitely for a write that never happens. While a full deadlock detection isn't required, consider a timeout mechanism or a way to signal an error to the reader.
  • Focus on correctness and thread safety first, then optimize for performance if necessary.
Loading editor...
go