I've discovered that, given the following code:
{.passC: "-fopenmp", passL: "-fopenmp".}
for k in 1 || 10:
echo k
If compiled with --mm:refc, it'd cause a segmentation fault (at the runtime). So it must be protected with a lock.
{.passC: "-fopenmp", passL: "-fopenmp".}
import locks
var lock: Lock
lock.initLock()
for k in 1 || 10:
lock.acquire()
echo k
lock.release()
And if the lock is added, it causes no error, no fault, even works without --threads:on, but if --threads is turned on, it'd cause a segmentation fault, which is very interesting.
Forthermore, if compiled with --mm:orc, the backend C compiler will output
error: use of undeclared label 'BeforeRet_'
if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;
^
1 error generated.
and stop compiling. (The same applies to arc.)
However, if compiled with --mm:orc --exceptions:setjmp, it'd pass the compilation, then cause a segmentation fault at the runtime, even if protected with exclusive locks.
I was about to open an issue, but I saw this, which stopped me (because it seems to be a known issue) and caused me to use --exceptions:setjmp. Thus I discovered that if the 2nd version is compiled with --mm:orc --threads:on --exceptions:setjmp, it causes no error, no fault, even works without a lock. (That is, the 1st version can be compiled with this command, not causing a fault at runtime, but the output can be strange, as echo is not an atomic operation.)
My question is, why couldn't me find a documentation about this? The documentation of || did mention that it has some problems with GC, but not the C code compilation error and the workarounds.
Untested but I would do it like this:
{.passC: "-fopenmp", passL: "-fopenmp".}
proc myecho(s: int) {.raises: [].} =
try:
echo s
except:
discard
for k in 1 || 10:
myecho k
I got the same result.
error: use of undeclared label 'BeforeRet_'
if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;
^
1 error generated.
It seems that ARC/ORC will check the error flag, even if nothing to raise.
However, the compilation of C code does not fail if the memory strategy is not ARC/ORC, or if -fopenmp is not passed to the C compiler, or if --exceptions is not goto. So I think the codegen was correct, but with OpenMP, it cannot jump out of the for block. The BeforeRet_ does exist, but not in the for block, so it cannot be reached.
If you echo in a thread, you use Nim string. Nim strings are allocated with Nim GC which does not exist within an OpenMP context except for the master thread.
So either you don't allocate string, sequence or ref object or you initialize the GC in those threads with: https://github.com/numforge/laser/blob/e23b5d6/laser/openmp.nim#L88-L110
template attachGC*(): untyped =
## If you are allocating reference types, sequences or strings
## in a parallel section, you need to attach and detach
## a GC for each thread. Those should be thread-local temporaries.
##
## This attaches the GC.
##
## Note: this creates too strange error messages
## when --threads is not on: https://github.com/nim-lang/Nim/issues/9489
if(omp_get_thread_num()!=0):
setupForeignThreadGc()
template detachGC*(): untyped =
## If you are allocating reference types, sequences or strings
## in a parallel section, you need to attach and detach
## a GC for each thread. Those should be thread-local temporaries.
##
## This detaches the GC.
##
## Note: this creates too strange error messages
## when --threads is not on: https://github.com/nim-lang/Nim/issues/9489
if(omp_get_thread_num()!=0):
teardownForeignThreadGc()
Alternatively use --gc:arc or --gc:orc