From 88248b1b666621c5fdbe37141182ef743903b962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Wed, 8 Jul 2020 11:05:43 +0200 Subject: [PATCH] cgen: add support for shared arrays (#5721) --- vlib/v/gen/cgen.v | 53 +++++++++++++++++++++++++++++-- vlib/v/tests/shared_array_test.v | 41 ++++++++++++++++++++++++ vlib/v/tests/shared_lock_2_test.v | 10 +++--- vlib/v/tests/shared_lock_3_test.v | 16 +++++----- vlib/v/tests/shared_lock_4_test.v | 16 +++++----- vlib/v/tests/shared_lock_test.v | 10 +++--- 6 files changed, 117 insertions(+), 29 deletions(-) create mode 100644 vlib/v/tests/shared_array_test.v diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 773b28939e..70fad1ef14 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2376,10 +2376,17 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { if g.is_assign_lhs && !is_selector && node.is_setter { g.is_array_set = true g.write('array_set(') - if !left_is_ptr { + if !left_is_ptr || node.left_type.has_flag(.shared_f) { g.write('&') } g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } g.write(', ') g.expr(node.index) mut need_wrapper := true @@ -2404,10 +2411,17 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { info.elem_type != table.string_type { // TODO move this g.write('*($elem_type_str*)array_get(') - if left_is_ptr { + if left_is_ptr && !node.left_type.has_flag(.shared_f) { g.write('*') } g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } g.write(', ') g.expr(node.index) g.write(') ') @@ -2428,10 +2442,17 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { } } else { g.write('(*($elem_type_str*)array_get(') - if left_is_ptr { + if left_is_ptr && !node.left_type.has_flag(.shared_f) { g.write('*') } g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } g.write(', ') g.expr(node.index) g.write('))') @@ -4497,6 +4518,24 @@ _Interface* I_${cctype}_to_Interface_${interface_name}_ptr($cctype* x) { fn (mut g Gen) array_init(it ast.ArrayInit) { type_sym := g.table.get_type_symbol(it.typ) + styp := g.typ(it.typ) + mut shared_styp := '' // only needed for shared &[]{...} + is_amp := g.is_amp + g.is_amp = false + if is_amp { + g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + if g.is_shared { + mut shared_typ := it.typ.set_flag(.shared_f) + shared_styp = g.typ(shared_typ) + g.writeln('($shared_styp*)memdup(&($shared_styp){.val = ') + } else { + g.write('($styp*)memdup(&') // TODO: doesn't work with every compiler + } + } else { + if g.is_shared { + g.writeln('{.val = ($styp*)') + } + } if type_sym.kind == .array_fixed { g.write('{') for i, expr in it.exprs { @@ -4568,6 +4607,14 @@ fn (mut g Gen) array_init(it ast.ArrayInit) { } } g.write('}))') + if g.is_shared { + g.write(', .mtx = sync__new_rwmutex()}') + if is_amp { + g.write(', sizeof($shared_styp))') + } + } else if is_amp { + g.write(', sizeof($styp))') + } } // `ui.foo(button)` => diff --git a/vlib/v/tests/shared_array_test.v b/vlib/v/tests/shared_array_test.v new file mode 100644 index 0000000000..2382940f47 --- /dev/null +++ b/vlib/v/tests/shared_array_test.v @@ -0,0 +1,41 @@ +import sync +import time + +fn incr(shared foo []int, index int) { + for _ in 0 .. 100000 { + lock foo { + foo[index] = foo[index] + 1 + } + } + lock foo { + foo[2]++ + } +} + +fn test_shared_array() { + shared foo := &[10, 20, 0] + go incr(shared foo, 0) + go incr(shared foo, 1) + go incr(shared foo, 0) + go incr(shared foo, 1) + for _ in 0 .. 50000 { + lock foo { + foo[0] -= 2 + foo[1] += 3 + } + } + mut finished_threads := 0 + for { + rlock foo { + finished_threads = foo[2] + } + if finished_threads == 4 { + break + } + time.sleep_ms(100) + } + rlock foo { + assert foo[0] == 100010 + assert foo[1] == 350020 + } +} diff --git a/vlib/v/tests/shared_lock_2_test.v b/vlib/v/tests/shared_lock_2_test.v index 9283968731..5944fd5893 100644 --- a/vlib/v/tests/shared_lock_2_test.v +++ b/vlib/v/tests/shared_lock_2_test.v @@ -7,7 +7,7 @@ mut: } fn (shared x St) f(shared y St, shared z St) { - for _ in 0..101 { + for _ in 0 .. 101 { lock x, y { tmp := y.a y.a = x.a @@ -30,7 +30,7 @@ fn test_shared_receiver_lock() { a: 1 } go x.f(shared y, shared z) - for _ in 0..100 { + for _ in 0 .. 100 { lock x, y { tmp := x.a x.a = y.a @@ -38,8 +38,8 @@ fn test_shared_receiver_lock() { } } // the following would be a good application for a channel - for finished := false; ; { - lock z { + for finished := false; true; { + rlock z { finished = z.a == 0 } if finished { @@ -47,7 +47,7 @@ fn test_shared_receiver_lock() { } time.sleep_ms(100) } - lock x, y { + rlock x, y { assert x.a == 7 && y.a == 5 } } diff --git a/vlib/v/tests/shared_lock_3_test.v b/vlib/v/tests/shared_lock_3_test.v index bdae101403..ad6a0682fb 100644 --- a/vlib/v/tests/shared_lock_3_test.v +++ b/vlib/v/tests/shared_lock_3_test.v @@ -7,7 +7,7 @@ mut: } fn f(shared x St, shared z St) { - for _ in 0..reads_per_thread { + for _ in 0 .. reads_per_thread { rlock x { // other instances may read at the same time time.sleep_ms(1) assert x.a == 7 || x.a == 5 @@ -20,8 +20,8 @@ fn f(shared x St, shared z St) { const ( reads_per_thread = 30 - read_threads = 10 - writes = 5 + read_threads = 10 + writes = 5 ) fn test_shared_lock() { @@ -32,21 +32,21 @@ fn test_shared_lock() { shared z := &St{ a: read_threads } - for _ in 0..read_threads { + for _ in 0 .. read_threads { go f(shared x, shared z) } - for i in 0..writes { + for i in 0 .. writes { lock x { // wait for ongoing reads to finish, don't start new ones x.a = 17 // this should never be read time.sleep_ms(50) - x.a = if (i&1) == 0 { 7 } else { 5 } + x.a = if (i & 1) == 0 { 7 } else { 5 } } // now new reads are possible again time.sleep_ms(20) } // wait until all read threads are finished - for finished := false; ; { + for finished := false; true; { mut rr := 0 - lock z { + rlock z { rr = z.a finished = z.a == 0 } diff --git a/vlib/v/tests/shared_lock_4_test.v b/vlib/v/tests/shared_lock_4_test.v index f4ae7f98ba..e973eee05b 100644 --- a/vlib/v/tests/shared_lock_4_test.v +++ b/vlib/v/tests/shared_lock_4_test.v @@ -7,7 +7,7 @@ mut: } fn (shared x St) f(shared z St) { - for _ in 0..reads_per_thread { + for _ in 0 .. reads_per_thread { rlock x { // other instances may read at the same time time.sleep_ms(1) assert x.a == 7 || x.a == 5 @@ -20,8 +20,8 @@ fn (shared x St) f(shared z St) { const ( reads_per_thread = 30 - read_threads = 10 - writes = 5 + read_threads = 10 + writes = 5 ) fn test_shared_lock() { @@ -32,21 +32,21 @@ fn test_shared_lock() { shared z := &St{ a: read_threads } - for _ in 0..read_threads { + for _ in 0 .. read_threads { go x.f(shared z) } - for i in 0..writes { + for i in 0 .. writes { lock x { // wait for ongoing reads to finish, don't start new ones x.a = 17 // this value should never be read time.sleep_ms(50) - x.a = if (i&1) == 0 { 7 } else { 5 } + x.a = if (i & 1) == 0 { 7 } else { 5 } } // now new reads are possible again time.sleep_ms(20) } // wait until all read threads are finished - for finished := false; ; { + for finished := false; true; { mut rr := 0 - lock z { + rlock z { rr = z.a finished = z.a == 0 } diff --git a/vlib/v/tests/shared_lock_test.v b/vlib/v/tests/shared_lock_test.v index 9fc2a35531..29feedf95b 100644 --- a/vlib/v/tests/shared_lock_test.v +++ b/vlib/v/tests/shared_lock_test.v @@ -7,7 +7,7 @@ mut: } fn f(shared x St, shared y St, shared z St) { - for _ in 0..101 { + for _ in 0 .. 101 { lock x, y { tmp := y.a y.a = x.a @@ -30,7 +30,7 @@ fn test_shared_lock() { a: 1 } go f(shared x, shared y, shared z) - for _ in 0..100 { + for _ in 0 .. 100 { lock x, y { tmp := x.a x.a = y.a @@ -38,8 +38,8 @@ fn test_shared_lock() { } } // the following would be a good application for a channel - for finished := false; ; { - lock z { + for finished := false; true; { + rlock z { finished = z.a == 0 } if finished { @@ -47,7 +47,7 @@ fn test_shared_lock() { } time.sleep_ms(100) } - lock x, y { + rlock x, y { assert x.a == 7 && y.a == 5 } }