I saw an example in vlang, which use a special keyword lock to lock a block of code. And I want to implement it (let's call it lockBlock) in nim. Ideally, it should be something like
var val = 0
proc run() =
lockBlock:
var n = val
for i in ..10000:
n += 1 - 2*(i mod 2) # what is doing here does not mater
val = n
for i in 1..8: spawn run()
sync()
echo val # correct output should be 8
I tried the following implementation of lockBlock, but it didn't work
import locks, threadpool
template lockBlock(code: untyped): untyped =
var lock {.global.} : Lock # declare to globals
# template local variable for one time initialization
var uninitialized = true
if uninitialized:
uninitialized = false
initLock(lock)
# copy withLock implementation
block:
acquire(lock)
defer:
release(lock)
{.locks: [lock].}:
code
How can I make the above code work ??
FYI, a program that have the same effect with explicit declaration of a lock
import locks, threadpool
var valLock : Lock
initLock(valLock)
var val = 0
proc run() =
withLock valLock:
var n = val
for i in ..10000:
n += 1 - 2*(i mod 2)
val = n
for i in 1..8: spawn run()
sync()
echo val # output 8
This works, check your basic program logic, it's wrong.
import locks, threadpool
var lock: Lock # declare to globals
initLock(lock)
template lockBlock(code: untyped): untyped =
# copy withLock implementation
acquire(lock)
try:
{.locks: [lock].}:
code
finally:
release(lock)
var val = 0
proc run() =
lockBlock:
var n = val
for i in ..10000:
n += 1 - 2*(i mod 2) # what is doing here does not mater
val = n
for i in 1..8: spawn run()
sync()
echo val # correct output should be 8
The point is that I don't want to explicitly declare a lock variable in globals.
I want write something that whenever I use lockBlock, it implicitly create a lock variable and automatically acquire lock and release lock.
The effect is like this example written in vlang, which has a lock section to prevent race on cursor
const STORIES_URL = 'https://hacker-news.firebaseio.com/v0/topstories.json'
const ITEM_URL_BASE = 'https://hacker-news.firebaseio.com/v0/item'
struct Story {
title string
}
fn main() {
resp := http.get(STORIES_URL)?
ids := json.decode([]int, resp.body)?
mut cursor := 0
for _ in 0..8 {
go fn() {
for {
lock {
if cursor >= ids.len {
break
}
id := ids[cursor]
cursor++
}
resp := http.get('$ITEM_URL_BASE/$id.json')?
story := json.decode(Story, resp.body)?
println(story.title)
}
}()
}
runtime.wait()
}
Your original example works if you also make uninitialized global because if you don't, it will create a new var for every call to lockBlock. For example:
# ty.nim
import locks
template lockBlock*(code: untyped): untyped =
var lock {.global.}: Lock # declare to globals
var uninitialized {.global.} = true
# template local variable for one time initialization
if uninitialized:
uninitialized = false
initLock(lock)
# copy withLock implementation
block:
acquire(lock)
defer:
release(lock)
{.locks: [lock].}:
code
# tx.nim
import ty, threadpool
var val = 0
proc run() =
lockBlock:
var n = val
for i in ..10000:
n += 1 - 2*(i mod 2) # what is doing here does not mater
val = n
for i in 1..8: spawn run()
sync()
echo val # correct output should be 8