Hone logo
Hone
Problems

Safe Concurrent Data Modification in Rust

Concurrency is a powerful tool for improving application performance, but it also introduces the risk of data races: situations where multiple threads access the same data concurrently, and at least one of the accesses is a write. Data races lead to unpredictable behavior and are a common source of bugs. This challenge focuses on writing Rust code that safely modifies shared data across multiple threads without introducing data races.

Problem Description

Your task is to implement a mechanism that allows multiple threads to safely update a shared counter. You will need to create a data structure that can be accessed concurrently by several threads, ensuring that no data races occur during the increment operations.

What needs to be achieved:

  • Create a shared counter that can be accessed and modified by multiple threads.
  • Implement a method to increment this counter safely from different threads.
  • Verify that the final value of the counter is the sum of all increments performed by the threads.

Key requirements:

  • Use Rust's concurrency primitives to ensure thread-safe access to the shared data.
  • The solution must be free of data races. Rust's compiler should be able to verify this safety.
  • The shared data should be accessible across threads without resorting to unsafe blocks for data access itself.

Expected behavior:

When a specified number of threads each increment the counter a specified number of times, the final value of the counter should equal the product of the number of threads and the number of increments per thread.

Important edge cases to consider:

  • What happens if the number of threads is very large?
  • What happens if the number of increments per thread is very large?

Examples

Example 1:

Input:
Number of threads: 4
Increments per thread: 1000

Output:
Final counter value: 4000

Explanation: Four threads are created. Each thread increments a shared counter 1000 times. The expected final value of the counter is 4 * 1000 = 4000.

Example 2:

Input:
Number of threads: 10
Increments per thread: 500

Output:
Final counter value: 5000

Explanation: Ten threads are created. Each thread increments a shared counter 500 times. The expected final value of the counter is 10 * 500 = 5000.

Example 3:

Input:
Number of threads: 1
Increments per thread: 10000

Output:
Final counter value: 10000

Explanation: A single thread increments the counter 10000 times. This tests the basic functionality without the complexity of multiple threads.

Constraints

  • The number of threads will be between 1 and 100.
  • The number of increments per thread will be between 1 and 100,000.
  • The total number of increments (threads * increments per thread) will not exceed 1,000,000.
  • The solution should be performant enough to complete within a reasonable time frame (e.g., a few seconds) on a typical modern machine.

Notes

  • Consider using Arc (Atomically Reference Counted) to share ownership of data across threads.
  • Think about which synchronization primitives in Rust are designed for safe mutable access to shared data. The standard library offers excellent tools for this.
  • The goal is to demonstrate how Rust's ownership and borrowing system, combined with appropriate concurrency primitives, prevent data races at compile time. You should not need unsafe code for the core data modification logic.
Loading editor...
rust