Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std::thread...too little, too late?

311 views
Skip to first unread message

me

unread,
Dec 18, 2014, 6:36:11 AM12/18/14
to
I was looking at what made its way into the C++11 standard in the way of
threads, and it seems very braindead. I mean third party libraries have
been giving us working threading for years, and "THAT"was all they came
up with for the standard?

I fully understand the implications of adding features that need to be
portable and that give a good least common denominator, but std::thread
seems too little, too late.

I thought it would be easy to at least build upon the std::thread class
and to something that more closely resembles the common threading utility
available in a variety of libraries: something with delayed thread
starting, and the ability to make the process OO with a virtual run()
method...no such luck.

Consider the following example that uses std::thread as a class trait and
then subclasses a UsefulThread class, adding the requested functionality:



class UsefulThread {
thread* stdThread;
public:
UsefulThread();
virtual ~UsefulThread() { delete stdThread; }
virtual void run()=0;
void start() { stdThread=new thread(run); }
void join() { stdThread->join(); }
};


class WorkerThread: public UsefulThread {
int _id;
BufferObject& r;
public:
WorkerThread(int id, BufferObject& b): UsefulThread(), _id(id), r(b)
{}
virtual void run() { for (auto i=0; i<_id; i++) r << _id; }
// share r object is made threadsafe with an internal mutex
};

int main(int argc, char** argv) {
using namespace std;
list<int> v ={1, 2, 3, 4, 5, 6, 7, 8, 9};
BufferObject b;
list<WorkerThread> threadList;
for (auto i: v) { WorkerThread t(i, b); threadList.push_back(t); };
for (auto i: threadList) i.start();
for (auto i: threadList) i.join();

cout << b.str() << endl;

return 0;
}

There is no way (that I can tell of) to make the UsefulThread::start()
method properly register the run() virtual. There was an article online
at <http://rafalcieslak.wordpress.com/2014/05/16/c11-stdthreads-managed-
by-a-designated-class/> where the author tries to address the delayed
start issue. Problem is that 1) he doesn't allow for a virtual run()
method, and 2) when using a normal thread object instead of a thread* the
resulting WorkerThread cannot be put into any sort of std container, and
gives compile errors about (use of deleted function) from the constructor.




Öö Tiib

unread,
Dec 18, 2014, 12:39:29 PM12/18/14
to
On Thursday, 18 December 2014 13:36:11 UTC+2, me wrote:
> I was looking at what made its way into the C++11 standard in the way of
> threads, and it seems very braindead.

Hey, brain-alive, where were you, what woke you up?! 2014 is ending
soon. std::thread is mostly based on Boost.Thread that has been around
since 2001.

Christopher Pisz

unread,
Dec 18, 2014, 2:21:39 PM12/18/14
to
I am not sure what the beef is. Are you trying to make a virtual Run
method that derived classes can implement, but can't?

I am sure I've successfully done that in the past as well as maintain a
pool of threads in a base class via boost thread, so I don't see why it
would be a problem with std::thread which came from boost thread.





Paavo Helde

unread,
Dec 18, 2014, 2:35:40 PM12/18/14
to
This example mostly demonstrates antipatterns and is probably inspired by
some Java-originating ideas that everything must be OO and derived from
some common base class.

I have not used std::thread but I gather it is similar to boost::thread
so I use that instead as a reference.

The data in the WorkerThread object remains in shared use so the access
to it must be protected. With boost::thread the functor object will be
copied over to another thread before the actual thread start, ensuring
that the user does not have to worry about how to get the data safely
into the new thread.

The delayed start() method complicates the usage and implementation and
is not needed for anything. If the thread needs to wait before performing
any actual tasks it could just wait on some condition in the start of the
thread function.

The run() function is called only once when the thread is created. At the
thread creation time the exact class which is created is of course known
so there is no need to have run() as a virtual function. In the above
example it is virtual only because somebody wanted to call it from a
useless non-template base class.

> when using a normal thread object instead of a thread*
> the resulting WorkerThread cannot be put into any sort of std
> container, and gives compile errors about (use of deleted function)
> from the constructor.

This is because copying of threads does not make sense (should it spawn a
new thread or what?), and all STL containers are based on value copying.
Use some pointers or smartpointers instead for holding the thread objects
in a container.

So the idea behind boost::thread and std::thread is to provide the most
simple building block, without any unnecessary kludges like forcing use
of virtual functions and without forcing use of shared data. It can be
used quite easily in simple scenarios. If a more complex scenario is
needed, one can build on top of it (like WorkerThread does, albeit in ill
fashion). For example, to avoid functor copying and keep data in shared
use one can define a simple template (probably boost has it already
defined by some other name) which can be used for all such threads:

template<typename T>
struct FunctorForwarder {
T& p_;
FunctorForwarder(T& p): p_(p) {}
void operator()() {p_();}
};

class TimerMgr {
public:
TimerMgr():
thread_(FunctorForwarder<TimerMgr>(*this))
{}
void operator()() {
// the thread function
}
// ...
private:
boost::thread thread_;
// ... shared data
};

hth
Paavo

Mr Flibble

unread,
Dec 18, 2014, 4:05:41 PM12/18/14
to
Stop fucking moaning mate.

void start() { stdThread=new thread(std::bind(&UsefulThread::run, this)); }

/Flibble

me

unread,
Dec 18, 2014, 5:11:12 PM12/18/14
to
Well, here's you're answer. I've religiously stayed away from anything
boost. The times I've had to use it I encountered poor documentation and
too frequently changing interface semantics. Other third party platforms
accomplished more with less effort. commoncpp and trolltech-qt had much
better features.

Just thought I'd take a look at the c++11 std to see what made its way in
and got a good chuckle over it.

Asbestos firesuit is now on. Flame at will! LOL

Christopher Pisz

unread,
Dec 18, 2014, 5:47:30 PM12/18/14
to
On 12/18/2014 4:11 PM, me wrote:
> On Thu, 18 Dec 2014 09:39:17 -0800, Öö Tiib wrote:
>
>> On Thursday, 18 December 2014 13:36:11 UTC+2, me wrote:

> Well, here's you're answer. I've religiously stayed away from anything
> boost. The times I've had to use it I encountered poor documentation and
> too frequently changing interface semantics. Other third party platforms
> accomplished more with less effort.

Oh....oh my...what in the world?

me

unread,
Dec 18, 2014, 5:48:56 PM12/18/14
to
On Thu, 18 Dec 2014 11:36:01 +0000, me wrote:

> I was looking at what made its way into the C++11 standard in the way of
> threads, and it seems very braindead. I mean third party libraries have
> been giving us working threading for years, and "THAT"was all they came
> up with for the standard?
>

> There is no way (that I can tell of) to make the UsefulThread::start()
> method properly register the run() virtual. There was an article online
> at <http://rafalcieslak.wordpress.com/2014/05/16/c11-stdthreads-managed-
> by-a-designated-class/> where the author tries to address the delayed
> start issue. Problem is that 1) he doesn't allow for a virtual run()
> method, and 2) when using a normal thread object instead of a thread*
> the resulting WorkerThread cannot be put into any sort of std container,
> and gives compile errors about (use of deleted function) from the
> constructor.

Problem solved via a work-around. Doesn't seem to be a valid way to make
std::thread(v) recognize v when it is a pure virtual, implemented in a
subclass. The work-around is to register a stub-function and have that
function call the virtual...and the non-copyable nature of std::thread
also causes me grief (particularly in managing lists of threads or thread
pools). I understand the "why" but that doesn't mean I have to like it
or a gree with it.

Richard

unread,
Dec 18, 2014, 6:20:22 PM12/18/14
to
[Please do not mail me a copy of your followup]

me <no...@all.net> spake the secret code
<WhIkw.936435$Fo3.8...@fx09.iad> thusly:

>Well, here's you're answer. I've religiously stayed away from anything
>boost.

That's certainly your choice, but you're really missing out on the
single most important community that is influencing the future of C++.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Mr Flibble

unread,
Dec 18, 2014, 6:35:16 PM12/18/14
to
I gave you a solution in my other reply mate; stop fucking moaning and
talking shite.

/Flibble


Öö Tiib

unread,
Dec 18, 2014, 7:43:59 PM12/18/14
to
On Friday, 19 December 2014 00:11:12 UTC+2, me wrote:
> On Thu, 18 Dec 2014 09:39:17 -0800, Öö Tiib wrote:
>
> > On Thursday, 18 December 2014 13:36:11 UTC+2, me wrote:
> >> I was looking at what made its way into the C++11 standard in the way
> >> of threads, and it seems very braindead.
> >
> > Hey, brain-alive, where were you, what woke you up?! 2014 is ending
> > soon. std::thread is mostly based on Boost.Thread that has been around
> > since 2001.
>
>
> Well, here's you're answer. I've religiously stayed away from anything
> boost. The times I've had to use it I encountered poor documentation and
> too frequently changing interface semantics.

Sometimes I've been impressed and sometimes disappointed by some external
code used but I've never felt anything religious about anything.

Perhaps one of most impressive things from boost is Boost.ASIO, it is hard
to find decent alternative to it and it uses Boost.Thread quite intuitively
and tidily for my taste. It can work stable for months.

> Other third party platforms
> accomplished more with less effort. commoncpp and trolltech-qt had much
> better features.

Qt has also impressed me with some features (but it does not have anything
like Boost.ASIO). However it has lately started to behave odd with that
QML of it. If I leave it running (and communicating) for a night then
it now crashes somewhere within some of its "qgraphicsitems"s,
"qanimation"s and "qeventloops". Also its latest QtCreator crashes too
frequently ... feels that Qt has changed its owners too often and so is
currently going down.

Chris Vine

unread,
Dec 18, 2014, 8:16:22 PM12/18/14
to
On Thu, 18 Dec 2014 13:35:09 -0600
Paavo Helde <myfir...@osa.pri.ee> wrote:
> This is because copying of threads does not make sense (should it
> spawn a new thread or what?), and all STL containers are based on
> value copying. Use some pointers or smartpointers instead for holding
> the thread objects in a container.

That's not right. std::thread has a move constructor and move
assignment operator, suitable for C++11 containers. The problem is
that the OP doesn't understand move semantics - he noticed that the
copy constructor and copy assignment operator are deleted, but failed
to consider moving into the container.

Nor does he seem to have any programming experience or he would not
need to ask why thread objects should not be copiable (it is
semantically meaningless for threads).

> So the idea behind boost::thread and std::thread is to provide the
> most simple building block, without any unnecessary kludges like
> forcing use of virtual functions and without forcing use of shared
> data. It can be used quite easily in simple scenarios. If a more
> complex scenario is needed, one can build on top of it (like
> WorkerThread does, albeit in ill fashion). For example, to avoid
> functor copying and keep data in shared use one can define a simple
> template (probably boost has it already defined by some other name)
> which can be used for all such threads:

The idea of inheriting from a thread object and giving a thread object
a virtual run method to assist that is totally bizarre. What
polymorphic behaviour could a run method reasonably offer? A thread
object is a classical concrete type.

Presumably the original poster only has some (fairly limited) java
experience to draw on.

Chris

me

unread,
Dec 18, 2014, 8:25:12 PM12/18/14
to
On Thu, 18 Dec 2014 16:43:49 -0800, Öö Tiib wrote:


> Qt has also impressed me with some features (but it does not have
> anything like Boost.ASIO). However it has lately started to behave odd
> with that QML of it. If I leave it running (and communicating) for a
> night then it now crashes somewhere within some of its
> "qgraphicsitems"s, "qanimation"s and "qeventloops". Also its latest
> QtCreator crashes too frequently ... feels that Qt has changed its
> owners too often and so is currently going down.

Admittedly, I'm beginning to share those Qt concerns, but for rapid
application prototyping it's still a good framework to get something up
and running: usually a python back-end with a Qt front end...and if the
project has professional or commercial value then a translation from
python to C++.

Mr Flibble

unread,
Dec 19, 2014, 8:53:25 AM12/19/14
to
I think it makes perfect sense to derive from std::thread so there is
nothing intrinsically wrong with having a virtual "thread started" method.

/Flibble

Mr Flibble

unread,
Dec 19, 2014, 9:03:18 AM12/19/14
to
Actually I am talking bollocks - I forgot what my code-base actually
does: I have a thread class that *contains* a boost::thread object and
you can derive from this thread class instead.

/Flibble

Martijn Lievaart

unread,
Dec 19, 2014, 9:05:13 AM12/19/14
to
A thread object needs a start function, so making thread a virtual base
makes perfect sense to me. To call that a "thread started" function
instead of run() or operator()() seems a stretch though.

M4

Chris Vine

unread,
Dec 19, 2014, 5:14:33 PM12/19/14
to
On Fri, 19 Dec 2014 13:53:26 +0000
Mr Flibble <flibbleREM...@i42.co.uk> wrote:
[snip]
> I think it makes perfect sense to derive from std::thread so there is
> nothing intrinsically wrong with having a virtual "thread started"
> method.

So, what polymorphic behaviour do you think this "run" method described
by the OP might exhibit? A run method should just start the thread.
It is horrible 1990's over-OO design to use polymorphism just to
represent the function to be executed. All you need do is apply the
thread function/lambda/callable object passed to the thread object to
its arguments (if any), which is what std::thread does.

Chris

Chris Vine

unread,
Dec 19, 2014, 5:33:37 PM12/19/14
to
On Fri, 19 Dec 2014 15:04:21 +0100
Martijn Lievaart <m...@rtij.nl.invlalid> wrote:
> A thread object needs a start function, so making thread a virtual
> base makes perfect sense to me. To call that a "thread started"
> function instead of run() or operator()() seems a stretch though.

So let me ask you then what polymorphic behaviour this run() method
might exhibit? std::thread just takes a callable object with arguments
and applies the callable object to its arguments (if any) in a new
thread. To use polymorphism just to encapsulate the callable object is
OO-obsessive mis-design, in my view (and also, apparently, the standard
library developers).

A thread object does not need a start function, although there is some
advantage in having one (I have a thread class with a start function).
But all it need do is start the new thread.

Chris

me

unread,
Dec 20, 2014, 6:31:16 AM12/20/14
to
On Thu, 18 Dec 2014 21:05:42 +0000, Mr Flibble wrote:

>>
>> There is no way (that I can tell of) to make the UsefulThread::start()
>> method properly register the run() virtual. There was an article
>> online
>
> Stop fucking moaning mate.
>
> void start() { stdThread=new thread(std::bind(&UsefulThread::run,
> this)); }

Well, partial kudos anyway...std::bind() was helpful, but if run was a
virtual then it barfed. It works when run is a simple class method. The
workaround of having run() call a virtual works.

Mr Flibble

unread,
Dec 20, 2014, 8:42:32 AM12/20/14
to
That is like saying "It is horrible 1990's over-OO design to use virtual
functions." which is of course nonsense.

You seem to be ignoring my other reply: my virtual "task()" function is
actually in my thread class which *contains* a boost::thread object.

My start() method:

void thread::start()
{
lock();
if (started())
{
unlock();
throw thread_already_started();
}
try
{
iState = Starting;
if (!iUsingExistingThread)
{
thread_object_pointer newThread(new
boost::thread(boost::bind(&thread::entry_point, this)));
iThreadObject = newThread;
unlock();
}
else
{
unlock();
entry_point();
}
}
catch(const std::exception& aException)
{
iState = Error;
unlock();
std::cerr << std::string("Error starting thread due to exception
being thrown (") + aException.what() + ")." << std::endl;
throw;
}
catch(...)
{
iState = Error;
unlock();
std::cerr << std::string("Error starting thread due to exception of
unknown type being thrown.") << std::endl;
throw;
}
}

My entry_point() method which calls the pure virtual task() function:

void thread::entry_point()
{
lock();
iState = Started;
iId = boost::this_thread::get_id();
unlock();
try
{
task(); /* This is the pure virtual that requires overriding */
if (!iUsingExistingThread)
{
lock();
if (iState == Started)
iState = Finished;
unlock();
}
}
catch(const std::exception& aException)
{
std::cerr << std::string("Thread terminating due to an uncaught
exception was being thrown (") + aException.what() + ")." << std::endl;
throw;
}
catch(...)
{
std::cerr << "Thread terminating due to an uncaught exception of
unknown type being thrown." << std::endl;
throw;
}
}


What we are basically talking about here is the /template method
pattern/ and there is nothing wrong with it; it certainly is *not*
"horrible 1990's over-OO design"

/Flibble

Mr Flibble

unread,
Dec 20, 2014, 8:44:40 AM12/20/14
to
You are talking shite mate: you can use virtual functions with
std::bind. Are you a fucking annoying troll? You seem like one to me;
either that or you are a common all garden bullshitter.

/Flibble

Chris Vine

unread,
Dec 20, 2014, 5:17:33 PM12/20/14
to
On Sat, 20 Dec 2014 13:42:16 +0000
Mr Flibble <flibbleREM...@i42.co.uk> wrote:
> On 19/12/2014 22:14, Chris Vine wrote:
> > On Fri, 19 Dec 2014 13:53:26 +0000
> > Mr Flibble <flibbleREM...@i42.co.uk> wrote:
> > [snip]
> >> I think it makes perfect sense to derive from std::thread so there
> >> is nothing intrinsically wrong with having a virtual "thread
> >> started" method.
> >
> > So, what polymorphic behaviour do you think this "run" method
> > described by the OP might exhibit? A run method should just start
> > the thread. It is horrible 1990's over-OO design to use
> > polymorphism just to represent the function to be executed. All
> > you need do is apply the thread function/lambda/callable object
> > passed to the thread object to its arguments (if any), which is
> > what std::thread does.
>
> That is like saying "It is horrible 1990's over-OO design to use
> virtual functions." which is of course nonsense.

It is of course nonsense. Which is why that is not what I said.

> You seem to be ignoring my other reply: my virtual "task()" function
> is actually in my thread class which *contains* a boost::thread
> object.
[snip]
> What we are basically talking about here is the /template method
> pattern/ and there is nothing wrong with it; it certainly is *not*
> "horrible 1990's over-OO design"

A virtual function is over-design in this usage. If you want to store a
callable object you can keep a std::function object as member data
rather than force derivation just to achieve the same end. (And we are
talking about C++11 here, as the OP's criticisms of std::thread formed
the subject of the originating post.)

Chris

PS: As it happens, your particular implementation would not work with
the BSDs or linux (and possibly some of the commercial unixes) if thread
cancellation is in use, but that is a side issue. I think you program
for windows, which does not have meaningful (that is,
deferred/blockable) cancellation.

Christopher Pisz

unread,
Dec 21, 2014, 12:54:00 PM12/21/14
to
What is the alternative to Fibble's code?

It is often that I have some base class that I want to derive from and
concretely specify the work in the derived class.

For example I might have one class that consumes XML documents from
threadsafe queue, while another consumes JSON. I want both to run in
parallel to my main program and prepare "configuration" structs that I
can grab from them.

Naturally I would want to have a base class, a XML consumer class and a
JSON consumer class.

Are you saying that each derived class would bind its own parsing method
to a thread object contained in the base?


Mr Flibble

unread,
Dec 21, 2014, 1:04:46 PM12/21/14
to
On 20/12/2014 22:17, Chris Vine wrote:
> On Sat, 20 Dec 2014 13:42:16 +0000
> Mr Flibble <flibbleREM...@i42.co.uk> wrote:
>> On 19/12/2014 22:14, Chris Vine wrote:
>>> On Fri, 19 Dec 2014 13:53:26 +0000
>>> Mr Flibble <flibbleREM...@i42.co.uk> wrote:
>>> [snip]
>>>> I think it makes perfect sense to derive from std::thread so there
>>>> is nothing intrinsically wrong with having a virtual "thread
>>>> started" method.
>>>
>>> So, what polymorphic behaviour do you think this "run" method
>>> described by the OP might exhibit? A run method should just start
>>> the thread. It is horrible 1990's over-OO design to use
>>> polymorphism just to represent the function to be executed. All
>>> you need do is apply the thread function/lambda/callable object
>>> passed to the thread object to its arguments (if any), which is
>>> what std::thread does.
>>
>> That is like saying "It is horrible 1990's over-OO design to use
>> virtual functions." which is of course nonsense.
>
> It is of course nonsense. Which is why that is not what I said.

It is equivalent to what you said in this case.

>
>> You seem to be ignoring my other reply: my virtual "task()" function
>> is actually in my thread class which *contains* a boost::thread
>> object.
> [snip]
>> What we are basically talking about here is the /template method
>> pattern/ and there is nothing wrong with it; it certainly is *not*
>> "horrible 1990's over-OO design"
>
> A virtual function is over-design in this usage. If you want to store a
> callable object you can keep a std::function object as member data
> rather than force derivation just to achieve the same end. (And we are
> talking about C++11 here, as the OP's criticisms of std::thread formed
> the subject of the originating post.)

It is not over design at all mate; basically you are clueless.

>
> Chris
>
> PS: As it happens, your particular implementation would not work with
> the BSDs or linux (and possibly some of the commercial unixes) if thread
> cancellation is in use, but that is a side issue. I think you program
> for windows, which does not have meaningful (that is,
> deferred/blockable) cancellation.

As it happens, mate, there is nothing wrong with my code.

/Flibble

Chris M. Thomasson

unread,
Dec 21, 2014, 2:03:52 PM12/21/14
to
> "me" wrote in message news:B_ykw.832074$Ub6....@fx20.iad...

> I was looking at what made its way into the C++11 standard in the way of
> threads, and it seems very braindead. I mean third party libraries have
> been giving us working threading for years, and "THAT"was all they came
> up with for the standard?

[...]

AFAICT, it is not brain-dead. The only little quibble I have is that C++11
gives us a way to wait on lock-based algorithms via condvar, however
there is no stock method to wait on conditions generated by lock/wait-free
algorithms. Luckily, one can build something that works by using what's
there.

This can all be done portably now. FWIW, take a look at the following topic:

https://groups.google.com/forum/?hl=en#!topic/lock-free/X3fuuXknQF0

and:

https://groups.google.com/d/topic/lock-free/acjQ3-89abE/discussion


All of the algorithms there can be portably implemented in C++11.

that is VERY nice!

:^D

Chris Vine

unread,
Dec 21, 2014, 3:07:19 PM12/21/14
to
On Sun, 21 Dec 2014 18:04:35 +0000
Mr Flibble <fli...@i42.co.uk> wrote:
> It is not over design at all mate; basically you are clueless.

So you have a program which happens to have, say, 100 functions which
might be executed in their own threads at some point during execution
of the program. You want to have the class representing a thread to
have a separate start method, so you cannot use std::thread just by
itself. One approach is to have a single concrete thread class, where
each function/callable object in question is passed at object
construction time and stored in a std::function or similar object, which
is a data member of the class, and which is executed when start is
called.

Another approach is to have a virtual run method of a virtual base
thread class and force the programmer to write 100 derived classes,
each with their own version of the run method representing the function
in question to be executed by it (that is what your virtual 'task'
method did).

The first approach provides run time binding to a polymorphic function
object. The second provides compile time binding in the class
definition but with run time virtual method selection. I very much
prefer the first.

> As it happens, mate, there is nothing wrong with my code.

To adopt your favourite response, you're talking shite, mate. With
the BSDs and linux (and possibly commercial unixes), forced stack
unwinding on cancellation would cause the program to terminate when it
hits your catch-all clause. As I said, that is not a problem in
windows (your platform) because it does not have meaningful/usable
cancellation.

Chris

Chris Vine

unread,
Dec 21, 2014, 3:32:57 PM12/21/14
to
On Sun, 21 Dec 2014 11:53:43 -0600
Christopher Pisz <nos...@notanaddress.com> wrote:
> What is the alternative to Fibble's code?
>
> It is often that I have some base class that I want to derive from
> and concretely specify the work in the derived class.
>
> For example I might have one class that consumes XML documents from
> threadsafe queue, while another consumes JSON. I want both to run in
> parallel to my main program and prepare "configuration" structs that
> I can grab from them.
>
> Naturally I would want to have a base class, a XML consumer class and
> a JSON consumer class.
>
> Are you saying that each derived class would bind its own parsing
> method to a thread object contained in the base?

No, I am saying don't have a derived class at all. Each object of the
concrete class would have a parsing method (or any other function to be
run in a thread) bound to it at run time.

poco threads now have a sort of hybrid arrangement. You can now either
pass start a callable object, or you can pass it a runnable object
which has implemented a virtual run method. I don't see the point of
that.

Chris

Ian Collins

unread,
Dec 21, 2014, 3:42:18 PM12/21/14
to
Chris Vine wrote:
>
> poco threads now have a sort of hybrid arrangement. You can now either
> pass start a callable object, or you can pass it a runnable object
> which has implemented a virtual run method. I don't see the point of
> that.

Maybe it's because people are comfortable with that design?

--
Ian Collins

Mr Flibble

unread,
Dec 21, 2014, 3:49:19 PM12/21/14
to
You are still talking shite mate. My catch-all rethrows. If I wanted to
support thread cancellation I would throw a cancellation object; pthread
stack cancellation is UB as far as C++ is concerned so is off-topic.

/Flibble

Ian Collins

unread,
Dec 21, 2014, 4:09:19 PM12/21/14
to
How would you "throw a cancellation object"? That appears to be the
opposite of thread cancellation where the operation is initiated from
another thread.

--
Ian Collins

Chris Vine

unread,
Dec 21, 2014, 4:47:12 PM12/21/14
to
On Sun, 21 Dec 2014 20:49:08 +0000
Mr Flibble <fli...@i42.co.uk> wrote:
> You are still talking shite mate. My catch-all rethrows. If I wanted
> to support thread cancellation I would throw a cancellation object;
> pthread stack cancellation is UB as far as C++ is concerned so is
> off-topic.

On looking at your code again I see you are right on the rethrow point.
The problem you have with your code is not program termination, but that
cancellation would be logged as if it were an exception error, which is
an intractable problem with your design because in common
implementations cancellation objects have no type or name. All you can
do is rethrow them with a generic throw.

However you are still wrong about your 1990's-style 'runnable' virtual
base class, mate (IMO). In any event, C++11 has gone for the more
usable std::function concrete class for that kind of polymorphism.

Chris

Mr Flibble

unread,
Dec 21, 2014, 5:25:12 PM12/21/14
to
In my designs threads cancel themselves or can be told to cancel themselves.

/Flibble

Chris Vine

unread,
Dec 21, 2014, 5:25:54 PM12/21/14
to
You cannot do it portably across platforms (and Flibble may well just be
confused), but you can sort-of do what he suggests on POSIX platforms.
The system calls which represent cancellation points are by and large
the same as those that are interruptible by a signal. So you can set a
flag, unblock system calls in the target thread by sending a signal with
pthread_kill(), and get the target thread to inspect its flags on EINTR
and commit suicide by throwing its own exception object which is caught
in the entry-point callable.

I don't think this can be done on Flibble's platform though. And if
you cannot interrupt blocking system calls then it is pointless.

Chris

Mr Flibble

unread,
Dec 21, 2014, 5:28:36 PM12/21/14
to
On 21/12/2014 21:47, Chris Vine wrote:
> On Sun, 21 Dec 2014 20:49:08 +0000
> Mr Flibble <fli...@i42.co.uk> wrote:
>> You are still talking shite mate. My catch-all rethrows. If I wanted
>> to support thread cancellation I would throw a cancellation object;
>> pthread stack cancellation is UB as far as C++ is concerned so is
>> off-topic.
>
> On looking at your code again I see you are right on the rethrow point.
> The problem you have with your code is not program termination, but that
> cancellation would be logged as if it were an exception error, which is
> an intractable problem with your design because in common
> implementations cancellation objects have no type or name. All you can
> do is rethrow them with a generic throw.

In my designs threads cancel themselves by throwing a custom thread
cancellation object. Again pthread cancel is off-topic in this ng.


>
> However you are still wrong about your 1990's-style 'runnable' virtual
> base class, mate (IMO). In any event, C++11 has gone for the more
> usable std::function concrete class for that kind of polymorphism.

You can keep repeating the same shite over and over again but it will
remain shite. There is *nothing* wrong with the template method pattern
and there is nothing wrong having a class that represents a thread of
execution that performs a specific task.

>
> Chris
>

/Flibble

Chris Vine

unread,
Dec 21, 2014, 5:30:42 PM12/21/14
to
On Sun, 21 Dec 2014 22:28:26 +0000
Mr Flibble <fli...@i42.co.uk> wrote:
> In my designs threads cancel themselves by throwing a custom thread
> cancellation object. Again pthread cancel is off-topic in this ng.

You can't do that effectively on windows, your platform, I believe.

> > However you are still wrong about your 1990's-style 'runnable'
> > virtual base class, mate (IMO). In any event, C++11 has gone for
> > the more usable std::function concrete class for that kind of
> > polymorphism.
>
> You can keep repeating the same shite over and over again but it will
> remain shite. There is *nothing* wrong with the template method
> pattern and there is nothing wrong having a class that represents a
> thread of execution that performs a specific task.

You're talking shite mate. When you wrote your code mobile telephones
probably weighed 5 pounds. You need to get with the beat.

Chris

Mr Flibble

unread,
Dec 21, 2014, 5:37:07 PM12/21/14
to
On 21/12/2014 22:30, Chris Vine wrote:
> On Sun, 21 Dec 2014 22:28:26 +0000
> Mr Flibble <fli...@i42.co.uk> wrote:
>> In my designs threads cancel themselves by throwing a custom thread
>> cancellation object. Again pthread cancel is off-topic in this ng.
>
> You can't do that effectively on windows, your platform, I believe.

That's amazing; I cannot explain how I am able to do it then.

>
>>> However you are still wrong about your 1990's-style 'runnable'
>>> virtual base class, mate (IMO). In any event, C++11 has gone for
>>> the more usable std::function concrete class for that kind of
>>> polymorphism.
>>
>> You can keep repeating the same shite over and over again but it will
>> remain shite. There is *nothing* wrong with the template method
>> pattern and there is nothing wrong having a class that represents a
>> thread of execution that performs a specific task.
>
> You're talking shite mate. When you wrote your code mobile telephones
> probably weighed 5 pounds. You need to get with the beat.

Funny but I used to work in the mobile phone industry and I was part of
the team that invented the smartphone.

Don't try to troll me mate as you are punching well above your weight.

>
> Chris
>

/Flibble

Chris M. Thomasson

unread,
Dec 21, 2014, 5:43:53 PM12/21/14
to
> "Mr Flibble" wrote in message
> news:y9OdnY_R7PUW1grJ...@giganews.com...

> > On 21/12/2014 22:30, Chris Vine wrote:
> > On Sun, 21 Dec 2014 22:28:26 +0000
> > > Mr Flibble <fli...@i42.co.uk> wrote:
> >> In my designs threads cancel themselves by throwing a custom thread
> >> cancellation object. Again pthread cancel is off-topic in this ng.
> >
> > You can't do that effectively on windows, your platform, I believe.


> That's amazing; I cannot explain how I am able to do it then.

IIRC, pthread-win32 has deferred cancellation, and asynchronous
cancel is a kernel hack requiring a special driver be installed or
something:

https://www.sourceware.org/pthreads-win32

Chris Vine

unread,
Dec 21, 2014, 5:44:50 PM12/21/14
to
On Sun, 21 Dec 2014 22:36:58 +0000
Mr Flibble <fli...@i42.co.uk> wrote:
> On 21/12/2014 22:30, Chris Vine wrote:
> > On Sun, 21 Dec 2014 22:28:26 +0000
> > You can't do that effectively on windows, your platform, I believe.
>
> That's amazing; I cannot explain how I am able to do it then.

Go ahead and explain. I am interested in any mainstream platform.

[snip]
> > You're talking shite mate. When you wrote your code mobile
> > telephones probably weighed 5 pounds. You need to get with the beat.
>
> Funny but I used to work in the mobile phone industry and I was part
> of the team that invented the smartphone.
>
> Don't try to troll me mate as you are punching well above your weight.

Don't like your own shite shovelling then? Oh dear.

Chris

Mr Flibble

unread,
Dec 21, 2014, 5:53:20 PM12/21/14
to
On 21/12/2014 22:44, Chris Vine wrote:
> On Sun, 21 Dec 2014 22:36:58 +0000
> Mr Flibble <fli...@i42.co.uk> wrote:
>> On 21/12/2014 22:30, Chris Vine wrote:
>>> On Sun, 21 Dec 2014 22:28:26 +0000
>>> You can't do that effectively on windows, your platform, I believe.
>>
>> That's amazing; I cannot explain how I am able to do it then.
>
> Go ahead and explain. I am interested in any mainstream platform.

If you don't make blocking calls thread cancellation is trivial mate.
In my designs I don't make blocking calls. My thread class can be
cancelled by another thread quite easily as all that happens is a flag
is set and the thread cancels itself by throwing a cancellation object.

>
> [snip]
>>> You're talking shite mate. When you wrote your code mobile
>>> telephones probably weighed 5 pounds. You need to get with the beat.
>>
>> Funny but I used to work in the mobile phone industry and I was part
>> of the team that invented the smartphone.
>>
>> Don't try to troll me mate as you are punching well above your weight.
>
> Don't like your own shite shovelling then? Oh dear.

What shite mate? I was part of the team that developed the first
smartphone: the Ericsson R380. It didn't weigh 5 pounds.

/Flibble

Chris Vine

unread,
Dec 21, 2014, 6:07:53 PM12/21/14
to
On Sun, 21 Dec 2014 22:53:10 +0000
Mr Flibble <fli...@i42.co.uk> wrote:
> On 21/12/2014 22:44, Chris Vine wrote:
> > On Sun, 21 Dec 2014 22:36:58 +0000
> > Mr Flibble <fli...@i42.co.uk> wrote:
> >> On 21/12/2014 22:30, Chris Vine wrote:
> >>> On Sun, 21 Dec 2014 22:28:26 +0000
> >>> You can't do that effectively on windows, your platform, I
> >>> believe.
> >>
> >> That's amazing; I cannot explain how I am able to do it then.
> >
> > Go ahead and explain. I am interested in any mainstream platform.
>
> If you don't make blocking calls thread cancellation is trivial mate.
> In my designs I don't make blocking calls. My thread class can be
> cancelled by another thread quite easily as all that happens is a
> flag is set and the thread cancels itself by throwing a cancellation
> object.

That's hopeless. One of the reasons you often want to start a thread
is so that you can make blocking calls, so you can program serially
rather than with asynchronous callbacks. In POSIX, it is the blocking
calls which represent cancellation points (other than
pthread_testcancel()).

Incidentally, that is the reason why thread cancellation will probably
never become part of the C++ standard, and why boost interruptible
threads never got any further. To be effective they depend on the
behaviour of blocking system calls, which is platform dependent.

Chris

Mr Flibble

unread,
Dec 21, 2014, 6:21:16 PM12/21/14
to
On 21/12/2014 23:07, Chris Vine wrote:
> On Sun, 21 Dec 2014 22:53:10 +0000
> Mr Flibble <fli...@i42.co.uk> wrote:
>> On 21/12/2014 22:44, Chris Vine wrote:
>>> On Sun, 21 Dec 2014 22:36:58 +0000
>>> Mr Flibble <fli...@i42.co.uk> wrote:
>>>> On 21/12/2014 22:30, Chris Vine wrote:
>>>>> On Sun, 21 Dec 2014 22:28:26 +0000
>>>>> You can't do that effectively on windows, your platform, I
>>>>> believe.
>>>>
>>>> That's amazing; I cannot explain how I am able to do it then.
>>>
>>> Go ahead and explain. I am interested in any mainstream platform.
>>
>> If you don't make blocking calls thread cancellation is trivial mate.
>> In my designs I don't make blocking calls. My thread class can be
>> cancelled by another thread quite easily as all that happens is a
>> flag is set and the thread cancels itself by throwing a cancellation
>> object.
>
> That's hopeless. One of the reasons you often want to start a thread
> is so that you can make blocking calls, so you can program serially
> rather than with asynchronous callbacks. In POSIX, it is the blocking
> calls which represent cancellation points (other than
> pthread_testcancel()).

Not hopeless at all. Yes one reason to create a thread is if you *have*
to make multiple simultaneous blocking calls however I maintain that you
don't have to make blocking calls if you design things properly (and are
not stuck with some legacy code). You obviously don't have the
experience to think about how to design things in a non-simplistic
(blocking) way. Any monkey can write code that blocks.

>
> Incidentally, that is the reason why thread cancellation will probably
> never become part of the C++ standard, and why boost interruptible
> threads never got any further. To be effective they depend on the
> behaviour of blocking system calls, which is platform dependent.

The first sensible thing you've said but alas mostly irrelevent to me as
I am able to write non-blocking code; shame you can't.

/Flibble

Chris Vine

unread,
Dec 21, 2014, 6:44:24 PM12/21/14
to
On Sun, 21 Dec 2014 23:21:06 +0000
Mr Flibble <fli...@i42.co.uk> wrote:
[snip]
> The first sensible thing you've said but alas mostly irrelevent to me
> as I am able to write non-blocking code; shame you can't.

Ad hominem: you've lost. Of course I can write non-blocking code. In
one of the languages I use (not C#, python or javascript, which are
quite well advanced on this) I have written assymetrical coroutines
which provide await-type semantics on asynchronous code via an event
loop. That might come with C++17 (probably later) but to break out of
nests of asynchronous callbacks in C++ sometimes you just need blocking
code.

And thread cancellation without it is pointless, in my opinion.

Chris

Mr Flibble

unread,
Dec 21, 2014, 6:48:39 PM12/21/14
to
If ad hom is losing then you lost about 10 posts ago mate. Get a proper
job.

/Flibble

Ian Collins

unread,
Dec 21, 2014, 7:49:36 PM12/21/14
to
How can something cancel itself if it is busy or blocked doing something
else?

--
Ian Collins

Mr Flibble

unread,
Dec 21, 2014, 7:52:39 PM12/21/14
to
In my designs a thread will not be blocked for any significant length of
time.

/Flibble


Ian Collins

unread,
Dec 21, 2014, 7:54:51 PM12/21/14
to
So your designs avoid any system calls that can block? I wish I had
that luxury...

--
Ian Collins

Mr Flibble

unread,
Dec 21, 2014, 7:56:32 PM12/21/14
to
Such as?

/Flibble


Ian Collins

unread,
Dec 21, 2014, 8:03:34 PM12/21/14
to
I guess that depends on the platform, but on mine (POSIX) most I/O and
process management calls (including the ubiquitous system()) are both
blocking and cancellation points.

--
Ian Collins

Richard Damon

unread,
Dec 21, 2014, 8:44:01 PM12/21/14
to
On 12/21/14 3:07 PM, Chris Vine wrote:
> On Sun, 21 Dec 2014 18:04:35 +0000
> Mr Flibble <fli...@i42.co.uk> wrote:
>> It is not over design at all mate; basically you are clueless.
>
> So you have a program which happens to have, say, 100 functions which
> might be executed in their own threads at some point during execution
> of the program. You want to have the class representing a thread to
> have a separate start method, so you cannot use std::thread just by
> itself. One approach is to have a single concrete thread class, where
> each function/callable object in question is passed at object
> construction time and stored in a std::function or similar object, which
> is a data member of the class, and which is executed when start is
> called.
>
> Another approach is to have a virtual run method of a virtual base
> thread class and force the programmer to write 100 derived classes,
> each with their own version of the run method representing the function
> in question to be executed by it (that is what your virtual 'task'
> method did).
>
> The first approach provides run time binding to a polymorphic function
> object. The second provides compile time binding in the class
> definition but with run time virtual method selection. I very much
> prefer the first.
>

I find that it very much depends on the application. I find that I often
have a set of classes representing things in the system, some of which
may have several instances, and these classes have a natural "thread of
execution" to handle them. In this case, having a base class for a
thread-class using a virtual "run" function can make sense.

As you say, sometimes you also have operations that aren't naturally
tied to a class that you want to schedule to run.

What I find tends to work is to have the actual thread system use a
provided function pointer with a void* parameter, and a void* value, and
the base thread-class use a function that takes pointer to the object
(converted to a void* pointer) and calls the "run" method of the class.
This lets you use which ever method is right for you application.

You could also do the reverse, and start from a class interface and make
a generic class that takes a function pointer (and any data to pass to
the function) and the run method just calls that function.

In both case "function pointer" could be generalized function like
object like std::function.

Melzzzzz

unread,
Dec 21, 2014, 9:00:46 PM12/21/14
to
Nope. As I see he re throws, it is fine. Program would be terminated
without re throw.

As I said, that is not a problem in
> windows (your platform) because it does not have meaningful/usable
> cancellation.

For thread cancellation to work one must assure that program is in
sensible state by disabling/enabling cancellation at appropriate points
anyway,

>
> Chris


Öö Tiib

unread,
Dec 22, 2014, 6:49:41 AM12/22/14
to
On Windows there are "Alertable I/O" and "Asynchronous Procedure Calls".
You just need to make threads to enter into "alertable state" now and
then (call waits or sleeps with special flags) for those to work.

Mr Flibble

unread,
Dec 22, 2014, 9:28:55 AM12/22/14
to
My platforms include Windows and POSIX. For I/O I use boost.asio which
is non-blocking. For "system()" I don't invoke processes that block for
any significant amount of time. If none of your threads block for more
than, say, a second then thread cancellation remains trivial.

The key to writing non-blocking code is getting your head around the
concept of splitting up work into manageable chunks.

/Flibble.

Ian Collins

unread,
Dec 23, 2014, 5:50:09 PM12/23/14
to
Mr Flibble wrote:
> On 22/12/2014 01:03, Ian Collins wrote:

>> I guess that depends on the platform, but on mine (POSIX) most I/O and
>> process management calls (including the ubiquitous system()) are both
>> blocking and cancellation points.
>
> My platforms include Windows and POSIX. For I/O I use boost.asio which
> is non-blocking.

Isn't boost.asio normally used as an alternative to threads+blocking I?O?

> For "system()" I don't invoke processes that block for
> any significant amount of time. If none of your threads block for more
> than, say, a second then thread cancellation remains trivial.

Sometimes one doesn't have a choice!

> The key to writing non-blocking code is getting your head around the
> concept of splitting up work into manageable chunks.

That's fine for your own code, but when using third party or system
interfaces, you don't have a choice.

--
Ian Collins

Mr Flibble

unread,
Dec 23, 2014, 8:03:44 PM12/23/14
to
On 23/12/2014 22:49, Ian Collins wrote:
> Mr Flibble wrote:
>> On 22/12/2014 01:03, Ian Collins wrote:
>
>>> I guess that depends on the platform, but on mine (POSIX) most I/O and
>>> process management calls (including the ubiquitous system()) are both
>>> blocking and cancellation points.
>>
>> My platforms include Windows and POSIX. For I/O I use boost.asio which
>> is non-blocking.
>
> Isn't boost.asio normally used as an alternative to threads+blocking I?O?

Who says you cannot use multiple threads with boost.asio? Think
threadpools doing work.

/Flibble

Ian Collins

unread,
Dec 23, 2014, 8:29:21 PM12/23/14
to
No one, but the choice is usually between one or the other. Both have
their complexities, async I/O may save synchronisation at the cost of
more complex I/O processing where threads simplify the I/O processing
and may require synchronisation. Adding the two complexities to one
solution appears counter-intuitive.

Async I/O doesn't help where the code your threads call doesn't support it.

--
Ian Collins

Martijn Lievaart

unread,
Dec 24, 2014, 6:35:36 AM12/24/14
to
Have a better look at asio. It is very good at exactly hiding the
complexities you mention. And mixing the two solutions makes perfect
sense as the result is very scalable and is actually what is done in most
high performance solutions.

M4

woodb...@gmail.com

unread,
Dec 25, 2014, 12:09:39 AM12/25/14
to
I use async IO and multiple process instances to minimize the
need for threads. I think this approach scales well.

Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net

Mr Flibble

unread,
Dec 25, 2014, 11:15:28 AM12/25/14
to
Multiple processes do not scale well at all which is why pthreads was
invented.

/Flibble

Öö Tiib

unread,
Dec 25, 2014, 2:58:06 PM12/25/14
to
That reads too generic claim to be believable. Sounds skewed. Do you
have any links to papers to point at?

If to reason:

1) The 'fork()' is optimized into quite cheap on lot of platforms by
using lazy copying on process data (CoW of virtual memory pages).
It is still more expensive than spawning a thread but nothing to
lament about.
2) Multithreading does waste lot of time to synchronize access
to shared data, processes do not share data.
3) Ending a process with the likes of 'std::quick_exit' is efficient
form of freeing a portion of unneeded resources. You got to use
some custom 'allocator' with lot of discipline (I haven't met)
to achieve anything competitive with a thread.

So there are no clear benefit that multithreading beats
multiprocessing always. Multiprocessing is efficient solution
for number of problems.

Melzzzzz

unread,
Dec 25, 2014, 4:34:54 PM12/25/14
to
Main reason for using threads is startup time. Other reason is
that not all systems overcommit memory.

> 2) Multithreading does waste lot of time to synchronize access
> to shared data, processes do not share data.

