docs: describe usage of atomic C functions (#10861)

pull/10870/head
Uwe Krüger 2021-07-20 07:31:20 +02:00 committed by GitHub
parent de39bba7bd
commit dc045806f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 0 deletions

View File

@ -444,6 +444,23 @@ fn (mut f MDFile) check_examples() CheckResult {
} }
oks++ oks++
} }
'globals' {
res := cmdexecute('"$vexe" -w -Wfatal-errors -enable-globals -o x.c $vfile')
os.rm('x.c') or {}
if res != 0 || fmt_res != 0 {
if res != 0 {
eprintln(eline(f.path, e.sline, 0, '`example failed to compile with -enable-globals'))
}
if fmt_res != 0 {
eprintln(eline(f.path, e.sline, 0, '`example is not formatted'))
}
eprintln(vcontent)
should_cleanup_vfile = false
errors++
continue
}
oks++
}
'live' { 'live' {
res := cmdexecute('"$vexe" -w -Wfatal-errors -live -o x.c $vfile') res := cmdexecute('"$vexe" -w -Wfatal-errors -live -o x.c $vfile')
if res != 0 || fmt_res != 0 { if res != 0 || fmt_res != 0 {

View File

@ -131,6 +131,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
* [Structs with reference fields](#structs-with-reference-fields) * [Structs with reference fields](#structs-with-reference-fields)
* [sizeof and __offsetof](#sizeof-and-__offsetof) * [sizeof and __offsetof](#sizeof-and-__offsetof)
* [Calling C from V](#calling-c-from-v) * [Calling C from V](#calling-c-from-v)
* [Atomics](#atomics)
* [Debugging](#debugging) * [Debugging](#debugging)
* [Conditional compilation](#conditional-compilation) * [Conditional compilation](#conditional-compilation)
* [Compile time pseudo variables](#compile-time-pseudo-variables) * [Compile time pseudo variables](#compile-time-pseudo-variables)
@ -4251,6 +4252,93 @@ fn main() {
} }
``` ```
## Atomics
V has no special support for atomics, yet, nevertheless it's possible to treat variables as atomics
by calling C functions from V. The standard C11 atomic functions like `atomic_store()` are usually
defined with the help of macros and C compiler magic to provide a kind of *overloaded C functions*.
Since V does not support overloading functions by intention there are wrapper functions defined in
C headers named `atomic.h` that are part of the V compiler infrastructure.
There are dedicated wrappers for all unsigned integer types and for pointers.
(`byte` is not fully supported on Windows) – the function names include the type name
as suffix. e.g. `C.atomic_load_ptr()` or `C.atomic_fetch_add_u64()`.
To use these functions the C header for the used OS has to be included and the functions
that are intended to be used have to be declared. Example:
```v globals
$if windows {
#include "@VEXEROOT/thirdparty/stdatomic/win/atomic.h"
} $else {
#include "@VEXEROOT/thirdparty/stdatomic/nix/atomic.h"
}
// declare functions we want to use - V does not parse the C header
fn C.atomic_store_u32(&u32, u32)
fn C.atomic_load_u32(&u32) u32
fn C.atomic_compare_exchange_weak_u32(&u32, &u32, u32) bool
fn C.atomic_compare_exchange_strong_u32(&u32, &u32, u32) bool
const num_iterations = 10000000
__global (
atom u32 // ordinary variable but used as atomic
)
fn change() int {
mut races_won_by_change := 0
for {
mut cmp := u32(17) // addressable value to compare with and to store the found value
// atomic version of `if atom == 17 { atom = 23 races_won_by_change++ } else { cmp = atom }`
if C.atomic_compare_exchange_strong_u32(&atom, &cmp, 23) {
races_won_by_change++
} else {
if cmp == 31 {
break
}
cmp = 17 // re-assign because overwritten with value of atom
}
}
return races_won_by_change
}
fn main() {
C.atomic_store_u32(&atom, 17)
t := go change()
mut races_won_by_main := 0
mut cmp17 := u32(17)
mut cmp23 := u32(23)
for i in 0 .. num_iterations {
// atomic version of `if atom == 17 { atom = 23 races_won_by_main++ }`
if C.atomic_compare_exchange_strong_u32(&atom, &cmp17, 23) {
races_won_by_main++
} else {
cmp17 = 17
}
desir := if i == num_iterations - 1 { u32(31) } else { u32(17) }
// atomic version of `for atom != 23 {} atom = desir`
for !C.atomic_compare_exchange_weak_u32(&atom, &cmp23, desir) {
cmp23 = 23
}
}
races_won_by_change := t.wait()
atom_new := C.atomic_load_u32(&atom)
println('atom: $atom_new, #exchanges: ${races_won_by_main + races_won_by_change}')
// prints `atom: 31, #exchanges: 10000000`)
println('races won by\n- `main()`: $races_won_by_main\n- `change()`: $races_won_by_change')
}
```
In this example both `main()` and the spawned thread `change()` try to replace a value of `17`
in the global `atom` with a value of `23`. The replacement in the opposite direction is
done exactly 10000000 times. The last replacement will be with `31` which makes the spawned
thread finish.
It is not predictable how many replacements occur in which thread, but the sum will always
be 10000000. (With the non-atomic commands from the comments the value will be higher or the program
will hang – dependent on the compiler optimization used.)
### Passing C compilation flags ### Passing C compilation flags
Add `#flag` directives to the top of your V files to provide C compilation flags like: Add `#flag` directives to the top of your V files to provide C compilation flags like: