1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

sync: atomic counters

This commit is contained in:
Tomas Hellström
2020-06-26 20:04:17 +02:00
committed by GitHub
parent 195f4d0911
commit def99bed02
5 changed files with 951 additions and 0 deletions

55
vlib/sync/atomic.v Normal file
View File

@@ -0,0 +1,55 @@
module sync
/*
Implements the atomic operations. For now TCC does not support
the atomic versions on nix so it uses locks to simulate the same behavor.
On windows tcc can simulate with other atomic operations.
The @VROOT/thirdparty/stdatomic contains compability header files
for stdatomic that supports both nix, windows and c++.
This implementations should be regarded as alpha stage and be
further tested.
*/
#flag windows -I @VROOT/thirdparty/stdatomic/win
#flag linux -I @VROOT/thirdparty/stdatomic/nix
#flag darwin -I @VROOT/thirdparty/stdatomic/nix
#flag freebsd -I @VROOT/thirdparty/stdatomic/nix
#flag solaris -I @VROOT/thirdparty/stdatomic/nix
#include "atomic.h"
fn C.atomic_fetch_add_explicit() int
fn C.atomic_fetch_sub_explicit() int
[typedef]
struct C.atomic_ullong {
}
[typedef]
struct C.atomic_llong {
}
// add_u64 adds provided delta as an atomic operation
pub fn add_u64(ptr &u64, delta int) bool {
res := C.atomic_fetch_add_explicit(&C.atomic_ullong(ptr), delta, C.NULL)
return res == 0
}
// sub_u64 subtracts provided delta as an atomic operation
pub fn sub_u64(ptr &u64, delta int) bool {
res := C.atomic_fetch_sub_explicit(&C.atomic_ullong(ptr), delta, C.NULL)
return res == 0
}
// add_i64 adds provided delta as an atomic operation
pub fn add_i64(ptr &i64, delta int) bool {
res := C.atomic_fetch_add_explicit(&C.atomic_llong(ptr), delta, C.NULL)
return res == 0
}
// add_i64 subtracts provided delta as an atomic operation
pub fn sub_i64(ptr &i64, delta int) bool {
res := C.atomic_fetch_sub_explicit(&C.atomic_llong(ptr), delta, C.NULL)
return res == 0
}

94
vlib/sync/atomic_test.v Normal file
View File

@@ -0,0 +1,94 @@
import sync
struct Counter {
mut:
counter u64 = 0
}
// without proper syncronization this would fail
fn test_count_100_milion_should_result_100_million() {
mut wg := sync.new_waitgroup()
mut counter := &Counter{}
wg.add(10)
for i := 0; i < 10; i++ {
go count_ten_million(mut counter, mut wg)
}
wg.wait()
assert counter.counter == 10000000
}
// This test just to make sure that we have an anti-test to prove it works
fn test_count_100_milion_should_fail_100_million_without_sync() {
mut wg := sync.new_waitgroup()
mut counter := &Counter{}
wg.add(10)
for i := 0; i < 10; i++ {
go count_ten_million_without_sync(mut counter, mut wg)
}
wg.wait()
assert counter.counter != 10000000
}
fn test_count_plus_one_u64() {
mut c := u64(0)
sync.add_u64(&c, 1)
assert c == 1
}
fn test_count_plus_one_i64() {
mut c := i64(0)
sync.add_i64(&c, 1)
assert c == 1
}
fn test_count_plus_greater_than_one_u64() {
mut c := u64(0)
sync.add_u64(&c, 10)
assert c == 10
}
fn test_count_plus_greater_than_one_i64() {
mut c := i64(0)
sync.add_i64(&c, 10)
assert c == 10
}
fn test_count_minus_one_u64() {
mut c := u64(1)
sync.sub_u64(&c, 1)
assert c == 0
}
fn test_count_minus_one_i64() {
mut c := i64(0)
sync.sub_i64(&c, 1)
assert c == -1
}
fn test_count_minus_greater_than_one_u64() {
mut c := u64(10)
sync.sub_u64(&c, 10)
assert c == 0
}
fn test_count_minus_greater_than_one_i64() {
mut c := i64(10)
sync.sub_i64(&c, 20)
assert c == -10
}
// count_ten_million counts the common counter 10 million times in thread-safe way
fn count_ten_million(mut counter Counter, mut group sync.WaitGroup) {
for i := 0; i < 1000000; i++ {
sync.add_u64(&counter.counter, 1)
}
group.done()
}
// count_ten_million_without_sync counts the common counter 10 million times in none thread-safe way
fn count_ten_million_without_sync(mut counter Counter, mut group sync.WaitGroup) {
for i := 0; i < 1000000; i++ {
counter.counter++
}
group.done()
}