Thanks for your answer, it already give me a lot of what I was looking for.
I will try to give more context below.
The first thing to be aware of is: who creates the thread for the callback?
To summarize, the thread are created externally from crystal, and then callbacks are called concurrently.
Libfuse is a C library + linux kernel module that enable to write user space filesystem. To do so a program, call libfuse providing a struct that provide FUSE a set of callbacks the kernel can call to run the said filesystem. So the callback provided have to mimic open, readir, fstat and so on.
So in my case I am implementing the callback in crystal, and use c-binding to call libfuse and do the binding.
The libfuse C function I call to register the callback, become the main execution and (nearly) never return. The callback are called by thread created by the kernel/libfuse (I don’t know which exactly) when needed to operate the registered filesystem. Two different process accessing the same mount point, will likely use two different thread, and so call the same set of callbacks but in two different threads. There are options to constrain libfuse to use only one thread, which is fine for debugging or prototyping (like I do), but not a target on its own.
The second one is: do you need IO / Channels / Fiber context switch in the callback?
As the goal is to implement the various operation needed for a filesytem, callbacks need to do IO, but do not necessary do use Channels or Fiber themselve.
For filesystem I am considering using sqlite and regular IO for the implementation of the callback. I need also some global hashmap (but this is another story).
The GC expects to be the one that creates them. That is what Thread.new does. The GC can be compiled and configured to accept registering additional threads that were not created by itself, but that is not the default.
The GC will stop all the thread to collect and from there it will travers the structures in the stack and the roots to know what to collect. So if a non-registered thread allocates things in the GC that are not reachable from a registered thread or roots then they will be collected.
That the kind of issue I was seeking when asking this question, so many thanks for sharing this.
Having this in mind, I am currently looking at fuse wrapper for Go and D, wondering what they to do manage that behavior, I am not so sure this is handled properly.