Hello guys! I need your help, cannot figure out wtf is going on here.
I made a minimal example which produces an "out of memory" error. The idea is simple: create a "spawn" proc which
Since I use shared table, each thread can (safely, due to lock) access other thread information using thread's name.
Here is the code:
import sharedtables
import strformat
import os
import typetraits
type
ThreadName = string
ThreadInfo = object
thread: Thread[ThreadName]
# ... and some other info (channel etc)
var threads: SharedTable[ThreadName, ThreadInfo]
threads.init()
proc threadEntrypoint(name: ThreadName) {.thread.} =
echo &"Inside thread: name={name}"
proc spawn(name: ThreadName) =
echo &"Spawning: name={name}"
threads.withKey(name) do (key: ThreadName, value: var ThreadInfo, pairExists: var bool):
if pairExists:
raise newException(ValueError, "Thread with this name was already registered")
echo &"withKey: name={key}"
value = ThreadInfo()
echo &"Creating thread: name={key}"
value.thread.createThread(param=key, tp=threadEntrypoint)
echo &"Created thread: name={key}"
pairExists = true
when isMainModule:
spawn(name="a")
sleep(1000)
The output is really surprising, I cant google something similar and it's even not an exception:
Spawning: name=a
withKey: name=a
Creating thread: name=a
Created thread: name=a
out of memory
It's probably something related to shared table lock and spawning a thread during this lock (but im not sure). I tried different ways to fill that shared table with spawned threads but didn't succeed.
Thanks for testing it!
I tried different combinations for writing thread instance to sharedTable. Seems that creating temporary variable may help, but other cases simply don't work:
import sharedtables
import strformat
import os
import typetraits
type ThreadInfo = object
thread: Thread[void]
var threads: SharedTable[string, ThreadInfo]
threads.init()
proc threadEntrypoint() {.thread.} =
while true:
echo "OK"
when isMainModule:
let key = "key"
# 1) works (well, if it didn't work nim would worth nothing)
var value = ThreadInfo()
value.thread.createThread(tp=threadEntrypoint)
# 2) illegal storage access
threads.withKey(key) do (key: string, value: var ThreadInfo, pairExists: var bool):
if pairExists:
raise newException(ValueError, "Thread with this name was already registered")
value = ThreadInfo()
value.thread.createThread(tp=threadEntrypoint)
pairExists = true
# 3) works
threads.withValue(key, value) do:
echo "Value already exists"
do:
var info = ThreadInfo()
info.thread.createThread(tp=threadEntrypoint)
threads[key] = info
# 4) hangs forever
threads.withValue(key, _) do:
echo "Value already exists"
do:
threads[key] = ThreadInfo()
threads.mget(key).thread.createThread(tp=threadEntrypoint)
sleep(1000)
Workaround with pointer to table and custom lock ¯_(ツ)_/¯
import tables
import strformat
import os
import typetraits
import locks
type
ThreadName = string
ThreadInfo = object
thread: Thread[ThreadName]
var threads = initTable[ThreadName, ThreadInfo]()
let threadsPtr = threads.addr
var threadsLock: Lock
threadsLock.initLock()
proc threadEntrypoint(name: ThreadName) {.thread.} =
while true:
echo &"Inside thread: name={name}"
proc spawn(name: ThreadName) =
echo &"Spawning: name={name}"
withLock threadsLock:
if name in threadsPtr[]:
raise newException(ValueError, "Thread with this name was already registered")
threadsPtr[][name] = ThreadInfo()
threadsPtr[][name].thread.createThread(param=name, tp=threadEntrypoint)
echo &"Created thread: name={name}"
when isMainModule:
spawn(name="a")
spawn(name="b")
sleep(1000)