docs: describe usage of atomic C functions (#10861)
parent
de39bba7bd
commit
dc045806f9
|
@ -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 {
|
||||||
|
|
88
doc/docs.md
88
doc/docs.md
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue