Hi all,
I have two examples of multi threading code that crash with segmentation fault. If there is a bug in guile please fix it. If there is a problem only in my code please help me how it can be workaround.
I am using Ubuntu 14.04 and guile compiled from the repository. The examples are compiled by:
g++ demo.cc -Wall -std=c++11 -pthread -I/usr/local/test/include/guile/2.2 -L/usr/local/test/lib -lguile-2.2 -lgc
The first example has started to segfault since guile tagged v2.1.7. I have not encountered problems for v2.1.6 and older versions. However for recent version v2.2.2 it requires much more iterations to fail than v2.1.7 that fails instantly.
////////////////////////////////////////////////////////////////////////////
// Example 1.
#include <libguile.h>
#include <thread>
#include <vector>
#include <atomic>
#include <mutex>
static volatile bool hold = true;
static std::atomic_int start_cnt(0);
static std::mutex init_once_mtx;
static bool start_inited_once = false;
static bool is_inited_once = false;
static std::mutex gc_mtx;
class Eval
{
public:
Eval();
~Eval();
};
void* c_wrap_init(void*)
{
return nullptr;
}
Eval::Eval()
{
scm_with_guile(c_wrap_init, this);
}
Eval::~Eval()
{
std::lock_guard<std::mutex> lck(gc_mtx);
scm_gc();
}
void* c_wrap_init_only_once(void*)
{
is_inited_once = true;
return nullptr;
}
void init_only_once()
{
std::lock_guard<std::mutex> lck(init_once_mtx);
if (!start_inited_once)
{
start_inited_once = true;
scm_with_guile(c_wrap_init_only_once, nullptr);
}
while (!is_inited_once); // spin;
}
void threadedInit(int thread_id)
{
start_cnt ++;
while (hold) {} // spin
init_only_once();
Eval* ev = new Eval();
delete ev;
}
void test_init_race()
{
int n_threads = 120;
start_cnt = 0;
hold = true;
std::vector<std::thread> thread_pool;
for (int i = 0; i < n_threads; i++)
thread_pool.push_back(std::thread(&threadedInit, i));
while (start_cnt != n_threads) {} // spin
printf("Done creating %d threads\n", n_threads);
hold = false;
for (std::thread& t : thread_pool) t.join();
printf("Done joining %d threads\n", n_threads);
}
int main()
{
for (int k = 0; k < 10000; k++)
{
test_init_race();
printf("------------------ done iteration %d\n", k);
}
The second example requires much more iterations to crash with segfault. Sometimes hundreds, sometimes thousands, it seems to be random. You need to wait over a dozen of minutes, sometimes you need try again and restart. I have found this problem for old versions e.g. v2.0.11 as well as for recent version v2.2.2, so it seems to be an old problem.
////////////////////////////////////////////////////////////////////////////
// Example 2.
#include <libguile.h>
#include <thread>
#include <vector>
#include <atomic>
#include <mutex>
static volatile bool hold = true;
static std::atomic_int start_cnt(0);
static std::mutex init_once_mtx;
static bool start_inited_once = false;
static bool is_inited_once = false;
void* c_wrap_init_only_once(void*)
{
is_inited_once = true;
return nullptr;
}
void* c_wrap_eval(void*)
{
return nullptr;
}
void init_only_once()
{
std::lock_guard<std::mutex> lck(init_once_mtx);
if (!start_inited_once)
{
start_inited_once = true;
scm_with_guile(c_wrap_init_only_once, nullptr);
}
while (!is_inited_once); // spin;
}
void threadedInit(int thread_id)
{
start_cnt ++;
while (hold) {} // spin
init_only_once();
for (int i = 0; i < 100; ++i)
{
scm_with_guile(c_wrap_eval, nullptr);
}
}
void test_init_race()
{
int n_threads = 120;
start_cnt = 0;
hold = true;
std::vector<std::thread> thread_pool;
for (int i = 0; i < n_threads; i++)
thread_pool.push_back(std::thread(&threadedInit, i));
while (start_cnt != n_threads) {} // spin
printf("Done creating %d threads\n", n_threads);
hold = false;
for (std::thread& t : thread_pool) t.join();
printf("Done joining %d threads\n", n_threads);
}
int main()
{
for (int k = 0; k < 10000; k++)
{
test_init_race();
printf("------------------ done iteration %d\n", k);
}
}
--
Jacek