all: make `lock` and `rlock` dead lock free :-) (#8534)
parent
f4b757e47d
commit
9dcf673216
|
@ -646,7 +646,7 @@ pub:
|
||||||
pub struct LockExpr {
|
pub struct LockExpr {
|
||||||
pub:
|
pub:
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
is_rlock bool
|
is_rlock []bool
|
||||||
pos token.Position
|
pos token.Position
|
||||||
pub mut:
|
pub mut:
|
||||||
lockeds []Ident // `x`, `y` in `lock x, y {`
|
lockeds []Ident // `x`, `y` in `lock x, y {`
|
||||||
|
|
|
@ -1081,6 +1081,15 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
||||||
v.is_changed = true
|
v.is_changed = true
|
||||||
if v.typ.share() == .shared_t {
|
if v.typ.share() == .shared_t {
|
||||||
if expr.name !in c.locked_names {
|
if expr.name !in c.locked_names {
|
||||||
|
if c.locked_names.len > 0 || c.rlocked_names.len > 0 {
|
||||||
|
if expr.name in c.rlocked_names {
|
||||||
|
c.error('$expr.name has an `rlock` but needs a `lock`',
|
||||||
|
expr.pos)
|
||||||
|
} else {
|
||||||
|
c.error('$expr.name must be added to the `lock` list above',
|
||||||
|
expr.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
to_lock = expr.name
|
to_lock = expr.name
|
||||||
pos = expr.pos
|
pos = expr.pos
|
||||||
}
|
}
|
||||||
|
@ -2620,6 +2629,8 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
left_type = left_type.set_nr_muls(1)
|
left_type = left_type.set_nr_muls(1)
|
||||||
}
|
}
|
||||||
|
} else if left_type.has_flag(.shared_f) {
|
||||||
|
left_type = left_type.clear_flag(.shared_f)
|
||||||
}
|
}
|
||||||
if ident_var_info.share == .atomic_t {
|
if ident_var_info.share == .atomic_t {
|
||||||
left_type = left_type.set_flag(.atomic_f)
|
left_type = left_type.set_flag(.atomic_f)
|
||||||
|
@ -4540,6 +4551,9 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
|
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
|
||||||
|
if c.rlocked_names.len > 0 || c.locked_names.len > 0 {
|
||||||
|
c.error('nested `lock`/`rlock` not allowed', node.pos)
|
||||||
|
}
|
||||||
for i in 0 .. node.lockeds.len {
|
for i in 0 .. node.lockeds.len {
|
||||||
c.ident(mut node.lockeds[i])
|
c.ident(mut node.lockeds[i])
|
||||||
id := node.lockeds[i]
|
id := node.lockeds[i]
|
||||||
|
@ -4555,18 +4569,15 @@ pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
|
||||||
} else if id.name in c.rlocked_names {
|
} else if id.name in c.rlocked_names {
|
||||||
c.error('`$id.name` is already read-locked', id.pos)
|
c.error('`$id.name` is already read-locked', id.pos)
|
||||||
}
|
}
|
||||||
if node.is_rlock {
|
if node.is_rlock[i] {
|
||||||
c.rlocked_names << id.name
|
c.rlocked_names << id.name
|
||||||
} else {
|
} else {
|
||||||
c.locked_names << id.name
|
c.locked_names << id.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
if node.is_rlock {
|
c.rlocked_names = []
|
||||||
c.rlocked_names = c.rlocked_names[..c.rlocked_names.len - node.lockeds.len]
|
c.locked_names = []
|
||||||
} else {
|
|
||||||
c.locked_names = c.locked_names[..c.locked_names.len - node.lockeds.len]
|
|
||||||
}
|
|
||||||
// void for now... maybe sometime `x := lock a { a.getval() }`
|
// void for now... maybe sometime `x := lock a { a.getval() }`
|
||||||
return table.void_type
|
return table.void_type
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
vlib/v/checker/tests/lock_already_locked.vv:11:9: error: `a` is already locked
|
vlib/v/checker/tests/lock_already_locked.vv:11:3: error: nested `lock`/`rlock` not allowed
|
||||||
9 | }
|
9 | }
|
||||||
10 | lock a {
|
10 | lock a {
|
||||||
11 | rlock a {
|
11 | rlock a {
|
||||||
| ^
|
| ~~~~~
|
||||||
12 | a.x++
|
12 | a.x++
|
||||||
13 | }
|
13 | }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
vlib/v/checker/tests/lock_already_rlocked.vv:11:8: error: `a` is already read-locked
|
vlib/v/checker/tests/lock_already_rlocked.vv:11:3: error: nested `lock`/`rlock` not allowed
|
||||||
9 | }
|
9 | }
|
||||||
10 | rlock a {
|
10 | rlock a {
|
||||||
11 | lock a {
|
11 | lock a {
|
||||||
| ^
|
| ~~~~
|
||||||
12 | a.x++
|
12 | a.x++
|
||||||
13 | }
|
13 | }
|
||||||
|
|
|
@ -26,14 +26,14 @@ vlib/v/checker/tests/shared_bad_args.vv:51:13: error: a is `shared` and must be
|
||||||
| ^
|
| ^
|
||||||
52 | println('$w $x')
|
52 | println('$w $x')
|
||||||
53 | }
|
53 | }
|
||||||
vlib/v/checker/tests/shared_bad_args.vv:61:3: error: r is `shared` and must be `lock`ed to be passed as `mut`
|
vlib/v/checker/tests/shared_bad_args.vv:61:3: error: r must be added to the `lock` list above
|
||||||
59 | shared r := Qr{ a: 7 }
|
59 | shared r := Qr{ a: 7 }
|
||||||
60 | lock s {
|
60 | lock s {
|
||||||
61 | r.s_mut(mut s)
|
61 | r.s_mut(mut s)
|
||||||
| ^
|
| ^
|
||||||
62 | }
|
62 | }
|
||||||
63 | lock r {
|
63 | lock r {
|
||||||
vlib/v/checker/tests/shared_bad_args.vv:64:15: error: s is `shared` and must be `lock`ed to be passed as `mut`
|
vlib/v/checker/tests/shared_bad_args.vv:64:15: error: s must be added to the `lock` list above
|
||||||
62 | }
|
62 | }
|
||||||
63 | lock r {
|
63 | lock r {
|
||||||
64 | r.s_mut(mut s)
|
64 | r.s_mut(mut s)
|
||||||
|
|
|
@ -1480,12 +1480,43 @@ pub fn (mut f Fmt) array_decompose(node ast.ArrayDecompose) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut f Fmt) lock_expr(lex ast.LockExpr) {
|
pub fn (mut f Fmt) lock_expr(lex ast.LockExpr) {
|
||||||
f.write(if lex.is_rlock { 'rlock ' } else { 'lock ' })
|
mut num_locked := 0
|
||||||
for i, v in lex.lockeds {
|
mut num_rlocked := 0
|
||||||
if i > 0 {
|
for is_rlock in lex.is_rlock {
|
||||||
f.write(', ')
|
if is_rlock {
|
||||||
|
num_rlocked++
|
||||||
|
} else {
|
||||||
|
num_locked++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if num_locked > 0 || num_rlocked == 0 {
|
||||||
|
f.write('lock ')
|
||||||
|
mut n := 0
|
||||||
|
for i, v in lex.lockeds {
|
||||||
|
if !lex.is_rlock[i] {
|
||||||
|
if n > 0 {
|
||||||
|
f.write(', ')
|
||||||
|
}
|
||||||
|
f.expr(v)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if num_rlocked > 0 {
|
||||||
|
if num_locked > 0 {
|
||||||
|
f.write('; ')
|
||||||
|
}
|
||||||
|
f.write('rlock ')
|
||||||
|
mut n := 0
|
||||||
|
for i, v in lex.lockeds {
|
||||||
|
if lex.is_rlock[i] {
|
||||||
|
if n > 0 {
|
||||||
|
f.write(', ')
|
||||||
|
}
|
||||||
|
f.expr(v)
|
||||||
|
n++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f.expr(v)
|
|
||||||
}
|
}
|
||||||
f.write(' {')
|
f.write(' {')
|
||||||
f.writeln('')
|
f.writeln('')
|
||||||
|
|
|
@ -3405,22 +3405,54 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) lock_expr(node ast.LockExpr) {
|
fn (mut g Gen) lock_expr(node ast.LockExpr) {
|
||||||
mut lock_prefixes := []string{len: 0, cap: node.lockeds.len}
|
mut mtxs := ''
|
||||||
for id in node.lockeds {
|
if node.lockeds.len == 0 {
|
||||||
|
// this should not happen
|
||||||
|
} else if node.lockeds.len == 1 {
|
||||||
|
id := node.lockeds[0]
|
||||||
name := id.name
|
name := id.name
|
||||||
deref := if id.is_mut { '->' } else { '.' }
|
deref := if id.is_mut { '->' } else { '.' }
|
||||||
lock_prefix := if node.is_rlock { 'r' } else { '' }
|
lock_prefix := if node.is_rlock[0] { 'r' } else { '' }
|
||||||
lock_prefixes << lock_prefix // keep for unlock
|
|
||||||
g.writeln('sync__RwMutex_${lock_prefix}lock(&$name${deref}mtx);')
|
g.writeln('sync__RwMutex_${lock_prefix}lock(&$name${deref}mtx);')
|
||||||
|
} else {
|
||||||
|
mtxs = g.new_tmp_var()
|
||||||
|
g.writeln('uintptr_t _arr_$mtxs[$node.lockeds.len];')
|
||||||
|
g.writeln('bool _isrlck_$mtxs[$node.lockeds.len];')
|
||||||
|
for i, id in node.lockeds {
|
||||||
|
name := id.name
|
||||||
|
deref := if id.is_mut { '->' } else { '.' }
|
||||||
|
g.writeln('_arr_$mtxs[$i] = &$name${deref}mtx;')
|
||||||
|
// TODO: fix `vfmt` to allow this in string interpolation
|
||||||
|
is_rlock_str := node.is_rlock[i].str()
|
||||||
|
g.writeln('_isrlck_$mtxs[$i] = $is_rlock_str;')
|
||||||
|
}
|
||||||
|
g.writeln('__sort_ptr(_arr_$mtxs, _isrlck_$mtxs, $node.lockeds.len);')
|
||||||
|
g.writeln('for (int $mtxs=0; $mtxs<$node.lockeds.len; $mtxs++) {')
|
||||||
|
g.writeln('\tif ($mtxs && _arr_$mtxs[$mtxs] == _arr_$mtxs[$mtxs-1]) continue;')
|
||||||
|
g.writeln('\tif (_isrlck_$mtxs[$mtxs])')
|
||||||
|
g.writeln('\t\tsync__RwMutex_rlock((sync__RwMutex*)_arr_$mtxs[$mtxs]);')
|
||||||
|
g.writeln('\telse')
|
||||||
|
g.writeln('\t\tsync__RwMutex_lock((sync__RwMutex*)_arr_$mtxs[$mtxs]);')
|
||||||
|
g.writeln('}')
|
||||||
}
|
}
|
||||||
g.stmts(node.stmts)
|
g.stmts(node.stmts)
|
||||||
// unlock in reverse order
|
if node.lockeds.len == 0 {
|
||||||
for i := node.lockeds.len - 1; i >= 0; i-- {
|
// this should not happen
|
||||||
id := node.lockeds[i]
|
} else if node.lockeds.len == 1 {
|
||||||
lock_prefix := lock_prefixes[i]
|
id := node.lockeds[0]
|
||||||
name := id.name
|
name := id.name
|
||||||
deref := if id.is_mut { '->' } else { '.' }
|
deref := if id.is_mut { '->' } else { '.' }
|
||||||
|
lock_prefix := if node.is_rlock[0] { 'r' } else { '' }
|
||||||
g.writeln('sync__RwMutex_${lock_prefix}unlock(&$name${deref}mtx);')
|
g.writeln('sync__RwMutex_${lock_prefix}unlock(&$name${deref}mtx);')
|
||||||
|
} else {
|
||||||
|
// unlock in reverse order
|
||||||
|
g.writeln('for (int $mtxs=${node.lockeds.len - 1}; $mtxs>=0; $mtxs--) {')
|
||||||
|
g.writeln('\tif ($mtxs && _arr_$mtxs[$mtxs] == _arr_$mtxs[$mtxs-1]) continue;')
|
||||||
|
g.writeln('\tif (_isrlck_$mtxs[$mtxs])')
|
||||||
|
g.writeln('\t\tsync__RwMutex_runlock((sync__RwMutex*)_arr_$mtxs[$mtxs]);')
|
||||||
|
g.writeln('\telse')
|
||||||
|
g.writeln('\t\tsync__RwMutex_unlock((sync__RwMutex*)_arr_$mtxs[$mtxs]);')
|
||||||
|
g.writeln('}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,21 @@ static inline voidptr __dup_shared_array(voidptr src, int sz) {
|
||||||
sync__RwMutex_init(&dest->mtx);
|
sync__RwMutex_init(&dest->mtx);
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
static inline void __sort_ptr(uintptr_t a[], bool b[], int l)
|
||||||
|
{
|
||||||
|
for (int i=1; i<l; i++) {
|
||||||
|
uintptr_t ins = a[i];
|
||||||
|
bool insb = b[i];
|
||||||
|
int j = i;
|
||||||
|
while(j>0 && (a[j-1] > ins || b[j-1] && !insb)) {
|
||||||
|
a[j] = a[j-1];
|
||||||
|
b[j] = b[j-1];
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
a[j] = ins;
|
||||||
|
b[j] = insb;
|
||||||
|
}
|
||||||
|
}
|
||||||
'
|
'
|
||||||
c_common_macros = '
|
c_common_macros = '
|
||||||
#define EMPTY_VARG_INITIALIZATION 0
|
#define EMPTY_VARG_INITIALIZATION 0
|
||||||
|
|
|
@ -7,31 +7,46 @@ fn (mut p Parser) lock_expr() ast.LockExpr {
|
||||||
// TODO Handle aliasing sync
|
// TODO Handle aliasing sync
|
||||||
p.register_auto_import('sync')
|
p.register_auto_import('sync')
|
||||||
mut pos := p.tok.position()
|
mut pos := p.tok.position()
|
||||||
is_rlock := p.tok.kind == .key_rlock
|
|
||||||
p.next()
|
|
||||||
mut lockeds := []ast.Ident{}
|
mut lockeds := []ast.Ident{}
|
||||||
for p.tok.kind == .name {
|
mut is_rlocked := []bool{}
|
||||||
lockeds << ast.Ident{
|
for {
|
||||||
language: table.Language.v
|
if p.tok.kind == .lcbr {
|
||||||
pos: p.tok.position()
|
goto start_stmts
|
||||||
mod: p.mod
|
}
|
||||||
name: p.tok.lit
|
is_rlock := p.tok.kind == .key_rlock
|
||||||
is_mut: true
|
if !is_rlock && p.tok.kind != .key_lock {
|
||||||
info: ast.IdentVar{}
|
p.error_with_pos('unexpected `$p.tok.lit`; `lock` or `rlock` expected', p.tok.position())
|
||||||
scope: p.scope
|
|
||||||
}
|
}
|
||||||
p.next()
|
p.next()
|
||||||
if p.tok.kind == .lcbr {
|
for p.tok.kind == .name {
|
||||||
break
|
lockeds << ast.Ident{
|
||||||
|
language: table.Language.v
|
||||||
|
pos: p.tok.position()
|
||||||
|
mod: p.mod
|
||||||
|
name: p.tok.lit
|
||||||
|
is_mut: true
|
||||||
|
info: ast.IdentVar{}
|
||||||
|
scope: p.scope
|
||||||
|
}
|
||||||
|
is_rlocked << is_rlock
|
||||||
|
p.next()
|
||||||
|
if p.tok.kind == .lcbr {
|
||||||
|
goto start_stmts
|
||||||
|
}
|
||||||
|
if p.tok.kind == .semicolon {
|
||||||
|
p.next()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.check(.comma)
|
||||||
}
|
}
|
||||||
p.check(.comma)
|
|
||||||
}
|
}
|
||||||
|
start_stmts:
|
||||||
stmts := p.parse_block()
|
stmts := p.parse_block()
|
||||||
pos.update_last_line(p.prev_tok.line_nr)
|
pos.update_last_line(p.prev_tok.line_nr)
|
||||||
return ast.LockExpr{
|
return ast.LockExpr{
|
||||||
lockeds: lockeds
|
lockeds: lockeds
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
is_rlock: is_rlock
|
is_rlock: is_rlocked
|
||||||
pos: pos
|
pos: pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
struct St {
|
||||||
|
mut:
|
||||||
|
a int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (shared x St) f(shared y St, shared z St) {
|
||||||
|
for _ in 0 .. 10000 {
|
||||||
|
lock x, y, z {
|
||||||
|
tmp := z.a
|
||||||
|
z.a = y.a
|
||||||
|
y.a = x.a
|
||||||
|
x.a = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (shared x St) g(shared y St, shared z St) {
|
||||||
|
for _ in 0 .. 10000 {
|
||||||
|
lock z, x, y {
|
||||||
|
tmp := x.a
|
||||||
|
x.a = z.a
|
||||||
|
z.a = y.a
|
||||||
|
y.a = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn h(shared x St, shared y St, shared z St) {
|
||||||
|
for _ in 0 .. 10000 {
|
||||||
|
lock y, x, z {
|
||||||
|
tmp := y.a
|
||||||
|
y.a = z.a
|
||||||
|
z.a = x.a
|
||||||
|
x.a = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_shared_receiver_lock() {
|
||||||
|
shared x := &St{
|
||||||
|
a: 5
|
||||||
|
}
|
||||||
|
shared y := &St{
|
||||||
|
a: 7
|
||||||
|
}
|
||||||
|
shared z := &St{
|
||||||
|
a: 1
|
||||||
|
}
|
||||||
|
t1 := go x.f(shared y, shared z)
|
||||||
|
t2 := go x.f(shared y, shared z)
|
||||||
|
t3 := go h(shared x, shared y, shared z)
|
||||||
|
for _ in 0 .. 10000 {
|
||||||
|
lock z, y, x {
|
||||||
|
tmp := y.a
|
||||||
|
y.a = x.a
|
||||||
|
x.a = z.a
|
||||||
|
z.a = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t1.wait()
|
||||||
|
t2.wait()
|
||||||
|
t3.wait()
|
||||||
|
rlock x, y, z{
|
||||||
|
assert x.a == 7
|
||||||
|
assert y.a == 1
|
||||||
|
assert z.a == 5
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
struct St {
|
||||||
|
mut:
|
||||||
|
a int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (shared x St) f(shared y St, shared z St) {
|
||||||
|
for _ in 0 .. 10000 {
|
||||||
|
lock x; rlock y, z {
|
||||||
|
x.a = y.a + z.a
|
||||||
|
if x.a > 1000000 {
|
||||||
|
x.a /= 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (shared x St) g(shared y St, shared z St) {
|
||||||
|
for _ in 0 .. 10000 {
|
||||||
|
rlock x; lock y, z {
|
||||||
|
y.a += x.a
|
||||||
|
if y.a > 1000000 {
|
||||||
|
y.a /= 2
|
||||||
|
}
|
||||||
|
z.a += x.a
|
||||||
|
if z.a > 1000000 {
|
||||||
|
z.a /= 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_shared_receiver_lock() {
|
||||||
|
shared x := St{
|
||||||
|
a: 5
|
||||||
|
}
|
||||||
|
shared y := St{
|
||||||
|
a: 7
|
||||||
|
}
|
||||||
|
shared z := St{
|
||||||
|
a: 103
|
||||||
|
}
|
||||||
|
t1 := go x.f(shared y, shared x)
|
||||||
|
t2 := go y.f(shared y, shared z)
|
||||||
|
t3 := go z.g(shared x, shared z)
|
||||||
|
t4 := go x.g(shared x, shared x)
|
||||||
|
t5 := go z.f(shared z, shared z)
|
||||||
|
t1.wait()
|
||||||
|
t2.wait()
|
||||||
|
t3.wait()
|
||||||
|
t4.wait()
|
||||||
|
t5.wait()
|
||||||
|
// the result is unpredictable - but should be positive
|
||||||
|
rlock x, y, z {
|
||||||
|
assert x.a > 10000
|
||||||
|
assert y.a > 10000
|
||||||
|
assert z.a > 10000
|
||||||
|
println('$x.a $y.a $z.a')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// integer values from -2^191 .. 2^191-1
|
||||||
|
struct Large {
|
||||||
|
mut:
|
||||||
|
l u64
|
||||||
|
m u64
|
||||||
|
h u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Large) clone() Large {
|
||||||
|
r := Large{ l: a.l, m: a.m h: a.h }
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut a Large) add(b Large) {
|
||||||
|
oldl := a.l
|
||||||
|
a.l += b.l
|
||||||
|
oldm := a.m
|
||||||
|
if a.l < oldl { a.m++ }
|
||||||
|
a.m += b.m
|
||||||
|
if a.m < oldm || (a.l < oldl && a.m <= oldm) { a.h++ }
|
||||||
|
a.h += b.h
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doub_large(shared a Large, shared b Large, shared c Large, shared d Large, shared e Large) {
|
||||||
|
for _ in 0..50 {
|
||||||
|
lock a, b; rlock c, d, e {
|
||||||
|
// double the sum by adding all objects to a or b
|
||||||
|
old_a := a.clone()
|
||||||
|
a.add(b)
|
||||||
|
b.add(old_a)
|
||||||
|
a.add(c)
|
||||||
|
b.add(d)
|
||||||
|
a.add(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mixed_order_lock_rlock() {
|
||||||
|
// initialze objects so that their sum = 1
|
||||||
|
shared a := Large{ l: 4 }
|
||||||
|
shared b := Large{ l: u64(-7), m: u64(-1) h: u64(-1) }
|
||||||
|
shared c := Large{ l: 17 }
|
||||||
|
shared d := Large{ l: u64(-11), m: u64(-1) h: u64(-1) }
|
||||||
|
shared e := Large{ l: u64(-2), m: u64(-1) h: u64(-1) }
|
||||||
|
// spawn 3 threads for doubling with different orders of objects
|
||||||
|
t1 := go doub_large(shared a, shared b, shared c, shared d, shared e)
|
||||||
|
t2 := go doub_large(shared e, shared b, shared a, shared c, shared d)
|
||||||
|
t3 := go doub_large(shared b, shared a, shared e, shared d, shared c)
|
||||||
|
t1.wait()
|
||||||
|
t2.wait()
|
||||||
|
t3.wait()
|
||||||
|
// calculate the sum after 3*50 doublings
|
||||||
|
mut sum := Large{}
|
||||||
|
rlock a, b, c, d, e {
|
||||||
|
sum.add(a)
|
||||||
|
sum.add(b)
|
||||||
|
sum.add(c)
|
||||||
|
sum.add(d)
|
||||||
|
sum.add(e)
|
||||||
|
}
|
||||||
|
// the sum should be 2^150
|
||||||
|
assert sum.l == 0
|
||||||
|
assert sum.m == 0
|
||||||
|
assert sum.h == 4194304
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue