How to initialize threadlocal variables without adding initialization code to every thread?

I keep forgetting to initialize my threadlocal variables when creating a new thread (in my particular case that it’s an allocator and a seed, both stored in my main.zig).
Now I recently stumbled over the DllMain on windows which gets executed when a thread is created or destroyed. This would be exactly what I need, but I need to target Windows, Linux and mac.

Now I wonder is there already cross-platform solution for this in standard library somewhere or would I need to implement it for every target? Does anyone know of an example in Zig that I could look at?

1 Like

I’m very hazy on everything Windows, but I don’t think this is quite correct? DllMain is a user function which is called “whenever a process or thread loads or unloads the DLL”, rather than being thread-specific.

I have a feeling you could use @chung-leong 's new library to wrap your thread functions with a consistent preamble and cleanup, if that’s what you’re going for.

The problem is not the code itself, the problem is that it’s easy to forget, and furthermore I have threads that are created outside of Zig (from an audio library).

1 Like

Fair. That’s the kind of problem I would solve with a wrapped closure in a language which isn’t Zig, and it might be worth going through the extra effort to emulate that in Zig as well. Doesn’t do anything for rogue threads coming in from C land though.

I don’t believe the mechanism you’re looking for exists, unfortunately. pthread predates the use of hooks as a matter of course, that’s the only thread interface other than Zig’s I have any background in, but I can tell you that it doesn’t have slots to stick an initializer or a destructor.

This sounds like the basis of a very nice enhanced thread library for the language, but again, that doesn’t help you with the threads you’re receiving.

But how do languages like C++ (with RAII) initialize and free threadlocal variables? There must be some mechanism that does this, or do they just resort to lazy initialization?

I can’t answer that in as much detail as I’d like, but the approximation is that they do it with RAII. A feature of the language / runtime, not an operating system service.

It’s possible that there are some C++ hooks to be discovered which could be pressed into service here. That’s way outside my wheelhouse, though.

1 Like

The loader handles thread local storage allocation and initialization.
For windows PE .tls section is used.
For linux ELF sections .tdata and .tbss are used. (See: ELF Handling For Thread-Local Storage PDF)

You can rely on the thread local storage initialization values to detect if you have a new thread before accessing any threadlocal variable.

3 Likes

Thank you, I guess that means lazy initialization would be the safest option, and I don’t want to do that.

So I will probably just keep doing what I do now, except I introduced a helper function so I can be sure that if I initialize things, that everything is initialized. Not great, but at least I usually get a runtime error soon enough because of the thread_local allocator.

2 Likes

DLLMain called also for every thread creation and deletion
Usually we disabled these calls via DisableThreadLibraryCalls function

Also we tried to minimize TLS usage

I am not sure that usage of TLS in Zig is good idea

I am not sure that usage of TLS in Zig is good idea

My main use-case is a threadlocal allocator for fast local allocations. This is really nice for small lists and stuff, and the alternative would be to pass this to every function, which would be rather tedious and also takes more resources than the threadlocal storage.

1 Like

how do you achieve code portability?

Zig idiom is to pass an allocator “interface” and you can implement your own allocator for tls simulation

Not sure what you are implying here. I haven’t had any trouble with zig’s threadlocals so far, and my game runs fine on windows, linux and macos* (arm).

1 Like

Games programming often leans into idioms which are against ‘best practices’ in most other domains. I wouldn’t worry about it.

1 Like

are you talking about
Example of threadlocals usage ?

yes, exactly