From 6e6010d198ffaf087a35e3fad49dfade44b850b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Mon, 13 Jul 2020 14:01:32 +0200 Subject: [PATCH] checker, cgen: implement auto-lock for `a[i]++`, `a[i]--` (#5817) --- vlib/v/ast/ast.v | 2 ++ vlib/v/checker/checker.v | 4 +-- vlib/v/gen/cgen.v | 7 +++++ vlib/v/tests/autolock_array1_test.v | 49 +++++++++++++++++++++++++++++ vlib/v/tests/autolock_array2_test.v | 38 ++++++++++++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 vlib/v/tests/autolock_array1_test.v create mode 100644 vlib/v/tests/autolock_array2_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 4ae20042bc..2d5c85d902 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -404,6 +404,8 @@ pub: op token.Kind expr Expr pos token.Position +pub mut: + auto_locked string } pub struct PrefixExpr { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index d430415797..73fec9cc86 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2855,7 +2855,7 @@ fn (c Checker) has_return(stmts []ast.Stmt) ?bool { return none } -pub fn (mut c Checker) postfix_expr(node ast.PostfixExpr) table.Type { +pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) table.Type { typ := c.expr(node.expr) typ_sym := c.table.get_type_symbol(typ) // if !typ.is_number() { @@ -2864,7 +2864,7 @@ pub fn (mut c Checker) postfix_expr(node ast.PostfixExpr) table.Type { c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)', node.pos) } else { - c.fail_if_immutable(node.expr) + node.auto_locked, _ = c.fail_if_immutable(node.expr) } if (typ.is_ptr() || typ_sym.is_pointer()) && !c.inside_unsafe { c.error('pointer arithmetic is only allowed in `unsafe` blocks', node.pos) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index c26cedfe04..e6d4661344 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1745,10 +1745,17 @@ fn (mut g Gen) expr(node ast.Expr) { g.write(')') } ast.PostfixExpr { + if node.auto_locked != '' { + g.writeln('sync__RwMutex_w_lock($node.auto_locked->mtx);') + } g.inside_map_postfix = true g.expr(node.expr) g.inside_map_postfix = false g.write(node.op.str()) + if node.auto_locked != '' { + g.writeln(';') + g.write('sync__RwMutex_w_unlock($node.auto_locked->mtx)') + } } ast.PrefixExpr { if node.op == .amp { diff --git a/vlib/v/tests/autolock_array1_test.v b/vlib/v/tests/autolock_array1_test.v new file mode 100644 index 0000000000..1f2fe5deb5 --- /dev/null +++ b/vlib/v/tests/autolock_array1_test.v @@ -0,0 +1,49 @@ +import sync +import time + +const ( + iterations_per_thread = 100000 +) + +fn add_elements(shared foo []int, n int) { + for _ in 0 .. iterations_per_thread { + foo << n + } + foo[0]++ +} + +fn test_autolocked_array() { + shared abc := &[0] + go add_elements(shared abc, 1) + go add_elements(shared abc, 3) + for _ in 0 .. iterations_per_thread { + abc << 0 + } + // wait for coroutines to finish - that should really be + // done by channels, yield, semaphore... + for { + mut finished_threads := 0 + rlock abc { + finished_threads = abc[0] + } + if finished_threads == 2 { + break + } + time.sleep_ms(100) + } + // create histogram of results + mut result := [0, 0, 0, 0] + rlock abc { + // automatic rlock for iteration is also not implemented, yet + for v in abc { + if v > 3 { + panic('unexpected element on array') + } + result[v]++ + } + } + assert result[0] == iterations_per_thread + assert result[1] == iterations_per_thread + assert result[2] == 1 // number of non-main threads + assert result[3] == iterations_per_thread +} diff --git a/vlib/v/tests/autolock_array2_test.v b/vlib/v/tests/autolock_array2_test.v new file mode 100644 index 0000000000..737b7a43d8 --- /dev/null +++ b/vlib/v/tests/autolock_array2_test.v @@ -0,0 +1,38 @@ +import sync +import time + +const ( + iterations_per_thread2 = 100000 +) + +fn inc_elements(shared foo []int, n int) { + for _ in 0 .. iterations_per_thread2 { + foo[n]++ + } + foo[0]++ // indicat that thread is finished +} + +fn test_autolocked_array_2() { + shared abc := &[0, 0, 0] + go inc_elements(shared abc, 1) + go inc_elements(shared abc, 2) + for _ in 0 .. iterations_per_thread2 { + abc[2]++ + } + // wait for coroutines to finish - that should really be + // done by channels, yield, semaphore... + for { + mut finished_threads := 0 + rlock abc { + finished_threads = abc[0] + } + if finished_threads == 2 { + break + } + time.sleep_ms(100) + } + rlock abc { + assert abc[1] == iterations_per_thread2 + assert abc[2] == 2 * iterations_per_thread2 + } +}