On Saturday, February 13, 2021 at 4:21:34 PM UTC+1 Ian Lance Taylor wrote:
> > > > I was naively assuming that the unloading issue is easy to tackle when > the runtime is not shared between .so. Is it true ? > > No. The current Go runtime has no ability to shut down all > goroutines. If any goroutines are left running, and the plugin is > unloaded, the program will crash. > > I was not assuming a synchronous uncooperative way for the runtime to kill a running goroutine. I was more thinking about some cooperative strategy. Like a running go routine could check sometimes (maybe in the function prologue, or when doing an allocation) that it is time to die. Anycase, that could be also a constraint of the business code in the plugin. Like in C, they are not supposed to be actively running when the .so is *dlclosed*. In fact, what happens in practice today if we *dlclose* a plugin that has no running goroutine (and also no cgo invocations). Does it "*work*" somehow ? Are we leaking threads, waiting on some dead semaphore or something ? > > A last technical question, do you link the shared object with the ld > flag `- z nodelete` (which turn dlclose into no op). > > As far as I know we do not. > > This `ld` flag makes me think of another approach. Maybe it is ok to just turn off the effective unloading of the .so (with that ld flag). So *dlclose* is no-op. But if the business code takes care of cleaning most of its global state, maybe the gc has the opportunity to return some memory back to the OS (well, since the gc is not moving stuff, maybe the hope is slim, I don't know). That approach does not solve the problem of the developer iteratively working on a plugin. Apart from that, this looks like an acceptable compromise for someone who wants to ship an loadable plugin that can be " *unloaded*". At least it *works* somehow, leaking a bit of memory. But it does not crash. Of course, it is expected that if I load *libfoo.so* unload it, and load it again, everything works. I don't see why it would not. The second *dlopen* is presumably no-op. Now about the unique runtime constraint. In the previous message, you said that we might be able to run several runtimes in the same process (private runtime symbols, ...) If that works, it would mean that if the runtime symbols are public, but *versioned*, then maybe the runtime constraint is gone, isn't it? If two lib *libfoo.so* and *libbar.so* are built with different runtimes, we get two instantiated runtime in the binary that loads them simultaneously. Well, too bad, we lose a bit of memory, but at least it works. It looks acceptable to me. In the favorable case, they share the same runtime and every one is happy. I am curious about what happens on MacOS today. Because my understanding of their *two level namespace *(https://developer.apple.com/library/archive/documentation/Porting/Conceptual/PortingUnix/compiling/compiling.html#//apple_ref/doc/uid/TP40002850-BCIHJBBF) implies that you instantiate a new runtime whenever you load a c-shared .dylib. I'm not sure at all. It seems there is a way to force a flat namespace (it does not seem to be the default, thought). I haven't verified that yet. By the way, what the most efficient way to detect if two runtime are being instantiated ? Something like a debug log that is supposed to happen once, or a similar trick. Thanks Fred -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/edc90dd0-9f6d-4fbb-8629-db52b11ede42n%40googlegroups.com.