Second main reason to use threads is simpler IPC.

> 3) Ending a process with the likes of 'std::quick_exit' is efficient
> form of freeing a portion of unneeded resources. You got to use
> some custom 'allocator' with lot of discipline (I haven't met)
> to achieve anything competitive with a thread.

If there are no memory leaks I can't see why is this problem
with threads? In C++, just throwing exception is usually enough
to exit from thread...

>
> So there are no clear benefit that multithreading beats
> multiprocessing always. Multiprocessing is efficient solution
> for number of problems.

Only thing that is bad for threads and goes in favor of processes
is memory fragmentation and therefore steady growth of memory usage. All
long running threaded programs have that problem.


Mr Flibble

unread,
Dec 25, 2014, 4:53:55 PM12/25/14
to
Most forked child processes will use most of their memory as the parent
process usually doesn't do much and the child process typically calls
exec rendering your point invalid. My assertion remains intact:
processes do not scale well, threads do.

/Flibble

Chris M. Thomasson

unread,
Dec 25, 2014, 5:59:03 PM12/25/14
to
> "Melzzzzz" wrote in message news:20141225223439.505a880d@maxa-pc...

[...]

> Only thing that is bad for threads and goes in favor of processes
> is memory fragmentation and therefore steady growth of memory usage. All
> long running threaded programs have that problem.

Are you perhaps referring to memory allocators? In my experience,
carefully written, long-running multi-threaded systems (e.e.g, a server),
do not suffer from this. Can you perhaps be more precise? I have a little
problem with the word "All" in your statement.

Sorry... :^o

Melzzzzz

unread,
Dec 25, 2014, 6:10:22 PM12/25/14
to
Ok, I generalized too much ;) But look for example gnome-shell
process and firefox. They grow and grow ;)


