Architecture generic Atomics (C11 + extras) (#958)

* Initial implementation for c11 atomics
* Fix cmpxchg usage
* Support for floating point atomics
* Added atomic min and max
* Updated copyright notice
* Removed Floats from and or xor. Added mul, div, bitshift
* Changed get_atomic_compatible_type to lower_to_atomic_compatible_type
* Require non-null pointers
* Fix spacing
* Added Atomic type
* Added macro to reduce code
* Small reorder and cleanup
* Added cmpxchg constrains
* Apply all the restrictions for atomic loads/stores and cmpxchg
This commit is contained in:
OdnetninI (Eduardo José Gómez Hernández)
2023-08-29 14:25:43 +02:00
committed by GitHub
parent efb492eace
commit 7aca8a02cb
5 changed files with 995 additions and 1 deletions

245
test/unit/stdlib/atomic.c3 Normal file
View File

@@ -0,0 +1,245 @@
import std::thread;
import std::io;
import std::atomic;
uint a;
float fa;
fn void! add() @test
{
Thread[100] ts;
a = 0;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
thread::sleep_ms(5);
atomic::fetch_add(&a, 5);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a == ts.len * 10 * 5, "Threads returned %d, expected %d", a, ts.len * 10 * 5);
}
fn void! sub() @test
{
Thread[100] ts;
a = ts.len * 10 * 5;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
thread::sleep_ms(5);
atomic::fetch_sub(&a, 5);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a == 0, "Threads returned %d, expected %d", a, 0);
}
fn void! div() @test
{
Thread[8] ts;
a = 8 * 8 * 8 * 8 * 8 * 8 * 8 * 8 * 8;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
atomic::fetch_div(&a, 8);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a == 8, "Threads returned %d, expected %d", a, 8);
}
fn void! max() @test
{
Thread[100] ts;
a = 0;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
uint la = 0;
thread::sleep_ms(5);
atomic::fetch_max(&a, la);
la++;
thread::sleep_ms(5);
atomic::fetch_max(&a, la);
la++;
thread::sleep_ms(5);
atomic::fetch_max(&a, la);
la++;
thread::sleep_ms(5);
atomic::fetch_max(&a, la);
la++;
thread::sleep_ms(5);
atomic::fetch_max(&a, la);
la++;
thread::sleep_ms(5);
atomic::fetch_max(&a, la);
la++;
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a == 5, "Threads returned %d, expected %d", a, 5);
}
fn void! min() @test
{
Thread[100] ts;
a = 10;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
uint la = 5;
thread::sleep_ms(5);
atomic::fetch_min(&a, la);
la--;
thread::sleep_ms(5);
atomic::fetch_min(&a, la);
la--;
thread::sleep_ms(5);
atomic::fetch_min(&a, la);
la--;
thread::sleep_ms(5);
atomic::fetch_min(&a, la);
la--;
thread::sleep_ms(5);
atomic::fetch_min(&a, la);
la--;
thread::sleep_ms(5);
atomic::fetch_min(&a, la);
la--;
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a == 0, "Threads returned %d, expected %d", a, 0);
}
fn void! fadd() @test
{
Thread[100] ts;
fa = 0;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_add(&fa, 0.5f);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(fa == ts.len * 10 * 0.5, "Threads returned %f, expected %f", fa, ts.len * 10 * 0.5);
}
fn void! fsub() @test
{
Thread[100] ts;
fa = ts.len * 10 * 0.5;
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
thread::sleep_ms(5);
atomic::fetch_sub(&fa, 0.5f);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(fa == 0, "Threads returned %f, expected %f", fa, 0);
}

View File

@@ -0,0 +1,154 @@
import std::thread;
import std::io;
import std::atomic::types;
Atomic(<uint>) a;
Atomic(<float>) fa;
fn void! add() @test
{
Thread[100] ts;
a.store(0);
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
thread::sleep_ms(5);
a.add(5);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a.load() == ts.len * 10 * 5, "Threads returned %d, expected %d", a.load(), ts.len * 10 * 5);
}
fn void! sub() @test
{
Thread[100] ts;
a.store(ts.len * 10 * 5);
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
thread::sleep_ms(5);
a.sub(5);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(a.load() == 0, "Threads returned %d, expected %d", a.load(), 0);
}
fn void! fadd() @test
{
Thread[100] ts;
fa.store(0);
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
thread::sleep_ms(5);
fa.add(0.5);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(fa.load() == ts.len * 10 * 0.5, "Threads returned %f, expected %f", fa.load(), ts.len * 10 * 0.5);
}
fn void! fsub() @test
{
Thread[100] ts;
fa.store(ts.len * 10 * 0.5);
foreach (&t : ts)
{
t.create(fn int(void* arg) {
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
thread::sleep_ms(5);
fa.sub(0.5);
return 0;
}, null)!;
}
foreach (&t : ts)
{
assert(t.join()! == 0);
}
assert(fa.load() == 0, "Threads returned %f, expected %f", fa.load(), 0);
}