FYI: C++20 std:jthread vs Julia's threads

I notices Julia’s GC relies on C++ code. I though the GC was written in C, and only the LLVM and parts related to it written in C++, but at least by now Julia’s GC relies on C++11, i.e. on std:thread. All threads seemingly rely on it (if I recall from C++11), and since the GC is now multi-threaded it too.

Then I got curious and looked into threads in C++, and since this jthread is rather new wrapper on thread, I’m curious what people did before C++20, how much better this new thing is, people use boost or something? Same for C people, what do they do? Should Julia’s threads rely on std::jthread (would it be a simple change… adding j?), thus C++20, not just/mostly for the GC (actually if might not need it…).

In C one simply used pthreads directly. I’ve written a couple of threaded R-packages. R did not support threading (due in part to reliance on addressing relative to the base of the stack), so it had to be completely hidden in C/C++. So it was pthreads, but ifdef’ed for native threads on windows. It’s not very hard. You just write a function to run in the thread, and launch it with pthread_create.

1 Like

The GC is still written in C and we don’t use std::thread we use LibUV as a shallow portability layer over pthreads

1 Like

TL;DR Thanks for [un]clarifying. I believe we’re on the same page, the GC is “C” code, as I thought and still think, only depending on C++ code; thus requiring a C++ compiler (so in some strict technical sense C++ code*), what I had in mind with it “being” C++ code (i.e. it, e.g. gc.c is C code, non-OOP, only depends on templated C++ code, jl_atomic_* functions). [I assume I was incorrectly implicating std::threads (for the GC or any Julia code) as opposed to C++ std::atomic_fetch_add. Both are concurrency-related, so linked in my mind.]

It all started with me looking at this, and pros and cons of that new merged PR vs old code (it’s faster, I believe no, or less(?), possible lock contention; for lines excluding 1541, that changed line is still unclear to me) and how the GC is implemented:

I believe julia_atomics.h is a C++ file (a so-called header-only file, if it’s not C++ then all my argument falls apart, see below*), and thus all code, any .c (not just .cpp) files, that depends on it, are in effect C++ code, requiring a C++ compiler.*

Files such as: ccall.cpp (and iddict.c, engine.cpp, julia_atomics.h, smallintset.c precompileutils.c).

I.e. it came as a surprise to me that when you use the ccall keyword, to call C code (not e.g. C++, Fortran or Rust), then C++ is still involved, i.e. ccall.cpp; and atomics (why?).

julia_threads.h depends on julia_atomics.h, and yes pthread.h (except on Windows).

I can’t clearly see that std::thread is actually used (probably isn’t), was a wrong assumption on my part, just based on seeing C++ atomics.

My question still remains, what are the pros and cons for using C++11 std::threads (for Julia) vs C++20 std::jthreads vs pthread.h (or something equivalent on Windows).

I’m not trying to argue, just clarify for myself (and others). Everything you can do in C++, C++11 (or C++20) you can do in C, in some equivalent way, at least for atomics (I’m thinking semantics, not arguing pros and cons of C++ vs C for code maintenance).

* Is code requiring a C++ compiler C++ code? If it’s C code depending on C++ code (not dynamically linked). C++ is a (non-strict) superset of C, so most C code can be called C++ code. It may be unfair if it’s actually only (non-OOP) C code. If Julia code depends on e.g. C or C++ code, then we do not call it C/C++ code, just because the code depends on a JLL, but we (at least I) do not call it pure Julia code. That has never been a huge concern of mine, we should depend on good available (at least proven correct, e.g. Rust) libraries regardless of language.

julia_atomics.h is actually conditionally compiled

so what does it actually mean, is it also compiled by a C compiler? I.e. triggering lines 37 and 38, such as for the GC? Then that seems to be an implementation detail, I still see:

julia_atomics.h is a polymorph, it provides functionality for both C and C++ components.

If it is included from C++ it’s uses different code paths julia/src/julia_atomics.h at f2558c461c85be4220901f4c67cbce718ddc015b · JuliaLang/julia · GitHub

Every file that uses .c is compiled with a C compiler:

We are using stdatomic because the C/C++ memory model is pretty great.

We are not using std::threads since it doesn’t really help us as a layer of abstraction over OS threads.

We use LibUV for threads.