Applications can then instantiate their own threads and pass them to the library (i.e. dependency injection.)
So if you have 10 different concurrent libraries in an application, then you don't end up with 10 different threading paradigms or 10 different thread pools. This is a matter of application architecture -- libraries shouldn't bake in a specific policy.
...this applies not just to threading, but other things that are not in the domain of the library such as memory allocation strategy or communication mechanism.
Libraries should be small, focused and decoupled. This is the way to proper re-usability.
I suppose another rule should be that there libraries should encapsulate a sufficient amount of complexity to make their existence worthwhile.... unlike left-pad.
True, but they're more often used as a crutch where a select-style loop and/or state machine would be more suitable, leaving race conditions in their wake.
For many years now I have the rule that new code must not use fork() and I am living a happier life. It's a bad call and it has no use if you have good threading support in the language. There are many programming languages that do not support fork() andy more or heavily restrict the use.
This isn't an issue on non-UNIX OSes.
Hence why fork() is just kind-of implemented on their POSIX compatibility layers.
In UNIX it is only an issue, because IEEE doesn't want to set in stone in POSIX how threads, signals and processes should interact, due to the way threads were added to UNIX.
Then there is pipe() and probably some more similar ones.
For example, what happens to the interactions of signal handlers semantics and thread scheduling.
Or what happens to the threads when the process does a fork().
You mean this? http://pubs.opengroup.org/onlinepubs/009695399/functions/for...
A process shall be created with a single thread. If a multi-threaded process
calls fork(), the new process shall contain a replica of the calling thread
and its entire address space, possibly including the states of mutexes and
other resources.
Granted, it's SUSv3.Just to get a glimpse of what POSIX says and what each UNIX actually does.
EDIT: Not talking about you, rather in general.
POSIX defines a few mechanisms for IPC.
What is optional and what is mandatory in pthreads doesn't concern me as i don't even like pthreads. (will still read what you said, later)
CSP stile concurrency over pipes (be they named or not), shared memory and synchronizations over it. POSIX defines mechanisms that work as expected over UNIX platforms.
I wonder how many (standards compliant) programs work differently between linux, *bsd, solaris, etc.
Wrong, you can fork() and only the thread issuing the fork () call survives the fork(), for example.
There are quite a few other differences.
> POSIX defines mechanisms that work as expected over UNIX platforms.
Not really, there are a few semantic differences and corner cases specially outside the GNU/LINUX, *BSD ones.
Thinking about it, it makes perfect sense.
Threads are processes that share the same memory space. When a thread is created (via pthread_whatever() or clone()) it starts executing some code that is practically in a $RANDOM place. So how would one go about copying the state of a process that has threads ? Freeze all the threads and copy everything ? That way the thread, that is probably doing some job at the moment, would end up being cloned without it knowing. So you would get two processes (threads are processes) that are doing exactly the same thing. Until something external changes their (internal) state, that is.
Non-determinism should always be avoided if possible.
This still does not say that one couldn't write portable, threaded, UNIX compliant code. Nor that those undefined behaviors from the specs would be fixable by rewriting the spec (by adding atomic fork+exec, that seems to me to be the context of this whole thing).
When you are embedding a language like Go in a C program, its runtime becomes a library. But that's the example the proves the rule: there are (or were) well-known problems with that. Libraries shouldn't be doing GC.
Boehm GC disagrees.
The thing is that any thread you launch is a big piece of machinery and your attempt to hide it is inevitably going to be a leaky abstraction.
That's not true at all. Threads aren't that complicated, and there's lots of ways of using them that aren't complicated and don't leak anything. As a trivial example, if I want to process a bunch of data, it might be faster to process it in parallel, and I can use a thread to do that without causing any observable difference in behavior (beyond potentially turning a single-threaded program into a multithreaded one). And if I'm on a platform like OS X or iOS, I can use higher-level threading APIs like libdispatch that make it even simpler.
And your example of a big calculation in a library is particurly bad, because the user can threadify that herself if she wants to.
#include <thread.h>
#include <yourlib.h>
#include "my_eventloop.h"
void launch_da_ting()
{
std::thread ting_doer([]{
Result rez = yourlib::do_da_ting();
my_event_loop::post([rez](){get_rez(rez);});
});
ting_doer.detach();
}
Now it's true those five lines would become a single function call if your library forced the user to user a particular dispatching library. But I think that would be poor seperation of concerns.And no, the user can't threadify that themselves. Your sample code is putting the library call on a thread. That's not at all what I was talking about. I was talking about a library using multiple threads (or, more likely, some abstraction over a thread pool that handles scheduling chunks of work on threads for you, such as libdispatch on Apple platforms) in order to process a bunch of data in parallel, data that only the library knows about. The user makes a single call into the library, the library processes a bunch of data in parallel, and then returns to the user. From the user's perspective it's a synchronous call, but it runs in a fraction of the time it would if the library wasn't allowed to use threading internally.
That't not what makes a framework. It's all about inversion of control, you call a library and a framework calls you.
http://stackoverflow.com/questions/3057526/framework-vs-tool...
I might use it to start out an application, since it requires very little code for dumb parallelism. But if I were making a real library, I would try to abstract the concurrency policy away so the user can control it.
This should be trivial by layering the library. Just provide a bunch of single-threaded functions, and then some OpenMP wrappers. Then user can call the single-threaded functions if she wants to do something different.
What I don't want is OpenMP baked deeply into the program logic. I think it shares the problem I was talking about. If you have 10 different libraries using OpenMP in an app, and the app is ITSELF multi-threaded, it seems like you have a mess of threads that you have no control over.