Öö Tiib

unread,
Dec 26, 2014, 12:39:50 AM12/26/14
to
I am not against using threads. I was arguing against generic claim of
Leigh that multithreading is superior solution than multiprocessing.
Neither is generally superior. Each has usages.
Existence of systems without feature (for example floating point hardware)
can not be good excuse to avoid the feature (for example floating point
arithmetic) on general.

> > 2) Multithreading does waste lot of time to synchronize access
> > to shared data, processes do not share data.
>
> Second main reason to use threads is simpler IPC.

However when there are no IPC needed? For example your program tries
to open a file but discovers that it is for some older version. It can
spawn a process that converts it since all that it needs is file of
current version. No IPC is needed. Does it scale better if it instead
keeps all the code for converting old files in memory just for such
rare case?

> > 3) Ending a process with the likes of 'std::quick_exit' is efficient
> > form of freeing a portion of unneeded resources. You got to use
> > some custom 'allocator' with lot of discipline (I haven't met)
> > to achieve anything competitive with a thread.
>
> If there are no memory leaks I can't see why is this problem
> with threads? In C++, just throwing exception is usually enough
> to exit from thread...

What memory leaks? The program reached a point where the work is done.
However by our typical good design nothing will leak. Everything is
managed by RAII. So thread has nothing like 'std::quick_exit' that frees
all the resources it acquired for doing the work at once. Instead it will
run all the destructors recursively and deallocate every tiny 'std::string'
it has separately. It may be considerably less efficient.

> > So there are no clear benefit that multithreading beats
> > multiprocessing always. Multiprocessing is efficient solution
> > for number of problems.
>
> Only thing that is bad for threads and goes in favor of processes
> is memory fragmentation and therefore steady growth of memory usage. All
> long running threaded programs have that problem.

No, not all at all. :D
My point was that there are no generic silver bullets "that are always
best and period".

Martijn Lievaart

unread,
Dec 28, 2014, 7:35:51 AM12/28/14
to
On Thu, 25 Dec 2014 11:57:43 -0800, Öö Tiib wrote:

> On Thursday, December 25, 2014 6:15:28 PM UTC+2, Mr Flibble wrote:

>> Multiple processes do not scale well at all which is why pthreads was
>> invented.
>
> That reads too generic claim to be believable. Sounds skewed. Do you
> have any links to papers to point at?

I agree its way too generic but:

> 2) Multithreading does waste lot of time to synchronize access
> to shared data, processes do not share data.

This doesn't have to be the case at all. A well written threaded program
needs very little synchronization. Memory management can be a problem,,
but that can be solved with per thread (or per strand) allocators.

In fact, the main synchronization would be for interthread communication,
something which is inherently heavyweight between multiple programs as
well.

M4

Mr Flibble

unread,
Dec 28, 2014, 8:29:11 AM12/28/14
to
On 28/12/2014 12:33, Martijn Lievaart wrote:
> On Thu, 25 Dec 2014 11:57:43 -0800, Öö Tiib wrote:
>
>> On Thursday, December 25, 2014 6:15:28 PM UTC+2, Mr Flibble wrote:
>
>>> Multiple processes do not scale well at all which is why pthreads was
>>> invented.
>>
>> That reads too generic claim to be believable. Sounds skewed. Do you
>> have any links to papers to point at?
>
> I agree its way too generic but:

Nonsense; processes are heavyweight, threads are lightweight; processes
have their own address space; threads share an address space. It seems
obvious to me that threads scale better than processes.

/Flibble

Öö Tiib

unread,
Dec 28, 2014, 2:46:44 PM12/28/14
to
On Sunday, December 28, 2014 2:35:51 PM UTC+2, Martijn Lievaart wrote:
> On Thu, 25 Dec 2014 11:57:43 -0800, Öö Tiib wrote:
>
> > On Thursday, December 25, 2014 6:15:28 PM UTC+2, Mr Flibble wrote:
>
> >> Multiple processes do not scale well at all which is why pthreads was
> >> invented.
> >
> > That reads too generic claim to be believable. Sounds skewed. Do you
> > have any links to papers to point at?
>
> I agree its way too generic but:
>
> > 2) Multithreading does waste lot of time to synchronize access
> > to shared data, processes do not share data.
>
> This doesn't have to be the case at all. A well written threaded program
> needs very little synchronization. Memory management can be a problem,,
> but that can be solved with per thread (or per strand) allocators.

Cross-task message passing is usually more pleasant form of
synchronization than the mutexes and semaphores. It can become less
scalable when the resulting traffic (sharing of data) is large but like
you said it does not have to be the case always. When the threads keep
anyway majority of data separate (using such message-passing and separate
memory management) then there are no clear difference from (and so
advantage versus) processes.

> In fact, the main synchronization would be for interthread communication,
> something which is inherently heavyweight between multiple programs as
> well.

Good example of rather scalable server-side architecture is Node.js.
There the applications are single threaded (as rule) yet it can
service millions of simultaneous connections. It is worth to consider
and to compare such things a bit.

One important point for multiprocessing (that I did not initially add)
is security. Huge applications with numerous modules are usually made
by rather multiple teams (sometimes located on multiple continents).
It is unpleasant to find out that someone got illegal access to your
module's data because some other module has security vulnerability.

I find multi-threading clearly better than multi-processing for solving
embarrasingly parallel problems. On all other cases it is worth to
consider what is better in situation under hand.

Öö Tiib

unread,
Dec 28, 2014, 3:27:12 PM12/28/14
to
Sharing of memory between processes typically means that two processes
share access to same page of virtual memory. Each thread has its own stack anyway. The code and any static immutable data can be shared by overcommit
of 'fork'. There certainly is overhead but it may be outweighed by
simplicity and robustness of single-threadedness.

For example: The measured overhead per process is 6MB. A thread becomes
overburdened with I/O and starts to lag at 10K connections. So it forks
additional process to service other 10K connections. The overhead is
split as 60 bytes per connection but there's no need to do per-thread
allocators or other such tricks.

Ian Collins

unread,
Dec 28, 2014, 4:09:08 PM12/28/14
to
嘱 Tiib wrote:
> On Sunday, December 28, 2014 3:29:11 PM UTC+2, Mr Flibble wrote:
>> On 28/12/2014 12:33, Martijn Lievaart wrote:
>>> On Thu, 25 Dec 2014 11:57:43 -0800, 嘱 Tiib wrote:
>>>
>>>> On Thursday, December 25, 2014 6:15:28 PM UTC+2, Mr Flibble wrote:
>>>
>>>>> Multiple processes do not scale well at all which is why pthreads was
>>>>> invented.
>>>>
>>>> That reads too generic claim to be believable. Sounds skewed. Do you
>>>> have any links to papers to point at?
>>>
>>> I agree its way too generic but:
>>
>> Nonsense; processes are heavyweight, threads are lightweight; processes
>> have their own address space; threads share an address space. It seems
>> obvious to me that threads scale better than processes.
>
> Sharing of memory between processes typically means that two processes
> share access to same page of virtual memory. Each thread has its own stack anyway. The code and any static immutable data can be shared by overcommit
> of 'fork'. There certainly is overhead but it may be outweighed by
> simplicity and robustness of single-threadedness.
>
> For example: The measured overhead per process is 6MB. A thread becomes
> overburdened with I/O and starts to lag at 10K connections. So it forks
> additional process to service other 10K connections. The overhead is
> split as 60 bytes per connection but there's no need to do per-thread
> allocators or other such tricks.

Which isn't an uncommon model.

Web servers tend to run multiple processes, databases run multiple
processes with shared memory.

There are some tasks with tightly coupled data where threads are the
best or only viable solution, others where data separation makes a lot
more sense. I guess two opposite examples are the qemu emulator (some
VMs on this system run scores of threads) and Samba, which runs a
process per client.

--
Ian Collins

Mr Flibble

unread,
Dec 28, 2014, 4:15:36 PM12/28/14
to
On 28/12/2014 20:27, Öö Tiib wrote:
> On Sunday, December 28, 2014 3:29:11 PM UTC+2, Mr Flibble wrote:
>> On 28/12/2014 12:33, Martijn Lievaart wrote:
>>> On Thu, 25 Dec 2014 11:57:43 -0800, Öö Tiib wrote:
>>>
>>>> On Thursday, December 25, 2014 6:15:28 PM UTC+2, Mr Flibble wrote:
>>>
>>>>> Multiple processes do not scale well at all which is why pthreads was
>>>>> invented.
>>>>
>>>> That reads too generic claim to be believable. Sounds skewed. Do you
>>>> have any links to papers to point at?
>>>
>>> I agree its way too generic but:
>>
>> Nonsense; processes are heavyweight, threads are lightweight; processes
>> have their own address space; threads share an address space. It seems
>> obvious to me that threads scale better than processes.
>
> Sharing of memory between processes typically means that two processes
> share access to same page of virtual memory. Each thread has its own stack anyway. The code and any static immutable data can be shared by overcommit
> of 'fork'. There certainly is overhead but it may be outweighed by
> simplicity and robustness of single-threadedness.

IPC between processes is not as simple as threads communicating within a
shared address space; I would argue that multi-threaded is no less
simple or robust than multi-process.


>
> For example: The measured overhead per process is 6MB. A thread becomes
> overburdened with I/O and starts to lag at 10K connections. So it forks
> additional process to service other 10K connections. The overhead is
> split as 60 bytes per connection but there's no need to do per-thread
> allocators or other such tricks.

That is far more complicated than a threadpool within the same process.

/Flibble

Ian Collins

unread,
Dec 28, 2014, 4:33:15 PM12/28/14
to
Mr Flibble wrote:
> On 28/12/2014 20:27, Öö Tiib wrote:
>
> IPC between processes is not as simple as threads communicating within a
> shared address space; I would argue that multi-threaded is no less
> simple or robust than multi-process.

Then you are at odds with Google's Chrome developers, See
http://blog.chromium.org/2008/09/multi-process-architecture.html

A multi-threaded solution may well be simpler, but that simplicity comes
at a cost.

>> For example: The measured overhead per process is 6MB. A thread becomes
>> overburdened with I/O and starts to lag at 10K connections. So it forks
>> additional process to service other 10K connections. The overhead is
>> split as 60 bytes per connection but there's no need to do per-thread
>> allocators or other such tricks.
>
> That is far more complicated than a threadpool within the same process.

But less vulnerable to bugs or malicious code..

--
Ian Collins

Mr Flibble

unread,
Dec 28, 2014, 6:08:11 PM12/28/14
to
I hope you didn't get the impression that I think you shouldn't solve
problems with multiple processes; I am simply claiming that threads
scale better than processes.

/Flibble

Ian Collins

unread,
Dec 28, 2014, 6:27:00 PM12/28/14
to
I don't thinks it's a clear cut these days. Samba is a reasonable
counter example: on one of my servers I can see several hundred smbd
processes in a file share zone. These are all independent, so there
would be little point in running multiple threads. On the other hand
the storage pool process runs a pool of 256 threads, which does make
sense given its workload.

--
Ian Collins

Chris M. Thomasson

unread,
Dec 29, 2014, 2:54:30 PM12/29/14
to
> "Ian Collins" wrote in message news:cgbb8g...@mid.individual.net...
Great point Ian. If a process dies in a multi-process system, it should not
shutdown the entire system. If you only have a single process comprising
the entire system, well, if a thread dies, the process should die. However,
if
you have multiple, multi-threaded processes, and a thread dies, only that
individual process dies, and you can usually do something about it.

So, IMVVHO, a clever mix of multiple processes with multiple threads per-
process, can be a decent solution indeed...

woodb...@gmail.com

unread,
Dec 29, 2014, 5:12:36 PM12/29/14
to
That's my approach with my code generator. A web server is
a code generator basically so it makes sense to me to copy
that model.


Brian
Ebenezer Enterprises
http://webEbenezer.net

Tobias Müller

unread,
Dec 30, 2014, 12:53:29 PM12/30/14
to
Öö Tiib <oot...@hot.ee> wrote:
> [...]
> I am not against using threads. I was arguing against generic claim of
> Leigh that multithreading is superior solution than multiprocessing.
> Neither is generally superior. Each has usages.

That must be a typo. Certainly you meant SAusages.

> [...]

Tobi

woodb...@gmail.com

unread,
Jan 1, 2015, 2:16:44 PM1/1/15
to
On Thursday, December 25, 2014 1:58:06 PM UTC-6, Öö Tiib wrote:
>
> That reads too generic claim to be believable. Sounds skewed. Do you
> have any links to papers to point at?
>
> If to reason:
>
> 1) The 'fork()' is optimized into quite cheap on lot of platforms by
> using lazy copying on process data (CoW of virtual memory pages).
> It is still more expensive than spawning a thread but nothing to
> lament about.
> 2) Multithreading does waste lot of time to synchronize access
> to shared data, processes do not share data.
> 3) Ending a process with the likes of 'std::quick_exit' is efficient
> form of freeing a portion of unneeded resources. You got to use
> some custom 'allocator' with lot of discipline (I haven't met)
> to achieve anything competitive with a thread.
>

Thanks for mentioning std::quick_exit. I'm trying to use
it and have a problem/complaint. I think you're supposed
to include cstdlib to pick up quick_exit. I'm already
using strtol in another part of the program and have
included stdlib.h for that. There was a discussion
previously in this newsgroup about whether to use the
headers that start with c like cstdlib or to use stdlib.h.
It seemed better to use the old form to me, but in this
case, the old form doesn't have anything about quick_exit.
I tend to think there should be a different header for
quick_exit than cstdlib. Anyway, I could just include
cstdlib or could include both:

#include <cstdlib> // quick_exit
#include <stdlib.h> // strtol

Not sure which is better.


Brian
Ebenezer Enterprises - So far G-d has helped us.
http://webEbenezer.net

Mr Flibble

unread,
Jan 1, 2015, 2:39:41 PM1/1/15
to
On 01/01/2015 19:16, woodb...@gmail.com wrote:
> On Thursday, December 25, 2014 1:58:06 PM UTC-6, Öö Tiib wrote:
>>
>> That reads too generic claim to be believable. Sounds skewed. Do you
>> have any links to papers to point at?
>>
>> If to reason:
>>
>> 1) The 'fork()' is optimized into quite cheap on lot of platforms by
>> using lazy copying on process data (CoW of virtual memory pages).
>> It is still more expensive than spawning a thread but nothing to
>> lament about.
>> 2) Multithreading does waste lot of time to synchronize access
>> to shared data, processes do not share data.
>> 3) Ending a process with the likes of 'std::quick_exit' is efficient
>> form of freeing a portion of unneeded resources. You got to use
>> some custom 'allocator' with lot of discipline (I haven't met)
>> to achieve anything competitive with a thread.
>>
>
> Thanks for mentioning std::quick_exit. I'm trying to use
> it and have a problem/complaint. I think you're supposed
> to include cstdlib to pick up quick_exit. I'm already
> using strtol in another part of the program and have
> included stdlib.h for that. There was a discussion
> previously in this newsgroup about whether to use the
> headers that start with c like cstdlib or to use stdlib.h.
> It seemed better to use the old form to me, but in this

Well it is not better to use the old form.

/Flibble

woodb...@gmail.com

unread,
Jan 1, 2015, 2:47:19 PM1/1/15
to
I think it was Alf that thought it was, but I don't
have a link to that thread and it would probably take
me a while to find it.

To make matters worse I get an error on Windows:

error C2039: 'quick_exit' : is not a member of 'std'

I wonder if Microsoft has done something reasonable
and put the prototype in a different header.

Ian Collins

unread,
Jan 1, 2015, 4:01:39 PM1/1/15
to
woodb...@gmail.com wrote:
>
> Thanks for mentioning std::quick_exit. I'm trying to use
> it and have a problem/complaint. I think you're supposed
> to include cstdlib to pick up quick_exit. I'm already
> using strtol in another part of the program and have
> included stdlib.h for that.

It depends on whether quick_exit has made its way into your system's
libC (or whatever it uses) and headers. Considering it was only added
in C11, there's a good chance it hasn't.

If it has, you should be able to use either header.

I see the gcc <cstdlib> header includes this:

# ifdef _GLIBCXX_HAVE_QUICK_EXIT
extern "C" void quick_exit(int) throw() _GLIBCXX_NORETURN;
# endif

--
Ian Collins

Jorgen Grahn

unread,
Jan 1, 2015, 4:37:27 PM1/1/15
to
On Thu, 2014-12-25, Mr Flibble wrote:
> On 25/12/2014 19:57, 嘱 Tiib wrote:
...
>> 1) The 'fork()' is optimized into quite cheap on lot of platforms by
>> using lazy copying on process data (CoW of virtual memory pages).
>> It is still more expensive than spawning a thread but nothing to
>> lament about.
>
> Most forked child processes will use most of their memory as the parent
> process usually doesn't do much and the child process typically calls
> exec rendering your point invalid. My assertion remains intact:
> processes do not scale well, threads do.

I don't particularly want to be involved here, but that argument seems
flawed: forked processes which replace threads probably won't do an
exec. If most forked processes do an exec, perhaps that's due to the
popular perception that multiprocessing is much more costly than
multithreading -- not the /cause/ of that perception?

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

woodb...@gmail.com

unread,
Jan 1, 2015, 4:53:41 PM1/1/15
to
On Thursday, January 1, 2015 3:01:39 PM UTC-6, Ian Collins wrote:
>
> It depends on whether quick_exit has made its way into your system's
> libC (or whatever it uses) and headers. Considering it was only added
> in C11, there's a good chance it hasn't.
>

I can understand how quick_exit is helpful in a C++ program,
but am not sure how it would be useful in a C program.
Some of my earlier comments assumed it was a C++ only thing.
why is quick_exit helpful for C programs?

Ian Collins

unread,
Jan 1, 2015, 5:04:27 PM1/1/15
to
Jorgen Grahn wrote:
> On Thu, 2014-12-25, Mr Flibble wrote:
>> On 25/12/2014 19:57, 嘱 Tiib wrote:
> ...
>>> 1) The 'fork()' is optimized into quite cheap on lot of platforms by
>>> using lazy copying on process data (CoW of virtual memory pages).
>>> It is still more expensive than spawning a thread but nothing to
>>> lament about.
>>
>> Most forked child processes will use most of their memory as the parent
>> process usually doesn't do much and the child process typically calls
>> exec rendering your point invalid. My assertion remains intact:
>> processes do not scale well, threads do.
>
> I don't particularly want to be involved here, but that argument seems
> flawed: forked processes which replace threads probably won't do an
> exec.

That (not doing a exec) is true for most applications I'm aware of that
spawn multiple processes. Most servers that use multiple processes
simply clone an instance of themselves to do work. The parent handles
incoming requests and manages its children.

--
Ian Collins

Ian Collins

unread,
Jan 1, 2015, 5:17:34 PM1/1/15
to
The only difference I'm aware of between quick_exit use in C and C++ is
static destructors. All of the other differences between exit,
quick_exit() and _Exit apply. There is a big difference between the
actions performed by calling exit and calling _Exit. quick_exit fills
the gap by providing a means to perform limited cleanup.

--
Ian Collins

Jorgen Grahn

unread,
Jan 1, 2015, 5:58:45 PM1/1/15
to
Well, I'm not going to disagree with you (and it's not important to my
argument). But at least on Unix it's pretty common to do something
like a popen(2) to feed your data through an existing program, so it's
not like doing an exec is unheard of, either.

Ian Collins

unread,
Jan 1, 2015, 6:04:35 PM1/1/15
to
Jorgen Grahn wrote:
> On Thu, 2015-01-01, Ian Collins wrote:
>> Jorgen Grahn wrote:
>>> On Thu, 2014-12-25, Mr Flibble wrote:
>>>> On 25/12/2014 19:57, 嘱 Tiib wrote:
>>> ...
>>>>> 1) The 'fork()' is optimized into quite cheap on lot of platforms by
>>>>> using lazy copying on process data (CoW of virtual memory pages).
>>>>> It is still more expensive than spawning a thread but nothing to
>>>>> lament about.
>>>>
>>>> Most forked child processes will use most of their memory as the parent
>>>> process usually doesn't do much and the child process typically calls
>>>> exec rendering your point invalid. My assertion remains intact:
>>>> processes do not scale well, threads do.
>>>
>>> I don't particularly want to be involved here, but that argument seems
>>> flawed: forked processes which replace threads probably won't do an
>>> exec.
>>
>> That (not doing a exec) is true for most applications I'm aware of that
>> spawn multiple processes. Most servers that use multiple processes
>> simply clone an instance of themselves to do work. The parent handles
>> incoming requests and manages its children.
>
> Well, I'm not going to disagree with you (and it's not important to my
> argument).

I'm not really sure how you could disagree with me agreeing with you!

> But at least on Unix it's pretty common to do something
> like a popen(2) to feed your data through an existing program, so it's
> not like doing an exec is unheard of, either.

True, but those typically aren't applications that would use threads.

--
Ian Collins

Jorgen Grahn

unread,
Jan 1, 2015, 6:14:23 PM1/1/15
to
On Thu, 2015-01-01, Ian Collins wrote:
> Jorgen Grahn wrote:
>> On Thu, 2015-01-01, Ian Collins wrote:
>>> Jorgen Grahn wrote:
...
>>>> I don't particularly want to be involved here, but that argument seems
>>>> flawed: forked processes which replace threads probably won't do an
>>>> exec.
>>>
>>> That (not doing a exec) is true for most applications I'm aware of that
>>> spawn multiple processes. Most servers that use multiple processes
>>> simply clone an instance of themselves to do work. The parent handles
>>> incoming requests and manages its children.
>>
>> Well, I'm not going to disagree with you (and it's not important to my
>> argument).
>
> I'm not really sure how you could disagree with me agreeing with you!

You made a broader statement than I did: you could have agreed with me
and at the same time said something I disagreed with.

(I probably shouldn't have commented on that, but I figured people
here are used to everyone disagreeing with everyone, so I thought I'd
better clarify that I wasn't ...)

Paavo Helde

unread,
Jan 1, 2015, 6:33:54 PM1/1/15
to
Ian Collins <ian-...@hotmail.com> wrote in news:cgluj1Fm4qrU10
@mid.individual.net:

> Jorgen Grahn wrote:
>>
>> I don't particularly want to be involved here, but that argument seems
>> flawed: forked processes which replace threads probably won't do an
>> exec.
>
> That (not doing a exec) is true for most applications I'm aware of that
> spawn multiple processes. Most servers that use multiple processes
> simply clone an instance of themselves to do work. The parent handles
> incoming requests and manages its children.

If the parent process is multithreaded, then fork() clones a single thread
only. This means that in a multithreaded program, about the only thing the
child can do after fork is exec. Otherwise, all garbage from the other
threads like allocated memory and locked mutexes remains in the new
process. Locked mutexes (e.g. in the memory allocator) may easily cause the
child to be deadlocked. Ensuring that other threads are in a clean state
would need tedious synchronization and may kill the hoped-for performance.

This is relevant for the multithread+multiprocess solution proposed by
somebody upthread. Also, I believe third-party libraries are starting to
make more use of multiple threads. As more and more programs become
multithreaded, the possibilities to apply fork-no-exec scenario will be
reduced.

Ian Collins

unread,
Jan 1, 2015, 6:48:02 PM1/1/15
to
Paavo Helde wrote:
> Ian Collins <ian-...@hotmail.com> wrote in news:cgluj1Fm4qrU10
> @mid.individual.net:
>
>> Jorgen Grahn wrote:
>>>
>>> I don't particularly want to be involved here, but that argument seems
>>> flawed: forked processes which replace threads probably won't do an
>>> exec.
>>
>> That (not doing a exec) is true for most applications I'm aware of that
>> spawn multiple processes. Most servers that use multiple processes
>> simply clone an instance of themselves to do work. The parent handles
>> incoming requests and manages its children.
>
> If the parent process is multithreaded, then fork() clones a single thread
> only. This means that in a multithreaded program, about the only thing the
> child can do after fork is exec. Otherwise, all garbage from the other
> threads like allocated memory and locked mutexes remains in the new
> process. Locked mutexes (e.g. in the memory allocator) may easily cause the
> child to be deadlocked. Ensuring that other threads are in a clean state
> would need tedious synchronization and may kill the hoped-for performance.

Admittedly the situation can become messy, but POSIX does provide
pthread_atfork() to help manage forking in the threaded application.
See the rationale at http://pubs.opengroup.org/onlinepubs/000095399/

--
Ian Collins

woodb...@gmail.com

unread,
Jan 1, 2015, 9:08:48 PM1/1/15
to
On Thursday, January 1, 2015 4:17:34 PM UTC-6, Ian Collins wrote:

>
> The only difference I'm aware of between quick_exit use in C and C++ is
> static destructors.

http://www.cplusplus.com/reference/cstdlib/quick_exit/

That page says, "No object destructors are called."

> All of the other differences between exit,
> quick_exit() and _Exit apply. There is a big difference between the
> actions performed by calling exit and calling _Exit. quick_exit fills
> the gap by providing a means to perform limited cleanup.

I'm not finding much documentation on quick_exit on C sites.
I've only found some documentation for it on C++ sites.
I'm using https://duckduckgo.com to search.

Brian
Ebenezer Enterprises
http://webEbenezer.net


>
> --
> Ian Collins

Ian Collins

unread,
Jan 1, 2015, 9:48:18 PM1/1/15
to
woodb...@gmail.com wrote:
> On Thursday, January 1, 2015 4:17:34 PM UTC-6, Ian Collins wrote:
>
>>
>> The only difference I'm aware of between quick_exit use in C and C++ is
>> static destructors.
>
> http://www.cplusplus.com/reference/cstdlib/quick_exit/
>
> That page says, "No object destructors are called."

Quite. C doesn't have them...

>> All of the other differences between exit,
>> quick_exit() and _Exit apply. There is a big difference between the
>> actions performed by calling exit and calling _Exit. quick_exit fills
>> the gap by providing a means to perform limited cleanup.
>
> I'm not finding much documentation on quick_exit on C sites.
> I've only found some documentation for it on C++ sites.

My platform doesn't have it, but FreeBSD does:

http://www.unix.com/man-page/freebsd/3/QUICK_EXIT/

--
Ian Collins

Melzzzzz

unread,
Jan 1, 2015, 10:49:59 PM1/1/15
to
Hm, I am on Linux it has quick_exit in libc but it is nowhere
documented...

Ian Collins

unread,
Jan 1, 2015, 10:58:24 PM1/1/15
to
Melzzzzz wrote:
>
> Hm, I am on Linux it has quick_exit in libc but it is nowhere
> documented...

Indeed, the professional quality of Linux man pages!

It's been in glibc for quite a while.

--
Ian Collins

Paavo Helde

unread,
Jan 2, 2015, 1:41:05 AM1/2/15
to
Ian Collins <ian-...@hotmail.com> wrote in
news:cgm4l7F...@mid.individual.net:
Thanks for the link! However, I gather the only thing what pthread_atfork
() achieves is to make the "tedious synchronization" more manageable or
even possible. And the rationale still suggests to call exec() very soon
after fork(). It seems it's mostly just concerned with how to avoid
deadlocks in (arguably buggy) old programs which call unsafe library
routines between fork() and exec().

Cheers
Paavo

Ian Collins

unread,
Jan 2, 2015, 1:47:55 AM1/2/15
to
I see it as more of a tool for multi-threaded library writers to manage
the sate of their library around a fork. The library writers don't have
any control over how their code gets used.

--
Ian Collins

Paavo Helde

unread,
Jan 2, 2015, 3:20:44 AM1/2/15
to
Ian Collins <ian-...@hotmail.com> wrote in
news:cgmt8g...@mid.individual.net:

> Paavo Helde wrote:
>> Thanks for the link! However, I gather the only thing what
>> pthread_atfork () achieves is to make the "tedious synchronization"
>> more manageable or even possible. And the rationale still suggests to
>> call exec() very soon after fork(). It seems it's mostly just
>> concerned with how to avoid deadlocks in (arguably buggy) old
>> programs which call unsafe library routines between fork() and
>> exec().
>
> I see it as more of a tool for multi-threaded library writers to
> manage the sate of their library around a fork. The library writers
> don't have any control over how their code gets used.

Yes, but it's still more like a kludge. Imagine a library which makes use
of an internal threadpool of N threads. Now the pre-fork hook is
basically supposed to send a stop signal to the whole threadpool and wait
until all N threads have completed whatever task they were doing. Seems
not lightweight at all.

And what about the post-fork hook? Should it recreate the threadpool or
not? If there is exec() coming up soon this would not make any sense, and
if no exec is coming it might make sense for the library to terminate N/2
threads in the parent and start N/2 threads in the child. However, there
is no good way for the library to know that (the application and other
libraries may call fork() from multiple places in parallel, some followed
by exec, some not).

Cheers
Paavo

Chris Vine

unread,
Jan 3, 2015, 12:01:18 PM1/3/15
to
I am not certain that you can use pthread_atfork() to do even that.
David Butenhof, the author of the canonical "Programming with POSIX
threads" and then member of the POSIX committee dealing with threads
(he may still be, I don't know) pointed out some time ago that although
the prepare handler and parent handler can call functions which are not
async-signal-safe (provided fork() is not called in a signal handler),
the child handler cannot because it executes in the new process:
https://groups.google.com/forum/?_escaped_fragment_=msg/comp.programming.threads/ThHE32-vRsg/3j-YICgSQzoJ#!msg/comp.programming.threads/ThHE32-vRsg/3j-YICgSQzoJ
https://groups.google.com/forum/?_escaped_fragment_=msg/comp.programming.threads/ThHE32-vRsg/_nZ7_8Ygt-0J#!msg/comp.programming.threads/ThHE32-vRsg/_nZ7_8Ygt-0J

This means that the child handler cannot do anything as simple as call
pthread_mutex_unlock() (let alone create new threads), because that is
not async-signal-safe. All you can really use pthread_atfork() for is
to use the prepare and parent handlers to put the program in a
consistent state which both the parent and child processes are
subsequently capable of recovering from (and in the case of the child,
without calling non-async-signal-safe functions). This means that the
rule that any fork() in a multi-threaded program should shortly
thereafter be followed by an exec*() still generally applies.

Presumably those who write servers which are both multi-threaded
and spawn new server processes on demand are using windows, and windows
allows that (and I do not know if it does or it does not); or they must
be doing something very clever.

Chris

Paavo Helde

unread,
Jan 3, 2015, 3:29:32 PM1/3/15
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote in
news:20150103170...@laptop.homenet:
> Presumably those who write servers which are both multi-threaded
> and spawn new server processes on demand are using windows, and
> windows allows that (and I do not know if it does or it does not);

Windows does not really have fork(). There is a function ZwCreateProcess()
in the POSIX subsystem of Windows which ought to be capable of that, but it
seems this is underdocumented and not easily usable, if at all. And the
POSIX subsystem is ditched anyway in last Windows versions.

Instead of fork(), there is CreateProcess() which always starts a new
process from scratch and is pretty heavyweight. One can emulate fork(Cygwin
does this), but this is even more heavyweight (involves lots of twiddling
with the new process to copy over all the memory and state).

Cheers
Paavo

Scott Lurndal

unread,
Jan 5, 2015, 10:11:28 AM1/5/15
to
With multiple processes, one has an address space per process
(that means page tables, folks). Whereas multiple threads
share a single address space (a single page table).

A page table can be quite large (for a 1TB virtual address space
using 4k pages, about 2GB). That's a fair amount of overhead
on a per-process basis. This can be reduced somewhat by using
larger pages (not particularly straightforward, unfortunately,
whether using transparent large pages or hugetlbfs).

There are use cases where multiple processes are better (no
shared data, pipelines, programmers who don't understand
concurrency) and cases where multiple threads
are better (shared data, large address spaces, skilled programmers).

Jorgen Grahn

unread,
Jan 5, 2015, 12:23:02 PM1/5/15
to
On Mon, 2015-01-05, Scott Lurndal wrote:
...
> There are use cases where multiple processes are better (no
> shared data, pipelines, programmers who don't understand
> concurrency)

I.e. almost everyone of us ...

> and cases where multiple threads
> are better (shared data, large address spaces, skilled programmers).

It is loading more messages.
0 new messages