A stable portable threading implementation

Started by
7 comments, last by Jason Z 9 years, 6 months ago

I have finally taken up implementing a basic job scheduler in my engine and it would seem getting reliable information on various libraries is either difficult or doesn't really exist. The documentation is there, but Googling turns up a lot of quirks and limitations.

Let me start by saying that I do not want to use Boost - I don't have Boost as a dependency and I do not wish to introduce it as such.

The other options on the table seem to be using native threads, pthreads or std::thread. Of the three std::thread seems to be the most appealing option, especially since I'm already on VS2013. Then again, two-year-old discussions seem to suggest std::thread either used to have or still has portability issues, especially on Win64 (although I'm compiling to 32 bits, so that shouldn't affect me) and MacOS, and compatibility/support problems with compilers for UNIX-based systems. Have these been resolved?

I'm going to write a lightweight wrapper for my threading code anyway so branching wouldn't really an issue, but I'm still not particularly fond of the idea.

In short, barring the use of Boost, what would be the most lightweight and simple solution?

Advertisement

In short, barring the use of Boost, what would be the most lightweight and simple solution?

1) std::thread

2) pthreads (standard almost everywhere, and a thin wrapper library on Windows)

3) just wrapping the OS-specific libraries yourself as needed.

I actually do #3 myself, because it's really not much code.

Short and to the point as usual, Hodgman. Much appreciated.

Wrapping OS-specific APIs as needed isn't as bad as it sounds. While it is somewhat reinventing the wheel seeing how std::thread already exists, you get exactly what you want. And, it's something you can actually do in a very acceptable amount of time.

Obviously, it depends a lot what you want and what you like. Neither phthreads nor std::thread which borrows a lot of "weird stuff" from pthreads (like condition variables) is stuff I like, personally. But it may just be what's perfect for you.

std::thread is nice insofar as it's standardized (like the weird stuff or not, but in any case it's well-defined what is to happen, and that'll be the same on every platform).

Still, I prefer the OS-specific wrapper. What you basically need is a facility to create threads, a mutex, and something that blocks until you signal it (so basically a Win32 event or a keyed event or a futex if you want to get funky). Everything else that you may need can be built with that, but 99% of the time you really don't need anything more than "something that blocks and that you can signal" if you use a job scheduler.

What's somewhat annoying is that if you want your own OS-specific wrapper to run under Linux as well, you have no way of avoiding pthreads alltogether even though you can do everything apart from actually creating threads without pthreads -- yes you can create threads with clone, but they don't work properly. Unluckily clone does not set up errno when you create a thread, and there is no documented way of doing this manually (none that I could find anyway!), thus calling any glibc function which modifies errno (so... pretty much every function that could possibly fail) will access a thread-local variable that isn't initialized. Which is... big fun. Maybe, hopefully, they'll fix that some day (but that's unlikely, I can already figure what the reply would be if you filed a bug report: "Well, you're not meant to use clone anyway, use pthread_create...").

Being able to start threads is like 0.5% of the work of doing useful parallelism.

I recommend Intel Threaded Building Blocks or Microsoft Parallel Programming Library (which _is_ portable!) as they help you solve the actually hard stuff.

Sean Middleditch – Game Systems Engineer – Join my team!

Being able to start threads is like 0.5% of the work of doing useful parallelism.

I recommend Intel Threaded Building Blocks or Microsoft Parallel Programming Library (which _is_ portable!) as they help you solve the actually hard stuff.

TBB looks really interesting. As it is, I've already started work on a work stealing implementation and seeing as I've chewed (or in a way forced myself to gnaw) through quite a few of the intricacies that come with writing the core aspects of an engine, I'll first try to see this through on my own. It's not that I don't want something that's proven and reliable - rather, this is all a learning exercise for me and I just want to see if I can do it. I'm stubborn, but I do have a purpose. Cheers for the references, though!

I've settled with std::thread for now and will try to stick with it unless it starts acting up.

The only major problem I have with std::thread is that it doesn't have a way to set a thread name for debugging purposes. You can layer the per-platform code for that over std::thread, though.

Sean Middleditch – Game Systems Engineer – Join my team!

std::thread has condition variables?

Yep, see the <condition_variable> header.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Being able to start threads is like 0.5% of the work of doing useful parallelism.

I recommend Intel Threaded Building Blocks or Microsoft Parallel Programming Library (which _is_ portable!) as they help you solve the actually hard stuff.

I think this is a pretty insightful comment - the machinery of making threads is one thing, but being able to make good use of them is of course much harder. Under the assumption that you are aware of the myriad issues with multithreaded program design, the higher level libraries that Sean mentions here will help you considerably in safely utilizing more of your available parallelism. It is at least worth checking the docs on those libraries to see how they handle certain situations, and then use that to inform your own design if you go that route.

This topic is closed to new replies.

Advertisement