I've been wondering if something like Java's ThreadLocal (which I use a massively in (Java) server-side code), could be defined in Nim.

I tried those two approaches, but neither is valid code. It seems to me it isn't possible.

Attempt #1:

type
  ThreadLocal*[T] = object
    ## Represents a thread-local, similar to ThreadLocals in Java.
    initialised*: bool
      ## Is this thread local already initialised?
    value*: T
      ## The thread local value.
  
  InitThreadLocalProc*[T] = proc(): T {.nimcall, gcsafe.}
    ## Type of a proc that lazy initialises a ThreadLocal.

proc getValue*[T](init: InitThreadLocalProc[T]): var T =
  ## Returns the value of a thread-local.
  var myThreadVar {.threadvar.}: ThreadLocal[T]
  if not myThreadVar.initialised:
    myThreadVar.value = init()
    myThreadVar.initialised = true
  return myThreadVar.value

proc Returns42(): int =
  42

echo("TL: ",getValue(Returns42))

Attempt #2 (would be preferable to #1, because you are not limited to 1 ThreadLocal per type):

type
  InitThreadLocalProc*[T] = proc(): T {.nimcall, gcsafe.}
    ## Type of a proc that lazy initialises a ThreadLocal.
  
  ThreadLocal*[T] = object
    ## Represents a thread-local, similar to ThreadLocals in Java.
    lazyInit: InitThreadLocalProc[T]
      ## The lazy initialisation proc.
    initialised {.threadvar.}: bool
      ## Is this thread local already initialised?
    value {.threadvar.}: T
      ## The thread local value.


proc initThreadLocal*[T](init: InitThreadLocalProc[T]): ThreadLocal[T] =
  ## Initialises a ThreadLocal.
  result.lazyInit = init

proc getValue*[T](tl: ThreadLocal[T]): var T =
  ## Returns the value of a thread-local.
  if not tl.initialised:
    tl.value = tl.lazyInit()
    tl.initialised = true
  return tl.value


proc Returns42(): int =
  42

var testThreadLocal = initThreadLocal[int](Returns42)

echo("TL: ",getValue(testThreadLocal))

2018-02-12 13:29:35
Maybe attempt #1 when you make getValue a .dirty template with a when not declared check to prevent multiple declarations of the thread var. Seems a bad hack though.
2018-02-12 13:52:21

Mayby, the better solution is just to do explicit "eager" initialisation of threadvars, assuming the dev can insert code at all thread creation points. This seems to work:

import locks

type
  InitThreadLocalsProc* = proc(): void {.nimcall, gcsafe.}
    ## Type of a proc that initialises threadvars of some module in a new Thread.

const MAX_INIT_PROCS = 100
  ## Maximum number of registered InitThreadLocalsProc.

var allInitThreadLocalsProcs: array[MAX_INIT_PROCS,InitThreadLocalsProc]
  ## All registered InitThreadLocalsProc
var registeredThreadLocalsProcs: int
  ## Number of registered InitThreadLocalsProc.
var used: bool
  ## Was some thread already initialised?
var arrayLock: Lock
  ## Lock to allow safe multi-threaded use.

initLock(arrayLock)

proc registerThreadLocalInitialiser*(init: InitThreadLocalsProc) =
  ## Registers a InitThreadLocalsProc.
  ## This cannot be called anymore, after the first call to
  ## runThreadLocalInitialisers().
  if init.isNil:
    raise newException(Exception, "init is nil!")
  acquire(arrayLock)
  try:
    if used:
      raise newException(Exception, "New Threads already initialised!")
    if registeredThreadLocalsProcs == MAX_INIT_PROCS:
      raise newException(Exception, "Too many ThreadLocal Initialisers!")
    allInitThreadLocalsProcs[registeredThreadLocalsProcs] = init
    inc registeredThreadLocalsProcs
  finally:
    release(arrayLock)

proc runThreadLocalInitialisers*() =
  ## Runs all InitThreadLocalsProcs.
  ## Only call this after all initialisers have been registered.
  var initialisers: array[MAX_INIT_PROCS,InitThreadLocalsProc]
  var count: int
  acquire(arrayLock)
  try:
    used = true
    initialisers = allInitThreadLocalsProcs
    count = registeredThreadLocalsProcs
  finally:
    release(arrayLock)
  for i in 0..<count:
    initialisers[i]()

And a test:

import threadlocal

proc initA() {.gcsafe.} =
  echo("Running initA() in thread ",getThreadId())

proc initB() {.gcsafe.} =
  echo("Running initB() in thread ",getThreadId())

registerThreadLocalInitialiser(initA)
registerThreadLocalInitialiser(initB)

proc whatever() {.thread.} =
  echo("In Thread ",getThreadId())
  runThreadLocalInitialisers()
  echo("Thread ",getThreadId(), " done")

var thread: Thread[void]
createThread[void](thread, whatever)
joinThread(thread)

echo("DONE")

2018-02-12 14:28:41
We had something like that and removed it because it is racy. You can call registerThreadLocalInitialiser after a thread has been created.
2018-02-12 14:35:43