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

threading problems

61 views
Skip to first unread message

Christopher Pisz

unread,
Aug 24, 2015, 5:59:03 PM8/24/15
to

I am trying to write some concept code for starting a collection of
threads from the main thread that will each be somewhat encapsulated by
a class.

Basically, I want to move that into a service I wrote where I've got
multiple threads doing the same thing in their own loop instead of the
single thread doing one customer in a loop.

Problem I am having is that in order to make the main thread start
multiples, I cannot do the wait for join right after I start, but need
to iterate through a bunch of objects, start them, get the thread they
are running on, add it to a thread_group, and then wait on all of them
from the main thread.

The compiler is complaining "error C2280: 'boost::thread::thread(const
boost::thread &)' : attempting to reference a deleted function"

I am not sure how to accomplish my goal. Please advise!

Here is my concept code listing:

______________________
SomeClass.h

#pragma once

// Boost Includes
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>

// Standard Includes
#include <atomic>

//--------------------------------------------------------------------------------------------------
class SomeClass
{
public:

typedef boost::shared_ptr<SomeClass> Shared_Ptr;

static boost::thread Start(SomeClass & someClass);

SomeClass();
~SomeClass();

void Stop();

protected:

void ThreadProcedure();

static size_t m_count;
size_t m_id;
boost::thread m_thread; // Worker thread this
object will run on
boost::function<void()> m_threadProcedure; // Function object that
is the main loop for the thread
std::atomic<bool> m_stopping; // Flag to exit loop in
thread
};

//--------------------------------------------------------------------------------------------------

__________________
SomeClass.cpp

#include "SomeClass.h"

// Boost Includes
#include <boost/thread.hpp>

// Standard Includes
#include <iostream>

//--------------------------------------------------------------------------------------------------
size_t SomeClass::m_count = 0;

//--------------------------------------------------------------------------------------------------
boost::thread SomeClass::Start(SomeClass & someClass)
{
// Start the thread and wait for it to exit
someClass.m_thread.swap(boost::thread(someClass.m_threadProcedure));

// Return the thread we started, so that the caller may wait on it
to join along with any other
// threads the started from other instances of this class.
return someClass.m_thread;
}

//--------------------------------------------------------------------------------------------------
void SomeClass::Stop()
{
// Flags the loop in the thread procedure to exit
m_stopping = false;
}

//--------------------------------------------------------------------------------------------------
SomeClass::SomeClass()
:
m_id (m_count++),
m_thread (),
// Derived classes should override this behavior by binding their
own thread procedure in their constructor
m_threadProcedure(boost::bind(&SomeClass::ThreadProcedure, this)),
m_stopping (false)
{
std::cout << "SomeClass #" << m_id << " constructor has been
called." << std::endl;
}

//--------------------------------------------------------------------------------------------------
SomeClass::~SomeClass()
{
std::cout << "SomeClass #" << m_id << " deconstructor has been
called." << std::endl;

// If the thread is still running, interupt it and wait for it to join
m_thread.interrupt();
m_thread.join();
}

//--------------------------------------------------------------------------------------------------
void SomeClass::ThreadProcedure()
{
std::cout << "SomeClass #" << m_id << " thread procedure has been
called." << std::endl;

// Will go forever until interupted
try
{
while(true)
{
boost::this_thread::sleep_for(boost::chrono::seconds(60));
std::cout << "SomeClass #" << m_id << " tick..." << std::endl;
}
}
catch(boost::thread_interrupted)
{
std::cout << "SomeClass #" << m_id << " interupted. Exiting
thread." << std::endl;
}
}

//--------------------------------------------------------------------------------------------------

___________
main.cpp


// Project Includes
#include "SomeClass.h"

// Shared Library
#include "Exception.h"
#include "PerformanceTimer.h"

// Boost Includes
#include <boost/thread/thread.hpp>

// Standard Library
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>

#include <signal.h>

//--------------------------------------------------------------------------------------------------
// TODO - Only global for concept testing, later we'll wrap all this in
a class
std::vector<SomeClass::Shared_Ptr> g_workerObjects;
boost::thread_group g_workerThreads;

