Skip to content

Commit

Permalink
examples: add a thread_safety/ folder with several examples of using …
Browse files Browse the repository at this point in the history
…different thread synchronisation mechanisms (#22561)
  • Loading branch information
enghitalo authored Oct 18, 2024
1 parent 891a8bc commit 5ec9bb5
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 0 deletions.
2 changes: 2 additions & 0 deletions examples/thread_safety/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.c
*.out
52 changes: 52 additions & 0 deletions examples/thread_safety/atomic_counter.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
This code demonstrates thread safety using atomic operations in V.
Thread safety is achieved by using atomic functions to manipulate the shared counter variable.
Atomic operations ensure that the read-modify-write sequence is performed as a single, indivisible operation,
preventing race conditions and ensuring data integrity when accessed by multiple threads concurrently.
Key points:
1. **Atomic Fetch and Add**: The `C.atomic_fetch_add_u32` function atomically increments the counter.
This means that the increment operation is performed without interruption, ensuring that no two threads
can increment the counter simultaneously and cause a race condition.
2. **Atomic Load**: The `C.atomic_load_u32` function atomically reads the value of the counter.
This ensures that the read operation is consistent and not affected by concurrent writes from other threads.
3. **Thread Synchronization**: The `spawn` function is used to create new threads that run the `increment` function.
The `wait` method is called on each thread to ensure that the main thread waits for both threads to complete
before reading the final value of the counter.
By using atomic operations and proper thread synchronization, the code ensures that the shared counter is
incremented safely and correctly by multiple threads.
*/
$if windows {
#include "@VEXEROOT/thirdparty/stdatomic/win/atomic.h"
} $else {
#include "@VEXEROOT/thirdparty/stdatomic/nix/atomic.h"
}

// Declare the atomic functions
fn C.atomic_fetch_add_u32(&u32, u32) u32
fn C.atomic_load_u32(&u32) u32

// Function to increment the atomic counter
fn increment(atomic_counter &u32) {
C.atomic_fetch_add_u32(atomic_counter, 1)
}

fn main() {
atomic_counter := u32(0) // Atomic counter variable

// Spawn two threads that increment the atomic counter
t1 := spawn increment(&atomic_counter)
t2 := spawn increment(&atomic_counter)

// Wait for both threads to complete
t1.wait()
t2.wait()

// Load and print the final value of the atomic counter
final_count := C.atomic_load_u32(&atomic_counter)
println('Atomic Counter: ${final_count}')
}
36 changes: 36 additions & 0 deletions examples/thread_safety/concurrent_shared_data.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
This example demonstrates thread safety using V's concurrency features.
Key points:
- The `SharedData` struct contains a mutable counter that will be accessed by multiple threads.
- The `increment` function increments the counter within a lock to ensure that only one thread
can modify the counter at a time, preventing race conditions.
- In the `main` function, two threads are spawned to increment the shared counter concurrently.
- The `lock` keyword is used to ensure exclusive access to the shared data during modification,
and the `rlock` keyword is used to allow multiple threads to read the data concurrently without
modification.
This ensures that the counter is incremented safely and the final value is printed correctly.
*/

struct SharedData {
mut:
counter int
}

fn increment(shared data SharedData) {
lock data {
data.counter++
}
}

fn main() {
shared data := SharedData{}
threads := [spawn increment(shared data), spawn increment(shared data)]

for t in threads {
t.wait() // Wait for both threads to complete
}

rlock data {
println('Counter: ${data.counter}')
}
}
49 changes: 49 additions & 0 deletions examples/thread_safety/queue.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
This example demonstrates thread safety using channels in V.
### Functions:
- `producer(ch chan int)`: This function simulates a producer that sends integers from 1 to 99 to
the channel `ch`. It prints each produced item.
- `consumer(ch chan int)`: This function simulates a consumer that receives integers from the
channel `ch`.
### Thread Safety:
- The use of channels ensures thread safety by providing a synchronized way to communicate between
the producer and consumer threads.
- Channels in V are designed to handle concurrent access, preventing race conditions and ensuring
that data is safely passed between threads.
- The `select` statement in the consumer function allows it to handle timeouts gracefully,
ensuring that the program does not hang if the producer is not ready.
*/
import time

fn producer(ch chan int) {
for i in 1 .. 100 {
ch <- i
println('Produced: ${i}')
}
}

fn consumer(ch chan int) {
for {
select {
item := <-ch {
println('Consumed: ${item}')
}
500 * time.millisecond {
println('Timeout: No producers were ready within 0.5s')
break
}
}
}
}

fn main() {
ch := chan int{cap: 10}

producer_thread := spawn producer(ch)
consumer_thread := spawn consumer(ch)

producer_thread.wait()
consumer_thread.wait()
}
17 changes: 17 additions & 0 deletions examples/thread_safety/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Run

```sh
v -prod -autofree ./queue.v -o ./queue.c && \
gcc ./queue.c -o ./queue.out && \
./queue.out
```

### Valgrind

```sh
# Helgrind: a tool for detecting synchronisation errors in programs that use the POSIX pthreads threading primitives.
valgrind --tool=helgrind ./queue.out

# DRD: a tool for detecting errors in multithreaded programs. The tool works for any program that uses the POSIX threading primitives or that uses threading concepts built on top of the POSIX threading primitives.
valgrind --tool=drd ./queue.out
```

0 comments on commit 5ec9bb5

Please sign in to comment.