Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,2 @@
|
||||
*.c
|
||||
*.out
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
import sync as _
|
||||
|
||||
// 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}')
|
||||
}
|
||||
@@ -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}')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
This example demonstrates thread safety using a queue of callbacks.
|
||||
|
||||
### Functions:
|
||||
- `producer`: creates a callback and adds it to the queue.
|
||||
- `consumer`: consumes a callback from the queue and runs it.
|
||||
- `heavy_processing`: a heavy processing function that is added to the queue.
|
||||
|
||||
### Thread Safety:
|
||||
- The `fn producer` function is protected by a mutex. It locks the mutex before adding a callback
|
||||
to the queue and unlocks it after adding the callback.
|
||||
- The `fn consumer` function is also protected by the same mutex. It locks the mutex before
|
||||
consuming a callback from the queue and unlocks it after consuming the callback.
|
||||
- The `heavy_processing` function is added to the queue by the main thread before the producer
|
||||
threads start producing callbacks. The main thread is the only thread that adds this function to
|
||||
the queue, so it doesn't need to be protected by a mutex.
|
||||
*/
|
||||
import time
|
||||
import sync
|
||||
|
||||
type Callback = fn (id string)
|
||||
|
||||
fn producer(producer_name string, mut arr []Callback, mut mtx sync.Mutex) {
|
||||
for i in 1 .. 5 {
|
||||
mtx.lock()
|
||||
arr << fn [producer_name, i] (consumer_name string) {
|
||||
println('task ${i} created by producer ${producer_name}: consumed by ${consumer_name}')
|
||||
time.sleep(500 * time.millisecond)
|
||||
}
|
||||
println('Produced: ${i}')
|
||||
time.sleep(50 * time.millisecond)
|
||||
mtx.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
fn consumer(consumer_name string, mut arr []Callback, mut mtx sync.Mutex) {
|
||||
for {
|
||||
mtx.lock()
|
||||
if arr.len > 0 {
|
||||
callback := arr[0]
|
||||
arr.delete(0)
|
||||
|
||||
mtx.unlock()
|
||||
callback(consumer_name) // run after unlocking to allow other threads to consume
|
||||
continue
|
||||
} else {
|
||||
println('- No items to consume')
|
||||
mtx.unlock()
|
||||
|
||||
// time.sleep(500 * time.millisecond)
|
||||
// continue // uncomment to run forever
|
||||
|
||||
break // uncomment to stop after consuming all items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn heavy_processing(queue_id string) {
|
||||
println('One more: ${queue_id}')
|
||||
time.sleep(500 * time.millisecond)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut mtx := sync.new_mutex()
|
||||
mut arr := []Callback{}
|
||||
|
||||
producer_threads := [
|
||||
spawn producer('Paula', mut &arr, mut mtx),
|
||||
spawn producer('Adriano', mut &arr, mut mtx),
|
||||
spawn producer('Kaka', mut &arr, mut mtx),
|
||||
spawn producer('Hitalo', mut &arr, mut mtx),
|
||||
spawn producer('Jonh', mut &arr, mut mtx),
|
||||
]
|
||||
|
||||
mut consumer_threads := [
|
||||
spawn consumer('consumer number 0', mut &arr, mut mtx),
|
||||
]
|
||||
|
||||
// spawn 16 consumers
|
||||
for i in 1 .. 16 {
|
||||
consumer_threads << spawn consumer('consumer number ${i}', mut &arr, mut mtx)
|
||||
}
|
||||
|
||||
mtx.lock()
|
||||
arr << heavy_processing
|
||||
mtx.unlock()
|
||||
|
||||
for t in producer_threads {
|
||||
t.wait()
|
||||
}
|
||||
for t in consumer_threads {
|
||||
t.wait()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
### Run
|
||||
|
||||
```sh
|
||||
v -prod -gc none -cc gcc ./queue.v && \
|
||||
./queue
|
||||
```
|
||||
|
||||
### Valgrind
|
||||
|
||||
```sh
|
||||
# Helgrind: a tool for detecting synchronisation errors in programs that use the POSIX pthreads threading primitives.
|
||||
valgrind --tool=helgrind ./queue
|
||||
|
||||
# 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
|
||||
```
|
||||
Reference in new issue
Block a user