简体   繁体   中英

pthread_create in constructor segfault

I discovered, in this little example below, if I call pthread_create in the constructor of my struct, I get a segfault randomly on the call to pthread_mutex_lock().

And sometimes the name field is empty for the first philosopher.

If I move pthread_create to a run() function after the constructor, no segfault.

It seems the call to pthread_create happens before all the members are initialized. Shouldn't the member init list of the class be completed before the call to constructor body?

Thanks for any tips!

clang version 9.0.0 (tags/RELEASE_900/final) Target: x86_64-apple-darwin17.7.0

Sincerely, George

#include <array>
#include <iostream>
#include <pthread.h>
#include <string>
#include <vector>

using namespace std;


struct chopstick
{
        pthread_mutex_t mutex;
        chopstick()
        {
                pthread_mutex_init(&mutex,nullptr);
        }
        ~chopstick()
        {
                pthread_mutex_destroy(&mutex);
        }

};

void* feed(void* data);
struct philosopher
{
        pthread_t thread;
        string name;
        unsigned mouthfuls;
        chrono::seconds sec;
        chopstick &left, &right;

        pthread_t& get_thread() { return thread; }

        philosopher(const string &s, chopstick &l, chopstick &r): name(move(s)), left(l), right(r), mouthfuls(0)
        /*
                enable below to avoid segfault
        {}
        void run()  
        */
        {
                pthread_create(&thread, nullptr, feed, this);
        };

};

void* feed(void* data)
{
        philosopher & a = *static_cast<philosopher*>(data);
        while (a.mouthfuls < 20)
        {
                pthread_mutex_lock(&a.left.mutex);
                pthread_mutex_lock(&a.right.mutex);
                cout << "Apostle " << a.name << " thread id " << pthread_self() 
                        << " acquired a chopstick at count: " << a.mouthfuls << endl;
                ++a.mouthfuls;
                pthread_mutex_unlock(&a.right.mutex);
                pthread_mutex_unlock(&a.left.mutex);
        }
        return nullptr;
}

int main (int argc, char const * argv[])
{
        array<string, 12> names {"John", "Thaddeus", "Simon Peter", "James", "Judhas", "Bartholomew", "Matthew", "Philip", "Simon Zealot", "Thomas", "Andrew", "James the Lesser" };
        array<chopstick,names.size()> sticks;
        vector<philosopher> philosophers;

        for (int i=0; i+1<names.size(); ++i)
                philosophers.emplace_back( names[i],sticks[i],sticks[i+1] );
        philosophers.emplace_back(names[names.size()-1], sticks[0],sticks[names.size()-1]);
        //for (philosopher&  a: philosophers) a.run();  //<-- enable to avoid segfault
        for (philosopher&  a: philosophers) pthread_join(a.get_thread(), nullptr);

        return 0;

}

std::vector resizes when the code does philosophers.emplace_back() , which can move the elements in memory, so that their previous addresses become invalid and the feed() function ends up accessing objects using their old invalid addresses.

A fix would be to make the philosopher class non-copyable and non-movable, and then use std::list<philosopher> or std::forward_list<philosopher> instead of std::vector<philosopher> . std::list and std::forward_list do not move elements in memory and hence are capable of storing non-copyable and non-moveable objects.

You may also like to use std::thread instead of pthread_t , and std::mutex instead of pthread_mutex_t . The std classes are non-copyable/movable which would prevent you from making this error at compile-time. Also, the code doesn't check return values of the pthread functions for errors, whereas std::thread and std::mutex do that for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM