When I look at the Nim Forums source, the globals don't have any special pragmas: https://github.com/nim-lang/nimforum/blob/master/src/forum.nim
However when I compile my own Jester web app in release mode, the globals fail if they are not {.threadvar.}. But even when I add that pragma the global vars are nil despite being initialized in a function.
What am I doing wrong here?
I'm doing an experiment with jester called jesterwithplugins that allows you to do certains tasks and setups at the start of each thread. So, if ambitious, you could also write a plugin. But that is very much beta right now.
I'll be making a fourth video in my jester series that deals with databases and addresses this issue as well since you cannot share a common database connection safely in a thread unless it is pooled (or has some other mechanism to handle conflict.)
Hi @jasonfi
The global vars marked with {.threadvar.} needs to be initialized in each thread at creation: Every thread local variable needs to be replicated at thread creation
If you are using Jester with httpbeast, you need to find a method to catch that, since httpbeast doesn't have a callback or similar, when the threads are created (as far as I know?). I use a combination of treeforms pg library with a hacky table, where I store the database pool with the threadID. In each route I use something like the Nim forums createTFD() which checks if the user is logged in. In that template, I also check if my global database pool table has an instance with the threadID - if it does not exist, it is created. By doing that, I create the database pool for each thread the first time it is "activated". But yeah, it does have some overhead.. any one else has some ideas?
If you include treeforms pg library and include the following:
import anotheFileWithVars
# treeform pg: AsyncPool, newAsyncPool, getFreeConnIdx, returnConn
var dbt {.threadvar.}: Table[int, AsyncPool]
proc avaiDbNr(threadId: int): int =
## Check if the thread has a DbConn available
if not dbt.hasKey(threadId):
initVarsDynamic() # This init other global vars with {.threadlocal.}, which cannot be const due to being loaded form a config file.
dbt[threadId] = newAsyncPool(cfgDbHost, cfgDbUser, cfgDbPass, cfgDbName, parseInt(cfgDbPoolConnNr))
return dbt[threadId].getFreeConnIdx()
template currThreadId(): int =
when compileOption("threads"):
getThreadId()
else:
0
proc checkDbPoolIsInitiated*() =
# Init DB connection and vars for thread. Checked in routes.
let threadID = currThreadId()
if not dbt.hasKey(threadId):
initVarsDynamic()
dbt[threadId] = newAsyncPool(cfgDbHost, cfgDbUser, cfgDbPass, cfgDbName, parseInt(cfgDbPoolConnNr))
proc getValueCustom*(query: SqlQuery, args: varargs[string, `$`]): string =
let threadId = currThreadId()
let conIdx = avaiDbNr(threadId)
result = getValue(dbt[threadId].conns[conIdx], query, args)
dbt[threadId].returnConn(conIdx)
anotherFileWithVars.nim
var var1* {.threadvar.}: string
var var2* {.threadvar.}: string
var var3* {.threadvar.}: string
proc initVarsDynamic() =
var1 = "valueFromConfig"
var2 = "valueFromConfig"
var3 = "valueFromConfig"