13

I have this object that contains a thread. I want the fate of the object and the fate of the thread to be one in the same. So the constructor creates a thread (with pthread_create) and the destructor performs actions to cause the thread to return in a reasonable amount of time and then joins the thread. This is working fine as long as I don't instantiate one of these objects with static storage duration. If I instantiate one of these objects at global or namespace or static class scope the program compiles fine (gcc 4.8.1) but immediately segfaults upon running. With print statements I have determined that the main thread doesn't even enter main() before the segfault. Any ideas?

Update: Also added a print statement to the first line of the constructor (so before pthread_create is called), and not even that gets printed before the segfault BUT the constructor does use an initialization list so it is possible something there is causing it?

Here is the constructor:

worker::worker(size_t buffer_size):
m_head(nullptr),m_tail(nullptr),
m_buffer_A(operator new(buffer_size)),
m_buffer_B(operator new(buffer_size)),
m_next(m_buffer_A),
m_buffer_size(buffer_size),
m_pause_gate(true),
m_worker_thread([this]()->void{ thread_func(); }),
m_running(true)
{
    print("this wont get printed b4 segfault");
    scoped_lock lock(worker_lock);
    m_worker_thread.start();
    all_workers.push_back(this);
}

And destructor:

worker::~worker()
{
    {
        scoped_lock lock(worker_lock);
        auto w=all_workers.begin();
        while(w!=all_workers.end())
        {
            if(*w==this)
            {
                break;
            }
            ++w;
        }
        all_workers.erase(w);
    }

    {
        scoped_lock lock(m_lock);
        m_running=false;
    }

    m_sem.release();
    m_pause_gate.open();

    m_worker_thread.join();

    operator delete(m_buffer_A);
    operator delete(m_buffer_B);
}

Update 2:

Okay I figured it out. My print function is atomic and likewise protects cout with an extern namespace scope mutex defined elsewhere. I changed to just plain cout and it printed at the beginning of the ctor. Apparently none of these static storage duration mutexes are getting initialized before things are trying to access them. So yeah it is probably Casey's answer.

I'm just not going to bother with complex objects and static storage duration. It's no big deal anyway.

Mike
  • 400
  • 2
  • 10
  • 1
    This sounds bizarre and dangerous (so I'm not surprised that it's causing problems), however I've never heard of anything like it so I can't say it's absolutely wrong. I'm curious to hear what others have to say. – aldo Feb 14 '14 at 04:59
  • global and static objects are created even before executing `main()` method. My suggestion would be to add more debug prints in constructor and destructor of your class – Karthik Kalyanasundaram Feb 14 '14 at 05:00
  • Check that the thread is not accessing anything from the object which is destroyed when the object is destroyed. Otherwise make sure that the destructor function first terminates the thread. – Nipun Talukdar Feb 14 '14 at 05:51
  • 1
    `all_workers` appears to be another global object with static storage duration. Are you sure it is initialized before your `worker` object? – Casey Feb 14 '14 at 05:56
  • @Casey it sure is. And nope, is there even any way to be sure? What I do know is that it segfaults before the print statement though. – Mike Feb 14 '14 at 06:01
  • Always use the unbuffered `stderr` to log debug messages, or at least suffix the message with a `\n` so the output stream will be flushed upon printing the message. – alk Feb 14 '14 at 06:03
  • @Mike If they are defined in the same translation unit, they should be initialized in declaration order (barring any weirdness involving explicit template instantiation). – Casey Feb 14 '14 at 06:03

4 Answers4

8

Initialization of non-local variables is described in C++11 §3.6.2, there's a ton of scary stuff in paragraph 2 that has to do with threads:

If a program starts a thread (30.3), the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization.

I interpret "the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization" to mean that the spawned thread cannot access any variable with dynamic initialization that was not initialized before the thread was spawned without causing a data race. If that thread doesn't somehow synchronize with main, you're basically dancing through a minefield with your hands over your eyes.

I'd strongly suggest you read through and understand all of 3.6; even without threads it's a huge PITA to do much before main starts.

Casey
  • 41,449
  • 7
  • 95
  • 125
0

What happens before entering main will be platform specific, but here is a link on how main() executes on Linux

http://linuxgazette.net/84/hawk.html

The useful snipet is

__libc_start_main initializes necessary stuffs, especially C library(such as malloc) and thread environment and calls our main.

For more information look up __libc_start_main

Not sure how this behaves on Windows, but it seems like any standard C library call before entering main is not a good idea

thisisdog
  • 908
  • 1
  • 5
  • 13
0

There may be many ways to do that. See the snippet below where the constructor of class A called before main because we have declared an object of class A at global scope: (I have expanded the example to demonstrate how a thread can be created before main executes)

#include <iostream>
#include <stdlib.h>
#include <pthread.h>
using namespace std;

void *fun(void *x)
{
    while (true) {
        cout << "Thread\n";
        sleep(2);
    }
}

pthread_t t_id;
class A
{
    public: 
        A() 
        { 
            cout << "Hello before main \n " ;
            pthread_create(&t_id, 0, fun, 0);
            sleep(6);
        }
};

A a;
int main()
{
    cout << "I am main\n";
    sleep(40);
    return 0;
}
Nipun Talukdar
  • 4,975
  • 6
  • 30
  • 42
0

I found this question after I posted my own question about threads. Reviewing my question might be helpful to others. I found that when I allocated an object creating a thread in the constructor at global scope I got strange behavior, but if I moved the objection creation just inside main() things worked as I expected. That seems to be consistent with comments on this question.

amjjam
  • 1