In short, no declaration of non-primitives global variable. There seems
something declared at module level in blip that you didn't show.
When a proc is annotated with gcsafe, any proc called inside the proc has to be
gcsafe also, regardless of whether you are compiling with --threads. And if any
proc marked as gcsafe ever touched a module level non-primitive variable. An
error will throw. The first proc annotated with gcsafe here is the callback
function in serve().
Other people on this forum will probably explain better me. But here my 3-yo
attempt, if a proc is annotated with {.gcsafe.}, the proc is supposed to be run
in multi-threaded environment. And in current nim memory management model, each
thread has its own gc and they work on their own, no knowledge of each other.
This raises a problem: which gc should deal with the shared memory allocation
and freeing? Nim current answer is to avoid the problem at its first place: not
allow existence of non-primitives global variable touched at compile time.
gcsafe was one of the most annoying in my nim-experience... I had a long time
didn't know why my single threaded web server has to be complained with
non-gcsafe. Generally speaking, avoid global variable at all, this make testing
easier and common practice also.