//--------------------------------------------------------------------------------------------------
void SignalHandler(int signal)
{
// Signal worker thread to exit
std::cout << "Halt signal caught. Exiting all threads..." << std::endl;
g_workerThreads.interrupt_all();
}

//--------------------------------------------------------------------------------------------------
int main()
{
// Register a handler for the ctrl-z signal
signal(SIGINT, SignalHandler);

// Create the workers and start thier threads
for(size_t index = 0; index < 10; ++index)
{
SomeClass::Shared_Ptr worker(new SomeClass());
g_workerObjects.push_back(worker);

g_workerThreads.add_thread(&(SomeClass::Start(*worker)));
}

// Wait for the threads to exit
g_workerThreads.join_all();


// Done
return 0;
}






--
I have chosen to troll filter/ignore all subthreads containing the
words: "Rick C. Hodgins", "Flibble", and "Islam"
So, I won't be able to see or respond to any such messages
---

Ian Collins

unread,
Aug 24, 2015, 6:05:45 PM8/24/15
to
Christopher Pisz wrote:
>
> I am trying to write some concept code for starting a collection of
> threads from the main thread that will each be somewhat encapsulated by
> a class.
>
> Basically, I want to move that into a service I wrote where I've got
> multiple threads doing the same thing in their own loop instead of the
> single thread doing one customer in a loop.
>
> Problem I am having is that in order to make the main thread start
> multiples, I cannot do the wait for join right after I start, but need
> to iterate through a bunch of objects, start them, get the thread they
> are running on, add it to a thread_group, and then wait on all of them
> from the main thread.
>
> The compiler is complaining "error C2280: 'boost::thread::thread(const
> boost::thread &)' : attempting to reference a deleted function"

Somewhere you are attempting top copy assign a thread, you can't do that.

> I am not sure how to accomplish my goal. Please advise!
>
> Here is my concept code listing:
>
> ______________________
> SomeClass.h
>
> #pragma once
>
> // Boost Includes
> #include <boost/shared_ptr.hpp>
> #include <boost/thread.hpp>

Why mix boost and standard headers?

> // Standard Includes
> #include <atomic>

--
Ian Collins

Christopher Pisz

unread,
Aug 24, 2015, 6:14:01 PM8/24/15
to
On 8/24/2015 5:05 PM, Ian Collins wrote:
> Christopher Pisz wrote:
SNIP
>> The compiler is complaining "error C2280: 'boost::thread::thread(const
>> boost::thread &)' : attempting to reference a deleted function"
>
> Somewhere you are attempting top copy assign a thread, you can't do that.

So you cannot return a boost::thread from a method?
I am not sure why not? Why can't we?
It seems to work if I change it to return a pointer...

boost::thread * SomeClass::Start(SomeClass & someClass)
{
// Start the thread and wait for it to exit
someClass.m_thread.swap(boost::thread(someClass.m_threadProcedure));

// Return the thread we started, so that the caller may wait on it
to join along with any other
// threads the started from other instances of this class.
return &someClass.m_thread;
}

SNIP

>> // Boost Includes
>> #include <boost/shared_ptr.hpp>
>> #include <boost/thread.hpp>
>
> Why mix boost and standard headers?
>
>> // Standard Includes
>> #include <atomic>
>

Its one of those projects thats being updated from msvc 2010 to 2015. It
already uses boost for threads and posix_time, so don't want to change
too much at once.

Ian Collins

unread,
Aug 24, 2015, 6:40:02 PM8/24/15
to
Christopher Pisz wrote:
> On 8/24/2015 5:05 PM, Ian Collins wrote:
>> Christopher Pisz wrote:
> SNIP
>>> The compiler is complaining "error C2280: 'boost::thread::thread(const
>>> boost::thread &)' : attempting to reference a deleted function"
>>
>> Somewhere you are attempting top copy assign a thread, you can't do that.
>
> So you cannot return a boost::thread from a method?
> I am not sure why not? Why can't we?

You can't copy a thread, doing so doesn't make sense.

> It seems to work if I change it to return a pointer...

There you aren't doing a copy...

--
Ian Collins

Chris Vine

unread,
Aug 24, 2015, 8:17:42 PM8/24/15
to
On Mon, 24 Aug 2015 17:13:46 -0500
Christopher Pisz <nos...@notanaddress.com> wrote:
> On 8/24/2015 5:05 PM, Ian Collins wrote:
> > Christopher Pisz wrote:
> SNIP
> >> The compiler is complaining "error C2280:
> >> 'boost::thread::thread(const boost::thread &)' : attempting to
> >> reference a deleted function"
> >
> > Somewhere you are attempting top copy assign a thread, you can't do
> > that.
>
> So you cannot return a boost::thread from a method?
> I am not sure why not? Why can't we?
> It seems to work if I change it to return a pointer...

Pointers are built in types and always copiable.

However, your design is wrong. You attempt to export the thread
class member object of SomeClass by copying it - the reason given in the
comments being: "Return the thread we started, so that the caller may
wait on it to join along with any other threads the started from other
instances of this class."

This shows that you have got your ownership semantics wrong,
presumably to get around the rule (with POSIX threads and which
probably therefore applies to boost threads) that you cannot have two
different threads joining on the same thread object: the SUS states
that "the results of multiple simultaneous calls to pthread_join()
specifying the same target thread are undefined". However, your
copying doesn't work because thread objects are not copyable (copying is
semantically meaningless and impossible to achieve with POSIX threads
and probably also windows threads); and returning a pointer won't work
because it subverts your original intention of making a copy to avoid
violating the aforementioned requirement. Usually, if you want multiple
threads to wait for a condition such as thread termination you need to
use a condition variable.

(Although you do not use C++ threads, the same rule that applies
to POSIX threads appears to apply to std::thread, because it is a
precondition of calling join() that the thread should be joinable(),
and a thread ceases to be joinable as soon as join() or detach() returns
in a thread: this is not surprising because the standard states, albeit
non-normatively, that "These threads [std::thread threads] are intended
to map one-to-one with operating system threads".)

More generally, your approach seems like an unnecessarily convoluted way
of starting a thread.

Chris

Christopher Pisz

unread,
Aug 25, 2015, 11:30:39 AM8/25/15
to
I don't understand the why behind it being semantically wrong. I am not
trying have more than one thread in which the joins are called. I am
trying to join all the child threads in the main thread.

> However, your
> copying doesn't work because thread objects are not copyable (copying is
> semantically meaningless and impossible to achieve with POSIX threads
> and probably also windows threads);

I am not trying to "create a new thread by copying the thread object. I
am trying to "pass some thing the main thread can use to join on"

> and returning a pointer won't work
> because it subverts your original intention of making a copy to avoid
> violating the aforementioned requirement.

But it does work in my test. I'll post my edited concept code at the bottom.

> Usually, if you want multiple
> threads to wait for a condition such as thread termination you need to
> use a condition variable.

Just the main thread.

> (Although you do not use C++ threads, the same rule that applies
> to POSIX threads appears to apply to std::thread, because it is a
> precondition of calling join() that the thread should be joinable(),

It should be

> and a thread ceases to be joinable as soon as join() or detach() returns
> in a thread: this is not surprising because the standard states, albeit
> non-normatively, that "These threads [std::thread threads] are intended
> to map one-to-one with operating system threads".)
>
> More generally, your approach seems like an unnecessarily convoluted way
> of starting a thread.
>
> Chris
>

Maybe so. Garnering suggestions on how to edit it are my goal, but I
have a feeling people are misinterpreting what I am trying to do. I just
want to start multiple threads from one parent thread and have that
parent wait on them all to terminate, unless an signal is obtained, in
which case I want the main thread to signal the child threads to
terminate. I am trying to also encapsulate all the
activity/functionality that takes place on the child threads, in a
class. Maybe this concept code does a better job of it:

________
#pragma once

// Boost Includes
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>

// Standard Includes
#include <atomic>

//--------------------------------------------------------------------------------------------------
class SomeClass
{
public:

typedef boost::shared_ptr<SomeClass> Shared_Ptr;

static void Start(SomeClass & someClass);

SomeClass();
~SomeClass();

void Stop();
void Join();

protected:

void ThreadProcedure();

static size_t m_count;
size_t m_id;
boost::thread m_thread; // Worker thread this
object will run on
boost::function<void()> m_threadProcedure; // Function object that
is the main loop for the thread
std::atomic<bool> m_stopping; // Flag to exit loop in
thread
};

__________


#include "SomeClass.h"

// Boost Includes
#include <boost/thread.hpp>

// Standard Includes
#include <iostream>

//--------------------------------------------------------------------------------------------------
size_t SomeClass::m_count = 0;

//--------------------------------------------------------------------------------------------------
void SomeClass::Start(SomeClass & someClass)
{
// Start the thread
someClass.m_thread.swap(boost::thread(someClass.m_threadProcedure));
}

//--------------------------------------------------------------------------------------------------
void SomeClass::Stop()
{
// Flags the loop in the thread procedure to exit
m_stopping = true;
}

//--------------------------------------------------------------------------------------------------
void SomeClass::Join()
{
m_thread.join();
}

//--------------------------------------------------------------------------------------------------
SomeClass::SomeClass()
:
m_id (m_count++),
m_thread (),
// Derived classes should override this behavior by binding their
own thread procedure in their constructor
m_threadProcedure(boost::bind(&SomeClass::ThreadProcedure, this)),
m_stopping (false)
{
std::cout << "SomeClass #" << m_id << " constructor has been
called." << std::endl;
}

//--------------------------------------------------------------------------------------------------
SomeClass::~SomeClass()
{
std::cout << "SomeClass #" << m_id << " deconstructor has been
called." << std::endl;

// If the thread is still running, interupt it and wait for it to join
m_thread.interrupt();
m_thread.join();
}

//--------------------------------------------------------------------------------------------------
void SomeClass::ThreadProcedure()
{
std::cout << "SomeClass #" << m_id << " thread procedure has been
called." << std::endl;

// Will go forever until interupted
try
{
while(!m_stopping.load())
{
boost::this_thread::sleep_for(boost::chrono::seconds(60));
std::cout << "SomeClass #" << m_id << " tick..." << std::endl;
}
}
catch(boost::thread_interrupted)
{
std::cout << "SomeClass #" << m_id << " interupted. Exiting
thread." << std::endl;
}

std::cout << "SomeClass #" << m_id << " stopped. Exiting thread."
<< std::endl;

}

____________

// Project Includes
#include "SomeClass.h"

// Shared Library
#include "Exception.h"
#include "PerformanceTimer.h"

// Boost Includes
#include <boost/thread/thread.hpp>

// Standard Library
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>

#include <signal.h>

//--------------------------------------------------------------------------------------------------
// TODO - Only global for concept testing, later we'll wrap all this in
a class
std::vector<SomeClass::Shared_Ptr> g_workerObjects;

//--------------------------------------------------------------------------------------------------
void SignalHandler(int signal)
{
// Signal worker thread to exit
std::cout << "Halt signal caught. Exiting all threads..." << std::endl;

for(std::vector<SomeClass::Shared_Ptr>::const_iterator itWorker =
g_workerObjects.begin(); itWorker != g_workerObjects.end(); ++itWorker)
{
(*itWorker)->Stop();
}
}

//--------------------------------------------------------------------------------------------------
int main()
{
// Register a handler for the ctrl-z signal
signal(SIGINT, SignalHandler);

// Create the workers and start thier threads
for(size_t index = 0; index < 10; ++index)
{
SomeClass::Shared_Ptr worker(new SomeClass());
SomeClass::Start(*worker);
g_workerObjects.push_back(worker);
}

// Wait for the threads to exit
std::vector<SomeClass::Shared_Ptr>::const_iterator itWorker =
g_workerObjects.begin();

while(itWorker != g_workerObjects.end())
{
(*itWorker)->Join();
itWorker = g_workerObjects.erase(itWorker);
}

// Done
return 0;

Scott Lurndal

unread,
Aug 25, 2015, 2:17:02 PM8/25/15
to
Christopher Pisz <nos...@notanaddress.com> writes:
>On 8/24/2015 7:17 PM, Chris Vine wrote:

>> Usually, if you want multiple
>> threads to wait for a condition such as thread termination you need to
>> use a condition variable.
>
>Just the main thread.

My generic (pthread-based) thread class has the following
method that handles the join.

When the destructor for the class that inherits from c_thread
is called, the thread will be joined.

bool in_context(void) { return t_thread == pthread_self(); }


/*
* Terminate the thread. If called in-context, it will clear the
* running flag and exit. If called out of context, it will cancel
* the thread and join with it.
*/
void
c_thread::terminate(void)
{
int diag;

if (in_context()) {
t_running = false;
pthread_exit(NULL);
} else {
diag = pthread_cancel(t_thread);
if (diag == ESRCH) {
/* Thread already gone, nothing to do */
} else if (diag != 0) {
t_logger->log("%s Unable to cancel thread: %s\n",
t_thread_name, strerror(diag));
}

void *rval;
diag = pthread_join(t_thread, &rval);
if (diag == ESRCH) {
/* Thread already gone, nothing to do */
} else if (diag != 0) {
t_logger->log("%s Thread join failed: %s\n",
t_thread_name, strerror(diag));
}
t_running = false;
}
}


A class that needs a thread will inherit from c_thread and will
implement the virtual c_thread::run() method for the body of the
thread.

An example ::run method:

/**
* SSP Thread. This thread will coordinate ownership of a lock between
* the hosts which are connected to the SSP instance.
*/
void
c_ssp::run(void)
{

s_lock.lock();
s_running = true;
while (s_running) {
s_wait_for_work.wait(&s_lock);

if (!s_running) continue;

while (s_file->sf_waiter[s_sysnum].sf_waitlist != SSP_TABLE_SIZE) {
s_lock_notify *lnp = s_file->sf_notifiers;

lnp += s_file->sf_waiter[s_sysnum].sf_waitlist;
s_file->sf_waiter[s_sysnum].sf_waitlist = lnp->ln_offset;

c_dlist_iterator di (&s_lock_list);
while (di.next()) {
s_lock_op *lop = (s_lock_op *)di.curr();

if (lop->lo_address == lnp->ln_address) {
lop->remove();
s_client->lock_acquired(lop->lo_handle, false);
delete lop;
lnp->ln_offset = s_file->sf_freelist;
s_file->sf_freelist = lnp->ln_this;
}
}
}
}
s_terminate_wait.signal();
s_lock.unlock();
}



/**
* Destroy an SSP implementation.
*/
c_ssp::~c_ssp(void)
{
s_lock.lock();
s_running = false; // Signal ::run method to terminate
s_wait_for_work.signal(); // Awaken ::run method
s_terminate_wait.wait(&s_lock); // Wait for ::run method to exit
terminate(); // Join with thread

if (s_file != NULL) {
munmap(s_file, SSP_MAILBOX_SIZE);
s_file = NULL;
}
s_lock.unlock();
}

s_running member data item is declared volatile, of course.

Chris Vine

unread,
Aug 25, 2015, 4:35:28 PM8/25/15
to
On Tue, 25 Aug 2015 10:30:14 -0500
Christopher Pisz <nos...@notanaddress.com> wrote:
[snip]
> On 8/24/2015 7:17 PM, Chris Vine wrote:
> > (Although you do not use C++ threads, the same rule that applies
> > to POSIX threads appears to apply to std::thread, because it is a
> > precondition of calling join() that the thread should be joinable(),
>
> It should be

Not in your usage (see below). A thread ceases to be joinable as soon
as detach() is called on it, or otherwise at some unspecified time after
the thread has terminated between a call to join() being made and that
call returning. See in particular §30.3.1.5/2 and §30.3.1.5/6 of C++11.

[snip]
> Maybe so. Garnering suggestions on how to edit it are my goal, but I
> have a feeling people are misinterpreting what I am trying to do. I
> just want to start multiple threads from one parent thread and have
> that parent wait on them all to terminate, unless an signal is
> obtained, in which case I want the main thread to signal the child
> threads to terminate. I am trying to also encapsulate all the
> activity/functionality that takes place on the child threads, in a
> class. Maybe this concept code does a better job of it:

OK I can see where you are going. Having a thread handle (which is
basically what SomeClass now is) is not a bad idea, given that
std::thread is not exception safe for a joinable thread (but
boost::thread is). Having protected data members would also normally
be regarded as unusual, but presumably it is to make your m_stopping
flag visible to a worker thread function provided by a derived class,
but in that case I cannot see the point of having the polymorphic
function object m_threadProcedure - you might just as well use
inheritance with a virtual thread function, much as I try to avoid
them except for type erasure purposes.

However, on the technical level you still have undefined behaviour.
I mentioned that you cannot have more than one thread joining on
another according to the C++ and POSIX standards (and, having looked at
the boost documentation, I see also according to boost). In your code
the destructor of SomeClass calls join() on m_thread and any thread
calling your SomeClass::Join() method will do the same. By the time the
destructor of SomeClass is entered and calls join() any thread blocking
on SomeClass::join() will probably have returned leaving the thread in
already detached state and the destructor with undefined behaviour.
You say "But it does work in my test". If having non-conforming code
which happens to work on a particular compiler version with a
particular OS version is enough for the purposes of the contract with
your clients, so be it. If you are concerned with portability and
standard compliance you would use a condition variable, or remove your
SomeClass::Join() function.

(As an aside, you will get away with it on any implementation where
std::vector iterators are pointers, but manipulating iterators in a
signal handler is technically undefined behaviour because no standard
library functions are guaranteed to be asynchronous-safe except loads
and stores on lock-free atomic objects - §1.9/6 of C++11.
std::atomic<bool> is going to be lock free in practice. You might as
well use relaxed memory ordering for that usage though.)

Chris

Chris Vine

unread,
Aug 25, 2015, 4:59:16 PM8/25/15
to
On Tue, 25 Aug 2015 21:34:56 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> (As an aside, you will get away with it on any implementation where
> std::vector iterators are pointers, but manipulating iterators in a
> signal handler is technically undefined behaviour because no standard
> library functions are guaranteed to be asynchronous-safe except loads
> and stores on lock-free atomic objects - §1.9/6 of C++11.

Actually, because your main() function and your signal handler both
manipulate g_workerObjects, in your test case you will only get away
with it if the thread in which the signal handler runs is the thread in
which main() runs. To do that you would need to block signals in all
but the main thread using sigprocmask()/pthread_sigmask() or whatever
equivalent your OS offers. Otherwise you would need a mutex to protect
g_workerObjects, and mutexes are definitely not asynchronous safe.

In multi-threaded code I always use sigwait() to deal with asynchronous
signals safely.

Chris

Christopher Pisz

unread,
Aug 26, 2015, 1:06:32 PM8/26/15
to
But the destructor should only occur on the main thread. The class
doesn't enforce that, but that's the intention.


> By the time the
> destructor of SomeClass is entered and calls join() any thread blocking
> on SomeClass::join() will probably have returned

join should also only occur on the main thread. The class doesn't
enforce that, but that's the intention.

> leaving the thread in
> already detached state and the destructor with undefined behaviour.
> You say "But it does work in my test". If having non-conforming code
> which happens to work on a particular compiler version with a
> particular OS version is enough for the purposes of the contract with
> your clients, so be it. If you are concerned with portability and
> standard compliance you would use a condition variable, or remove your
> SomeClass::Join() function.

I don't see how I would go about using a condition variable to replace
the fact that the parent thread has to join its child threads. What
would be the condition I am waiting on? What would I do after the
condition is met?

Would the condition be waiting on all child threads to exit? I'm not
sure how to set that up... while the interrupt signals them to exit?


> (As an aside, you will get away with it on any implementation where
> std::vector iterators are pointers, but manipulating iterators in a
> signal handler is technically undefined behaviour because no standard
> library functions are guaranteed to be asynchronous-safe except loads
> and stores on lock-free atomic objects - §1.9/6 of C++11.
> std::atomic<bool> is going to be lock free in practice. You might as
> well use relaxed memory ordering for that usage though.)
>
> Chris
>


Chris Vine

unread,
Aug 26, 2015, 4:01:51 PM8/26/15
to
On Wed, 26 Aug 2015 12:06:11 -0500
Christopher Pisz <nos...@notanaddress.com> wrote:
[snip]
> > However, on the technical level you still have undefined behaviour.
> > I mentioned that you cannot have more than one thread joining on
> > another according to the C++ and POSIX standards (and, having
> > looked at the boost documentation, I see also according to boost).
> > In your code the destructor of SomeClass calls join() on m_thread
> > and any thread calling your SomeClass::Join() method will do the
> > same.
>
> But the destructor should only occur on the main thread. The class
> doesn't enforce that, but that's the intention.

See below.

> > By the time the
> > destructor of SomeClass is entered and calls join() any thread
> > blocking on SomeClass::join() will probably have returned
>
> join should also only occur on the main thread. The class doesn't
> enforce that, but that's the intention.

You cannot call join() in different threads, and you cannot call
join() twice in the same thread. A thread ceases to be joinable after
it has ended and at some short unspecified time before join() returns.
Calling join() on an unjoinable thread generates undefined behaviour,
whether it is a different thread from the one which previously called
join(), or the same one.

> > leaving the thread in
> > already detached state and the destructor with undefined behaviour.
> > You say "But it does work in my test". If having non-conforming
> > code which happens to work on a particular compiler version with a
> > particular OS version is enough for the purposes of the contract
> > with your clients, so be it. If you are concerned with portability
> > and standard compliance you would use a condition variable, or
> > remove your SomeClass::Join() function.
>
> I don't see how I would go about using a condition variable to
> replace the fact that the parent thread has to join its child
> threads. What would be the condition I am waiting on? What would I do
> after the condition is met?

The condition you are waiting on is the thread ending. The last thing
the thread function does (or a wrapper around the thread function does)
is to set an "ended" flag true and broadcast on the condition
variable. Waiting threads will then unwake, see from the flag that the
thread has ended and carry on doing what you want them to do in those
circumstances.

std::thread::join() is trivially replaceable by a condition variable
wait. In fact, if you can bear looking at the POSIX documentation
there is a discussion about it and why they bothered with
pthread_join() at all. C++11 threads are a direct transposition of
POSIX threads, as I am sure you have noticed.

I think from other posts of yours that you are a windows programmer, and
if so isn't that what you have to do with windows threads? Don't you
call WaitForSingleObject on the thread handle (which incorporates its
own event object). Presumably you must have done this stuff before with
windows threads, so just do the same with a condition variable.

Doing it with a condition variable has the advantage that (a) any
number of threads can block on it, and (b) any thread can test the
condition as many times as you want without undefined behaviour.

> Would the condition be waiting on all child threads to exit? I'm not
> sure how to set that up... while the interrupt signals them to exit

Using a condition variable also allows you to wait on more than one
thread to finish with only the single variable. Just set that as your
condition.

This approach, or using join(), will join on the ending of a thread
(or the threads) whether it/they end in response to your interrupt or
for some other reason. That is presumably what you want. (In any
event, as you know I am suspicious of your interrupt code: it is
difficult to get interrupts and threads to work well together.)

I may have misunderstood what you are doing again in which case I
return to my view that your code is unnecessarily convoluted.

Chris

Christopher Pisz

unread,
Aug 26, 2015, 4:38:20 PM8/26/15
to
Ok, maybe, it doesn't matter if the thread actually ended/returned as
long as the condition flipping was the last thing that happened?

I seem to recall some crash occuring because the condition was flagged
before the function actually returned, but I don't remember the details.

I will retry another example using std::thread.


> std::thread::join() is trivially replaceable by a condition variable
> wait. In fact, if you can bear looking at the POSIX documentation
> there is a discussion about it and why they bothered with
> pthread_join() at all. C++11 threads are a direct transposition of
> POSIX threads, as I am sure you have noticed.
>
> I think from other posts of yours that you are a windows programmer, and
> if so isn't that what you have to do with windows threads? Don't you
> call WaitForSingleObject on the thread handle (which incorporates its
> own event object). Presumably you must have done this stuff before with
> windows threads, so just do the same with a condition variable.

I program on Windows, but I am none too happy with the Window's API and
have managed to stay away from Windows Thread functions, since boost
1.32 or so. I can't even remember what I did before that. It's been
fairly infrequent on needed to go rearchitect some single threaded work
into multithreaded work and it is one of those things that once it's
done and it works, you don't look at it again...like calculus in college.

> Doing it with a condition variable has the advantage that (a) any
> number of threads can block on it, and (b) any thread can test the
> condition as many times as you want without undefined behaviour.
>
>> Would the condition be waiting on all child threads to exit? I'm not
>> sure how to set that up... while the interrupt signals them to exit
>
> Using a condition variable also allows you to wait on more than one
> thread to finish with only the single variable. Just set that as your
> condition.

I don't know how to set up a condition of "When all 10 threads are done,
you are flagged" with a single condition variable. Is there an example
somewhere?


> This approach, or using join(), will join on the ending of a thread
> (or the threads) whether it/they end in response to your interrupt or
> for some other reason. That is presumably what you want. (In any
> event, as you know I am suspicious of your interrupt code: it is
> difficult to get interrupts and threads to work well together.)

I am just trying to get ctrl-c, console program exit. I pretty much want
the threads to run until program shutdown. In the real world this will
be when the windows service stops.

> I may have misunderstood what you are doing again in which case I
> return to my view that your code is unnecessarily convoluted.

I just want to start some variable number of worker threads from program
startup and have them do some chore that can be defined by a derived
class, until program exit.

The only difference between my goal and the most trivial thread examples
on the internet are that I want to encapsulate the function being run by
a worker thread as a method in a class.

Such that I could make a class "file processor" that takes a filename,
and reads the file on its own thread. Or more realistically, a thread
that gets customer data, processes it, and sends it to another system
component periodically in a loop until shutdown.

Chris Vine

unread,
Aug 26, 2015, 5:07:44 PM8/26/15
to
On Wed, 26 Aug 2015 15:38:00 -0500
Christopher Pisz <nos...@notanaddress.com> wrote:
> On 8/26/2015 3:01 PM, Chris Vine wrote:
> > The condition you are waiting on is the thread ending. The last
> > thing the thread function does (or a wrapper around the thread
> > function does) is to set an "ended" flag true and broadcast on the
> > condition variable. Waiting threads will then unwake, see from the
> > flag that the thread has ended and carry on doing what you want
> > them to do in those circumstances.
>
> Ok, maybe, it doesn't matter if the thread actually ended/returned as
> long as the condition flipping was the last thing that happened?

It should not make any difference, provided setting its flag
and then broadcasting on the condition is the last thing the thread
does.

> I seem to recall some crash occuring because the condition was
> flagged before the function actually returned, but I don't remember
> the details.
>
> I will retry another example using std::thread.

It occurs to me that if you can guarantee as an invariant that all
calls to join() are made in a single thread, you could precede the call
to join() with a test of joinable(), which would avoid any undefined
behaviour. However, having such an invariant would run the risk of
making your code very brittle.

[snip]
> > Using a condition variable also allows you to wait on more than one
> > thread to finish with only the single variable. Just set that as
> > your condition.
>
> I don't know how to set up a condition of "When all 10 threads are
> done, you are flagged" with a single condition variable. Is there an
> example somewhere?

You give each thread an "end" flag and, as the condition applied to the
variable on which your waiting thread is waiting, you test that each of
the flags is set to true. If not, loop and carry on waiting. Whether
this does what you want I am not sure because I have not got a clear
enough view of the strategic objective.

Chris

seeplus

unread,
Aug 27, 2015, 7:19:27 AM8/27/15
to
On Thursday, August 27, 2015 at 6:38:20 AM UTC+10, Christopher Pisz wrote:
> >>
>
> I program on Windows, but I am none too happy with the Window's API and
> have managed to stay away from Windows Thread functions, since boost
> 1.32 or so

/aside/ Are you using Boost threads with Windows GUI?
I have been using MFC/API threads successfully to start/suspend/restart/stop,
and get the thread reporting back to the OS about what it is doing, all good.

However in the future I would like to expand this, and have more control
and knowledge of how it works.
I have not tried Boost, but have used the offshoot just::threads, produced by
the Boost threads maintainer so it should work similarly?
Does not work well for me. Cannot control the thread much,
(but it does report back OK via Win messaging).
AND it stops the Win 8-10 OS from multitasking until the thread finishes,
presumably because it is not fitting in with Win slicing and messaging.

Cannot find good Win code examples for this (or Boost really), even with ~50
bookmarks : ( so it is on hold.
Grasping at threads is .... mind numbing.
0 new messages