Initializing D runtime and executing module and TLS ctors for D libraries

Ali Çehreli acehreli at yahoo.com
Sun Jan 24 00:24:55 UTC 2021


tl;dr I know enough to sense there are important stuff that I don't know.

Even though I sometimes act[1] like someone who knows stuff, there are 
many fuzzy areas for me especially in the runtime.

Things work great when D code is inside a D program. The runtime and 
module states are magically initialized and everything works. It is not 
clear when it comes to writing a D library and especially when that 
library may be used by other language runtimes, necessarily on foreign 
threads.

Here are the essential points that I do and don't understand.

- Initialize the runtime: This is automatically done for a D program as 
described on the wiki[2]. This must be done by calling rt_init[3] for a 
D shared library. I handle this by calling rt_init from a 
pragma(crt_constructor) function[4]. Luckily, this is easy and works for 
all cases that I have.

- Execute module constructors ("ctor" for short, i.e. 'shared static 
this' blocks). This is done automatically for a D program and when the D 
library is loaded by other language code like C++ and Python. However, 
I've encountered a case[5] where module ctors were not being called. 
This could be due to runtime bugs or something that I don't understand 
with loading shared libraries. (My workaround is very involved: I grep 
the output of 'nm' to determine the symbol for the module ctor, call it 
after dlsym'ing, and because 'nm | grep' is a slow process, I cache this 
information in a file along with the ~2K libraries that I may load 
conditionally.)

- Loading D libraries from D code: I call loadLibrary[6] to load a D 
library so that "[its] D runtime [...] will be integrated with the 
current runtime". Sounds promising; assuming that rt_init is already 
called for the calling library, I assume loadLibrary will handle 
everything, and all code will use a single runtime and things will work 
fine. This works flawlessly for my D and C++ programs that load my D 
library that loads the other D libraries.

- Attaching foreign threads: D runtime needs to know about all threads 
that are running D code so that it will know what threads consist of 
"the world" for it to "stop the world" when performing garbage 
collection. The function to do this is thread_attachThis[7].

One question I have is, does rt_init already do thread_attachThis? I ask 
because I have a library that is loaded by Python and things work even 
*without* calling thread_attachThis.

- Execute thread local storage (TLS) ctors: Again, this happens 
automatically for most cases. However, thread_attachThis says "[if] full 
functionality as a D thread is desired, [rt_moduleTlsCtor] must be 
called after thread_attachThis". Ok. When would I not want "full 
functionality" anyway?

Another question: Are TLS ctors executed when I do loadLibrary?

And when they are executed, which modules are involved? The module that 
is calling rt_moduleTlsCtor or all modules? What are "all modules"?

- Detaching foreign threads: Probably even more important than 
thread_attachThis is thread_detachThis[8]. As its documentation says, 
one should call rt_moduleTlsDtor as well for "full functionality".

This is very important because when the GC collection kick in, it will 
stop all threads that makes up its world. If one of those threads has 
already been terminated, we will crash. (Related, I have an abandoned 
PR[9] that tried to fix issues with thread_detachThis, which stalled due 
to failing unit tests for the 32-bit Apple operating system, which D 
stopped supporting since then.) (And I stopped working on that issue 
mostly because the company I used to work for stopped using D and 
rewrote their library in C++.)

I have questions regarding thread_attachThis and thread_detachThis: When 
should they be called? Should the library expose a function that the 
users must call from *each thread* that they will be using? This may not 
be easy because a user may not know what thread they are running on. For 
example, the user of our library may be on a framework where threads may 
come and go, where the user may not have an opportunity to call 
thread_detachThis when a thread goes away. For example, the user may 
provide callback functions (which call us) to a framework that is 
running on a thread pool.

For that reason, my belief has been to call thread_attachThis upon 
entering an API function and calling thread_detachThis upon leaving it 
because I may not know whether this thread will survive or die soon. 
(thread_detachThis is so important because the next GC cycle will try to 
stop this thread and may crash.)

More questions: Can I thread_detachThis the thread that called rt_init? 
Can I call rt_moduleTlsCtor more than once? I guess it depends on each 
module. It will be troubling if a TLS ctor reinitializes an module state. :/

While trying to sort all of these out, I am facing a bug[10], which will 
force me to move away from std.parallelism and perhaps use 
std.concurrency. Even though that bug is reported for OS X, I think both 
that case and my "called from Python" case are related to an undefined 
behavior in thread management of runtime, which is exposed by 
std.parallelism. (?)

As you can see, even though I can list many references to act like I 
know stuff, I really don't and have many questions. :) The trouble is, 
when there are so many dimensions to test to be sure, it is extremely 
difficult to learn when a seg-fault bug is intermixed with all this, 
which hits sporadically. :(

I want to learn.

Thank you,
Ali

[1] https://www.youtube.com/watch?v=FNL-CPX4EuM

[2] https://wiki.dlang.org/Runtime_internals

[3] https://dlang.org/library/core/runtime/rt_init.html

[4] https://dlang.org/spec/pragma.html#crtctor

[5] https://forum.dlang.org/thread/rucm30$1lgk$1@digitalmars.com

[6] https://dlang.org/library/core/runtime/runtime.load_library.html

[7] https://dlang.org/library/core/thread/osthread/thread_attach_this.html

[8] https://dlang.org/library/core/thread/threadbase/thread_detach_this.html

[9] https://github.com/dlang/druntime/pull/1989

[10] https://issues.dlang.org/show_bug.cgi?id=11736


More information about the Digitalmars-d-learn mailing list