Hone logo
Hone
Problems

Implement a Mutex-Based Barrier in Rust

This challenge focuses on implementing a synchronization primitive in Rust: a barrier. A barrier is a synchronization mechanism that allows a group of threads to wait until all threads in the group have reached a certain point in their execution before any of them can proceed. This is crucial for coordinating parallel tasks where certain operations must be completed by all participating threads before the next phase can begin.

Problem Description

Your task is to implement a Barrier struct in Rust that allows multiple threads to synchronize. The Barrier should accept a count, representing the number of threads that need to reach the barrier. When a thread calls the wait() method on the barrier, it should block until the specified number of threads have also called wait(). Once the last thread arrives, all waiting threads should be unblocked and allowed to proceed.

Key Requirements:

  • The Barrier should be initialized with a count representing the total number of threads that must reach the barrier.
  • The wait() method should be thread-safe.
  • When wait() is called, the calling thread should block if the count has not yet been reached.
  • Once the count is reached, all threads that are blocked on wait() should be unblocked.
  • The Barrier should be reusable. After all threads have passed, it should reset itself and be ready for the next synchronization point.

Expected Behavior:

When a thread calls barrier.wait(), it will either:

  1. Block until count threads have called wait().
  2. If it is the count-th thread to call wait(), it will unblock all other waiting threads and then proceed.

Edge Cases to Consider:

  • What happens if count is 0 or 1?
  • What happens if wait() is called more times than the initial count after a reset?
  • How to handle potential deadlocks or race conditions if not implemented correctly.

Examples

Example 1:

Let's say we have a barrier initialized with a count of 3.

  1. Thread A calls barrier.wait(). It blocks.
  2. Thread B calls barrier.wait(). It blocks.
  3. Thread C calls barrier.wait(). This is the 3rd thread. All threads (A, B, and C) are unblocked and proceed.

Example 2:

Consider a scenario where threads perform work, synchronize, and then perform more work.

  1. Initialize barrier with count = 2.
  2. Thread 1: Performs task X. Calls barrier.wait(). Blocks.
  3. Thread 2: Performs task Y. Calls barrier.wait(). Blocks.
  4. (Now, count is reached. Thread 1 and Thread 2 are unblocked.)
  5. Thread 1: Performs task Z.
  6. Thread 2: Performs task W.

Example 3: Reusability

  1. Initialize barrier with count = 2.
  2. Thread 1: Calls barrier.wait(). Blocks.
  3. Thread 2: Calls barrier.wait(). Unblocks Thread 1. Both proceed.
  4. Thread 1: Performs some work. Calls barrier.wait(). Blocks.
  5. Thread 2: Performs some work. Calls barrier.wait(). Unblocks Thread 1. Both proceed.

Constraints

  • The Barrier struct should use std::sync::Mutex and std::sync::Condvar for synchronization.
  • The count will be a positive integer (>= 1).
  • The implementation should be efficient and avoid unnecessary locking.
  • The wait() method should return () and not panic under normal operation.

Notes

  • Think about how to manage the count of waiting threads and how to signal when the barrier is released.
  • std::sync::Condvar is crucial for signaling between threads.
  • A Mutex will be needed to protect shared state (like the waiting thread count).
  • Consider how to manage the "generation" or "cycle" of the barrier to ensure proper reusability. You might need an extra variable to track this.
  • The standard library std::sync::Barrier is a good reference for understanding the desired behavior, but you should implement your own from scratch using Mutex and Condvar.
Loading editor...
rust