Skip to content

Commit

Permalink
checker, cgen: implement auto-lock for a[i]++, a[i]-- (#5817)
Browse files Browse the repository at this point in the history
  • Loading branch information
UweKrueger committed Jul 13, 2020
1 parent b04fff2 commit 6e6010d
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 2 deletions.
2 changes: 2 additions & 0 deletions vlib/v/ast/ast.v
Expand Up @@ -404,6 +404,8 @@ pub:
op token.Kind
expr Expr
pos token.Position
pub mut:
auto_locked string
}

pub struct PrefixExpr {
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/checker.v
Expand Up @@ -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() {
Expand All @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/gen/cgen.v
Expand Up @@ -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 {
Expand Down
49 changes: 49 additions & 0 deletions 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
}
38 changes: 38 additions & 0 deletions 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
}
}

0 comments on commit 6e6010d

Please sign in to comment.