I am trying to write multi-thread software using the thread library in C++11. Some basic tutorials found on the internet can compile and run as expected. But my own application separated into classes always throws an exception.
Could anyone kindly tell me which part of the code should be fixed, please?
$ clang++ -std=c++11 -stdlib=libc++ BaseInterface.cxx -c -o BaseInterface.o
$ clang++ -std=c++11 -stdlib=libc++ SocketReceiver.cxx -c -o SocketReceiver.o
$ clang++ -std=c++11 -stdlib=libc++ main.cxx -c -o main.o
$ clang++ -std=c++11 -stdlib=libc++ main.o BaseInterface.o SocketReceiver.o -o main
$ gdb main
(gdb) run
Starting program: /Users/oxon/test/main
Reading symbols for shared libraries ++............................ done
libc++abi.dylib: terminate called throwing an exception
Program received signal SIGABRT, Aborted.
[Switching to process 859 thread 0x40b]
0x00007fff88df8212 in __pthread_kill ()
(gdb) bt
#0 0x00007fff88df8212 in __pthread_kill ()
#1 0x00007fff8bc85af4 in pthread_kill ()
#2 0x00007fff8bcc9dce in abort ()
#3 0x00007fff894d3a17 in abort_message ()
#4 0x00007fff894d13c6 in default_terminate ()
#5 0x00007fff89874887 in _objc_terminate ()
#6 0x00007fff894d13f5 in safe_handler_caller ()
#7 0x00007fff894d1450 in std::terminate ()
#8 0x00007fff894d25b7 in __cxa_throw ()
#9 0x00007fff8a9ba3b9 in std::__1::thread::join ()
#10 0x0000000100000cf0 in SocketReceiver::Receive ()
#11 0x0000000100000c6d in SocketReceiver::DoReceive ()
#12 0x0000000100001593 in _ZNSt3__114__thread_proxyINS_5tupleIJPFPvS2_ES2_EEEEES2_S2_ ()
#13 0x00007fff8bc84742 in _pthread_start ()
#14 0x00007fff8bc71181 in thread_start ()
This is the result on OS X 10.8. Scientific Linux 6 with GCC 4.4 also gives a similar result.
= BaseInterface.h =
#ifndef BASE_INTERFACE_H
#define BASE_INTERFACE_H
#include "SocketReceiver.h"
class BaseInterface
{
private:
SocketReceiver* fReceiver;
public:
BaseInterface();
virtual ~BaseInterface();
virtual void Close();
virtual void Open();
;
#endif
= BaseInterface.cxx =
#include <iostream>
#include <string.h>
#include <unistd.h>
#include "BaseInterface.h"
BaseInterface::BaseInterface()
{
fReceiver = new SocketReceiver(this);
}
//______________________________________________________________________________
BaseInterface::~BaseInterface()
{
Close();
delete fReceiver;
fReceiver = 0;
}
//______________________________________________________________________________
void BaseInterface::Close()
{
fReceiver->Stop();
usleep(10000);
while(fReceiver->IsRunning()){
usleep(10000);
} // while
}
//______________________________________________________________________________
void BaseInterface::Open()
{
fReceiver->Start();
}
= SocketReceiver.h =
#ifndef SOCKET_RECEIVER_H
#define SOCKET_RECEIVER_H
#include <thread>
#include <mutex>
class BaseInterface;
class SocketReceiver
{
private:
BaseInterface* fInterface;
bool fIsRunning;
std::mutex fMutex;
bool fStop;
std::thread* fThread;
public:
SocketReceiver(BaseInterface* interface = 0);
virtual ~SocketReceiver();
bool IsRunning() const {return fThread ? true : false;}
static void* DoReceive(void* arg);
void Receive();
void Start();
void Stop();
};
#endif
= SocketReceiver.cxx =
#include <iostream>
#include <thread>
#include <unistd.h>
#include "BaseInterface.h"
#include "SocketReceiver.h"
SocketReceiver::SocketReceiver(BaseInterface* interface)
{
fInterface = interface;
fStop = true;
fThread = 0;
}
//______________________________________________________________________________
SocketReceiver::~SocketReceiver()
{
}
//______________________________________________________________________________
void* SocketReceiver::DoReceive(void* arg)
{
SocketReceiver* receiver = (SocketReceiver*)arg;
receiver->Receive();
return 0;
}
//______________________________________________________________________________
void SocketReceiver::Receive()
{
while(not fStop){
fMutex.lock();
usleep(10000);
fMutex.unlock();
} // while
fThread->join();
delete fThread;
fThread = 0;
}
//______________________________________________________________________________
void SocketReceiver::Start()
{
fStop = false;
fThread = new std::thread(DoReceive, (void*)this);
}
//______________________________________________________________________________
void SocketReceiver::Stop()
{
fStop = true;
}
= main.cxx =
#include "BaseInterface.h"
int main()
{
BaseInterface interface;
interface.Open();
interface.Close();
return 0;
}
You're joining the thread in itself. Call join in the parent thread, like in SocketReceiver::Stop.
When join is called, it cleans up underlying data. And if you call it in the thread you're running then that data will be removed while the thread still needs it.
If the function run by a std::thread exits via and exception then std::terminate will be called.
std::thread::join() is specified to throw std::system_error with an error code resource_deadlock_would_occur if join() is called in the same thread, i.e. a thread cannot join itself. So your attempt to make the thread join itself causes an exception which terminates the process. A thread cannot join itself, joining is defined as "wait for the thread to complete" so obviously cannot be done by the thread itself, it would block waiting for itself to finish, which can't happen until the call to join() finishes, which can't happen until the thread finishes, which can't happen until the call to join() finishes ... can you see where this is going?
Also why are you doing this?
fThread = new std::thread(DoReceive, (void*)this);
std::thread is not pthread_create, you don't need to pass it void*, just get rid of DoReceive and call:
fThread = new std::thread(&SocketReceiver::Receive, this);
And why is your std::thread allocated with new? That's not necessary either, you can use a std::thread member and use joinable() to check if it's active or not, instead of checking if the pointer is non-null.
Related
Below code generates a TSAN error(race condition).
Is this a valid error ? or a false positive ?
Object is destroyed only after ref count becomes zero(after all other thread memory operations are visible - with atomic_thread_fence)
If I use a std::shared_ptr instead of boost::intrusive_ptr, then TSAN error disappears.
Since both threads use the object as read-only, I presume this should be safe.
If this is a valid error how do I fix it ?
gcc version - 7.3.1 boost version - 1.72.0
compile command : "g++ -ggdb -I /usr/local/boost_1_72_0 -O3 -fsanitize=thread TSan_Intr_Ptr.cpp -lpthread
"
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/detail/spinlock.hpp>
#include <boost/atomic.hpp>
#include <thread>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>
using namespace std;
struct Shared
{
mutable boost::atomic<int> refcount_;
//From https://www.boost.org/doc/libs/1_72_0/doc/html/atomic/usage_examples.html
friend void intrusive_ptr_add_ref(const Shared * x)
{
x->refcount_.fetch_add(1, boost::memory_order_relaxed);
}
friend void intrusive_ptr_release(const Shared* x)
{
if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1)
{
boost::atomic_thread_fence(boost::memory_order_acquire);
delete x;
}
}
};
vector<boost::intrusive_ptr<Shared const>> g_vec;
boost::detail::spinlock g_lock = BOOST_DETAIL_SPINLOCK_INIT;
void consumer()
{
while(true)
{
g_lock.lock();
g_vec.clear();
g_lock.unlock();
usleep(10);
}
}
int main()
{
thread thd(consumer);
while(true)
{
boost::intrusive_ptr<Shared const> p(new Shared);
g_lock.lock();
g_vec.push_back(p);
g_lock.unlock();
usleep(1);
}
return 0;
}
TSAN Error
WARNING: ThreadSanitizer: data race (pid=14513)
Write of size 8 at 0x7b0400000010 by main thread:
#0 operator delete(void*) <null> (libtsan.so.0+0x00000006fae4)
#1 intrusive_ptr_release(Shared const*) /Test/TSan_Intr_Ptr_Min.cpp:25 (a.out+0x000000401195)
#2 boost::intrusive_ptr<Shared const>::~intrusive_ptr() /boost_1_72_0/boost/smart_ptr/intrusive_ptr.hpp:98 (a.out+0x000000401195)
#3 main /x01/exch/Test/TSan_Intr_Ptr_Min.cpp:51 (a.out+0x000000401195)
Previous atomic write of size 4 at 0x7b0400000010 by thread T1:
#0 __tsan_atomic32_fetch_sub <null> (libtsan.so.0+0x00000006576f)
#1 boost::atomics::detail::gcc_atomic_operations<4ul, true>::fetch_sub(unsigned int volatile&, unsigned int, boost::memory_order) /boost_1_72_0/boost/atomic/detail/ops_gcc_atomic.hpp:116 (a.out+0x000000401481)
#2 boost::atomics::detail::base_atomic<int, int>::fetch_sub(int, boost::memory_order) volatile /usr/local/boost_1_72_0/boost/atomic/detail/atomic_template.hpp:348 (a.out+0x000000401481)
#3 intrusive_ptr_release(Shared const*) /Test/TSan_Intr_Ptr_Min.cpp:22 (a.out+0x000000401481)
...
it seems using memory_order_acq_rel resolves the issue. (May be https://www.boost.org/doc/libs/1_72_0/doc/html/atomic/usage_examples.html example is in-correct)
friend void intrusive_ptr_add_ref(const Shared * x)
{
x->refcount_.fetch_add(1, boost::memory_order_acq_rel);
}
friend void intrusive_ptr_release(const Shared* x)
{
if (x->refcount_.fetch_sub(1, boost::memory_order_acq_rel) == 1)
{
delete x;
}
}
This question already has answers here:
Difference between -pthread and -lpthread while compiling
(3 answers)
Closed 4 years ago.
I want to make use of pthread ad hence use the -lpthread flag to compile, but here's what I get:
$ g++ -lpthread pseudo_code.cpp
/tmp/cc3mPrvt.o: In function `MyThreadClass::StartInternalThread()':
pseudo_code.cpp:(.text._ZN13MyThreadClass19StartInternalThreadEv[_ZN13MyThreadClass19StartInternalThreadEv]+0x26): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status
The code I try to compile is below:
#include <pthread.h>
#include <iostream>
#include <vector>
#define OK 0
#define ERROR -1
//-- ThreadClass
class MyThreadClass
{
public:
MyThreadClass() {/* empty */}
virtual ~MyThreadClass() {/* empty */}
/** Returns true if the thread was successfully started, false if there was an error starting the thread */
bool StartInternalThread()
{
return (pthread_create(&_thread, NULL, InternalThreadEntryFunc, this) == 0);
}
/** Will not return until the internal thread has exited. */
void WaitForInternalThreadToExit()
{
(void) pthread_join(_thread, NULL);
}
protected:
/** Implement this method in your subclass with the code you want your thread to run. */
virtual void InternalThreadEntry() = 0;
private:
static void * InternalThreadEntryFunc(void * This) {
((MyThreadClass *)This)->InternalThreadEntry(); return NULL;
}
pthread_t _thread;
};
//-- /ThreadClass
//--- DUMMY DECLARATIONS BELOW TO MAKE IT COMPILE ---//
#define LOG_NS_ERROR std::cout
class test{
public:
int get_child(std::string x){return OK;};
};
test *_global;
typedef struct test_struct{} _db_transact;
class db_transact{
public:
db_transact(int,int&,int&){};
};
int _ns;
int _log_id;
//--- DUMMY DECLARATIONS ABOVE TO MAKE IT COMPILE ---//
class db_c_hndlr : public MyThreadClass{
public:
db_c_hndlr(void);
~db_c_hndlr(void);
db_transact *db_conn_get(void);
void InternalThreadEntry(void *func);
private:
int _stop;
std::vector<db_transact*> _db_pool;
};
//---------------------------------------------------------
db_c_hndlr::db_c_hndlr(void) {
}
//---------------------------------------------------------
void db_c_hndlr::InternatThreadEntry(void *func) {
while(!stop){
std::cout << "going!" << std::endl;
sleep(1);
}
}
//---------------------------------------------------------
db_c_hndlr::~db_c_hndlr() {
int i = 0;
std::vector<db_transact*>::iterator it;
for (i=0, it = _db_pool.begin();it!=_db_pool.end();it++, i++) {
if (_db_pool[i])
if (_db_pool[i]!=NULL)
delete _db_pool[i];
}
}
//---------------------------------------------------------
db_transact *db_c_hndlr::db_conn_get(void) {
db_transact *tmp;
tmp = new db_transact(_global->get_child("db_config"), _ns, _log_id);
_db_pool.push_back(tmp);
return tmp;
}
//---------------------------------------------------------
int main(void)
{
db_transact *conn=NULL;
db_c_hndlr db;
//db = new db_c_hndlr();
conn= db.db_conn_get();
return OK;
}
Probably you need to do this:
extern "C" {
#include <pthread.h>
}
That tells the compiler that this header is for a C library, and that it should not use C++ name mangling.
You also need to use -pthread instead of -lpthread, because the pthread library is special and GCC wants to explicitly know you are trying to use threads, not simply link against libpthread.
Please try to compile with the command.
g++ pseudo_code.cpp -lpthread
It makes a difference where in the command you write this option; the
linker searches and processes libraries and object files in the order
they are specified. Thus, foo.o -lz bar.o searches library z after
file foo.o but before bar.o. If bar.o refers to functions in z, those
functions may not be loaded.
It worked for me. It seems, needs to specify the library after the source file so that symbols are searched in the library.
I'm trying to use -ftrap-function flag from clang manual to catch CFI (call frame information) errors in a custom handler.
Here is a basic example generating a CFI error:
#include <stdio.h>
#include <stdlib.h>
__attribute__((used)) extern "C" void CatchCfi() {
printf("catched\n");
}
struct Foo {
Foo(const char* s) : command(s) {}
virtual ~Foo() {}
void fooStuff() { printf("fooStuff\n"); }
const char* command;
};
struct Bar {
Bar(const char* s) : name(s) {}
virtual ~Bar() {}
void barStuff() { printf("barStuff\n"); }
const char* name;
};
enum class WhichObject { FooObject, BarObject };
static void* allocator(WhichObject w, const char* arg) {
switch (w) {
case WhichObject::FooObject:
return new Foo(arg);
case WhichObject::BarObject:
return new Bar(arg);
}
}
int main(int argc, const char* argv[]) {
void* ptr = nullptr;
(void)(argc);
(void)(argv);
ptr = allocator(WhichObject::BarObject, "system(\"/bin/sh\")");
Foo* fooptr = static_cast<Foo*>(ptr);
fooptr->fooStuff();
printf("not printed when compiled with -O2\n");
return 0;
}
I build it with these CFI related clang options:
-ftrap-function=CatchCfi -fsanitize=cfi-vcall -fvisibility=hidden -fsanitize=cfi-derived-cast -fsanitize=cfi-unrelated-cast -flto=thin
When this example is built without optimization it works as I want. Output:
catched
fooStuff
not printed when compiled with -O2
The problem appear when I build it with -O2 option:
catched
Trace/breakpoint trap (core dumped)
GDB shows that the program is receiving SIGTRAP just after CatchCfi returns:
(gdb) r
Starting program: /home/romex/browser/src/out/debug/hello_cfi
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
catched
Program received signal SIGTRAP, Trace/breakpoint trap.
0x000000000020118a in ?? ()
(gdb) bt
#0 0x000000000020118a in ?? ()
#1 0x00000000002010f0 in frame_dummy ()
#2 0x00007ffff748e830 in __libc_start_main (main=0x201180 <main(int, char const**)>, argc=1, argv=0x7fffffffde18, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
stack_end=0x7fffffffde08) at ../csu/libc-start.c:291
#3 0x0000000000201029 in _start ()
Warning: the current language does not match this frame.
(gdb)
How to fix that?
I'm wondering if somebody has a success story dealing with ftrap-function flag? May be there is some specific optimization flag fixing this error?
Thanks.
I've updated your code so it works as expected. My environment does not raise SIGTRAP, thus I inserted __builtin_trap() call. As mentioned #YSC it is UB. The program can not continue after your trap function returns, you must restore the program to well known good state before SIGTRAP raised.
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
jmp_buf env;
__attribute__((used)) extern "C" void CatchCfi() {
printf("catched\n");
longjmp(env, 1);
}
struct Foo {
Foo(const char* s) : command(s) {}
virtual ~Foo() {}
void fooStuff() { printf("fooStuff\n"); }
const char* command;
};
struct Bar {
Bar(const char* s) : name(s) {}
virtual ~Bar() {}
void barStuff() { printf("barStuff\n"); }
const char* name;
};
enum class WhichObject { FooObject, BarObject };
static void* allocator(WhichObject w, const char* arg) {
switch (w) {
case WhichObject::FooObject:
return new Foo(arg);
case WhichObject::BarObject:
return new Bar(arg);
}
}
int main(int argc, const char* argv[]) {
void* ptr = nullptr;
(void)(argc);
(void)(argv);
ptr = allocator(WhichObject::BarObject, "system(\"/bin/sh\")");
int val = setjmp(env);
if (!val) {
Foo* fooptr = static_cast<Foo*>(ptr);
fooptr->fooStuff();
__builtin_trap();
}
printf("not printed when compiled with -O2\n");
return 0;
}
Since ptr is a pointer to a Bar,
Foo* fooptr = static_cast<Foo*>(ptr);
fooptr->fooStuff();
is undefined behavior and the compiler is not compelled to work as you expect him to.
I have implemented a task queue with priority from asio examples and a timer class which use this queue. Here is the code:
priority_task_queue.h
class handler_priority_queue
{
private:
class queued_handler{
private:
size_t _priority;
std::function<void()> _function;
public:
queued_handler(size_t p, std::function<void()> f): _priority(p), _function(f){}
friend bool operator<(const queued_handler& a, const queued_handler& b){
return a._priority < b._priority;
}
void operator()() {
_function();
}
};
std::priority_queue<queued_handler> _handlers;
public:
// A generic wrapper class for handlers to allow the invocation to be hooked.
template <typename Handler> class wrapped_handler
{
private:
handler_priority_queue& _queue;
size_t _priority;
Handler _handler;
public:
handler_priority_queue& queue() {return _queue;}
size_t priority() {return _priority;}
wrapped_handler(handler_priority_queue& q, size_t p, Handler h)
: _queue(q), _priority(p), _handler(h){}
template <typename ...Args>
void operator()(Args&&... args){
_handler(std::forward<Args>(args)...);
}
};
template <typename Handler> wrapped_handler<Handler> wrap(size_t priority, Handler handler){
return wrapped_handler<Handler>(*this, priority, handler);
}
void add(size_t priority, std::function<void()> function);
void execute_all();
void execute_one();
bool empty();
};
// Custom invocation hook for wrapped handlers.
template <typename Function, typename Handler>
void asio_handler_invoke(Function f, handler_priority_queue::wrapped_handler<Handler>* h){
h->queue().add(h->priority(), f);
std::cout<<"LLAMANDO AL INVOKE"<<std::endl; //BORRAR!!
}
class C_priority_task_queue{
private:
asio::io_service& _io;
handler_priority_queue _pri_queue;
public:
template <typename Handler> handler_priority_queue::wrapped_handler<Handler> wrap(int priority, Handler handler){
return _pri_queue.wrap(priority, handler);
}
explicit C_priority_task_queue(asio::io_service& io): _io(io){}
C_priority_task_queue(C_priority_task_queue const&) = delete;
C_priority_task_queue& operator =(C_priority_task_queue const&) = delete;
asio::io_service& io() {return _io;}
void run();
};
priority_task_queue.cpp
void handler_priority_queue::add(size_t priority, std::function<void()> function){
_handlers.push(queued_handler(priority, function));
}
void handler_priority_queue::execute_one(){
if(!_handlers.empty()){
queued_handler handler = _handlers.top();
handler();
_handlers.pop();
}
}
bool handler_priority_queue::empty(){
return _handlers.empty();
}
void C_priority_task_queue::run(){
while (_io.run_one())
{
_io.poll();
while(!_pri_queue.empty())
{
_io.poll();
_pri_queue.execute_one();
}
}
}
base_timer.h
class C_timer {
private:
asio::high_resolution_timer _timer;
uint8_t _timer_id;
C_priority_task_queue& _prio_queue;
void timer_handler_internal(const asio::error_code& e, uint8_t timer_id, const uint64_t sched_time);
virtual void timer_handler(const uint64_t sched_time)=0;
public:
size_t _priority;
explicit C_timer(C_priority_task_queue& prio_queue, size_t priority);
virtual ~C_timer();
void set_timer(uint64_t sched_time);
int cancel();
};
base_timer.cpp
C_timer::C_timer(C_priority_task_queue& prio_queue, size_t priority):
_timer(prio_queue.io()), _timer_id(0), _prio_queue(prio_queue), _priority(priority){}
C_timer::~C_timer(){}
void C_timer::set_timer(uint64_t sched_time){
++_timer_id;
_timer.expires_at(std::chrono::time_point<std::chrono::high_resolution_clock>(std::chrono::milliseconds(sched_time)));
_timer.async_wait(_prio_queue.wrap(_priority, std::bind(&C_timer::timer_handler_internal, this,
std::placeholders::_1/*error*/, _timer_id, sched_time)));
}
int C_timer::cancel(){
++_timer_id;
return _timer.cancel();
}
void C_timer::timer_handler_internal(const asio::error_code& e, uint8_t timer_id,
const uint64_t sched_time){
if(e==asio::error::operation_aborted || timer_id != _timer_id){
return;
}
timer_handler(sched_time);
}
test class
class C_timer_test: public C_timer{
private:
int _period;
virtual void timer_handler(const uint64_t sched_time) override{
std::cout<<"timer fired"<<std::endl;
uint64_t current_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
set_timer(current_time + _period);
}
public:
C_timer_test(C_priority_task_queue& prio_queue, int priority, int period):C_timer(prio_queue, priority), _periodo(period){}
virtual ~C_timer_test(){}
void run(uint64_t delay=0){
uint64_t time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
set_timer(time + delay);
}
};
The problem is if I execute this :
int main()
{
asio::io_service io;
C_priority_task_queue prio_queue(io);
asio::io_service::work w(io);
C_timer_test ti1(prio_queue, 0, 2000);
ti1.run();
prio_queue.run();
return 0;
}
I get a segmentation fault.
However, if I execute the following code it works fine:
int main()
{
asio::io_service io;
C_priority_task_queue prio_queue(io);
asio::high_resolution_timer _timer1(io);
asio::io_service::work w(io);
C_timer_test ti1(prio_queue, 0, 2000);
ti1.run();
prio_queue.run();
return 0;
}
The only diference between both piece of code is in the second main I have added the following line asio::high_resolution_timer _timer1(io); that I haven't use in any place.
Debugging the program I have found that the signal is raising in this line:
func_(&owner, this, ec, bytes_transferred); in file task_io_service_operation.hpp
I am using asio version 1.10.6.
Any suggestion that what could be happening?
The backtrace from gdb:
gdb ./main
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
This GDB was configured as "i686-linux-gnu".
(gdb) r
[libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x0805f0d4 in ?? ()
(gdb) backtrace
#0 0x0805f0d4 in ?? ()
#1 0x080529fb in asio::detail::task_io_service::do_run_one (this=0x805f030, lock=..., this_thread=..., ec=...) at /src/cpp/external_lib/asio/include/asio/detail/impl/task_io_service.ipp:371
#2 0x080526ce in asio::detail::task_io_service::run_one (this=0x805f030, ec=...) at /src/cpp/external_lib/asio/include/asio/detail/impl/task_io_service.ipp:169
#3 0x08052c68 in asio::io_service::run_one (this=0xbffff08c) at /src/cpp/external_lib/asio/include/asio/impl/io_service.ipp:71
#4 0x08051f32 in C_priority_task_queue::run (this=0xbffff094) at priority_task_queue.cpp:19
#5 0x08049ac3 in main () at main.cpp:46
And here there is the MakeFile:
TARGET=main
SOURCES= main.cpp base_timer.cpp priority_task_queue.cpp
SOURCE_DIR=.
INCLUDE_LIB= -L/src/cpp/libcore
INCLUDE_DIR=-I/src/cpp/external_lib/asio/include \
-I/src/cpp/libcore/include
INSTALL_DIR=.
LIB=-pthread
CXX=g++
CFLAGS=-Wall -fexceptions -fpermissive -std=c++11 -DASIO_STANDALONE
CFLAGS_DEBUG = -g3 -DDEBUG
OBJDIR_DEBUG=obj
BINDIR_DEBUG=.
OBJECTS_DEBUG:= $(addprefix $(OBJDIR_DEBUG)/,$(SOURCES:.cpp=.o))
all: debug
$(OBJDIR_DEBUG)/%.o: $(SOURCE_DIR)/%.cpp
#test -d $(OBJDIR_DEBUG) || mkdir -p $(OBJDIR_DEBUG)
$(CXX) $(CFLAGS) $(CFLAGS_DEBUG) $(INCLUDE_DIR) -c $< -o $#
debug: $(OBJECTS_DEBUG)
#test -d $(BINDIR_DEBUG) || mkdir -p $(BINDIR_DEBUG)
$(CXX) -o $(BINDIR_DEBUG)/$(TARGET) $^ $(INCLUDE_LIB) $(LIB)
UPDATE
I my investigation, I have found that if I define the base_timer members (basically asio::high_resolution_timer) initializations in the .h, the code runs ok, but if I do this in the .cpp, the code crash.
I mean,
explicit C_timer(C_priority_task_queue& prio_queue, size_t priority):
_timer(prio_queue.io()), _timer_id(0), _prio_queue(prio_queue), _priority(priority){}
in .h works, but
C_timer::C_timer(C_priority_task_queue& prio_queue, size_t priority):
_timer(prio_queue.io()), _timer_id(0), _prio_queue(prio_queue), _priority(priority){}
in .cpp fails
Except for "unnatural" juggling with chrono (timepoint or duration, pick one!) the code looks ok. I can't reproduce any failure (GCC, linux):
Live On Coliru
If adding random variables make problems appear/disappear, you should think of Undefined Behaviour, use static analysis, valgrind/purify/... and code scrutiny to find your culprit. Valgrind, ASAN and UBSAN ran clean on my PC
#sehe thanks for your effort. The diference is If I put every code into a single file, main.cpp, it runs, but if I separate into several files, the problem still there. In other hand, If I instanciate a high_resolution_timer object in the main.cpp, no matter the place (inside main(), in separate function that never is called, ...) it runs, but without it, a segmentation fault is raised.
That's great: you have found a potential source of the UB: look at the use of static variables or inline functions that are not ODR-safe. (Double check all translation units are using the same compiler flags).
Also, keep in mind UB is UNDEFINED, so like adding an unrelated _timer1 changes the apparent behaviour (without changing the source of UB) the same thing could make it appear to work.
The fact that it runs clean on my machine tells you that this would have to be a platform-specific source of UB
The problem was that I had been using the version 1.10.2 of the library and it seems there was a bug.
I have updated to the last version 1.10.6 and now it runs ok.
There is my code:
#include <string>
#include <tr1/functional>
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;
class Event
{
public:
typedef std::tr1::function<void()> Handler;
void set(Handler h)
{
m_handler = h;
}
template<typename T, typename F>
void set(T * obj, F memfn)
{
set(std::tr1::bind(memfn, obj));
}
void operator()()
{
m_handler();
}
static void fire(Event * event) throw ()
{
(*event)();
}
Handler m_handler;
};
class BuggyHandler
{
public:
BuggyHandler()
{
}
BuggyHandler(Event * b) :
bar(b)
{
bar->set(this, &BuggyHandler::HandleEvent);
}
void HandleEvent()
{
// throw std::length_error
std::string().append(std::numeric_limits<size_t>::max(), '0');
}
private:
Event * bar;
};
void get_correct_stacktrace()
{
Event bar;
BuggyHandler handler(&bar);
bar();
}
void get_incorrect_stacktrace()
{
Event bar;
BuggyHandler handler(&bar);
Event::fire(&bar);
}
int main(int argc, char **argv)
{
int opt = atoi(argv[1]);
if (opt)
get_correct_stacktrace();
else
get_incorrect_stacktrace();
}
When I call ./test 1, I can get correct stack trace from core:
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xf7d028d0 in raise () from /lib/libc.so.6
#2 0xf7d03ff3 in abort () from /lib/libc.so.6
#3 0xf7ede880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0xf7edc2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5 0xf7edc2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0xf7edc41a in __cxa_throw () from /usr/lib/libstdc++.so.6
#7 0xf7e73c6f in std::__throw_length_error () from /usr/lib/libstdc++.so.6
#8 0xf7eb9a17 in std::string::append () from /usr/lib/libstdc++.so.6
#9 0x08049b96 in BuggyHandler::HandleEvent (this=0xffc26c9c) at /home/liangxu/release/server_2.0/test/src/test.cc:54
#10 0x08049857 in get_correct_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:67
#11 0x080498e0 in main (argc=Cannot access memory at address 0x5ac6) at /home/liangxu/release/server_2.0/test/src/test.cc:81
The location of throw exception is test.cc:54
When I call ./test 0, I can get incorrect stack trace from core:
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xf7d508d0 in raise () from /lib/libc.so.6
#2 0xf7d51ff3 in abort () from /lib/libc.so.6
#3 0xf7f2c880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0xf7f2a2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5 0xf7f2a2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0xf7f2a305 in std::exception::what () from /usr/lib/libstdc++.so.6
#7 0xf7f29d98 in __cxa_call_unexpected () from /usr/lib/libstdc++.so.6
#8 0x080497eb in get_incorrect_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:30
#9 0x080498f5 in main (argc=Cannot access memory at address 0x5adf) at /home/liangxu/release/server_2.0/test/src/test.cc:83
There aren't the location of throw exception.
My compiler is "gcc (GCC) 4.1.2 20070115 (prerelease) (SUSE Linux)"
If compiled with "-fno-exceptions", both methods generated correct stack trace.
What is the reason?
Both stack traces are correct.
When you call Event::fire, the exception is thrown in HandleEvent and stack unwinding happens until it encounters the fire which has that exception specification.
If you don't know the real behaviour of exception specifications, you can read about it here : http://www.gotw.ca/publications/mill22.htm
Basically, the specification throw () does guarantee that this method does not throw by failing miserably if one of the containing calls throws. When stack unwinding tries to go out of this method, it checks the exception specification, sees that it doesn't match, and calls std::unexpected from the current location of unwiding, thus a __cxa_call_unexpected () in your stack trace just after get_incorrect_stacktrace ().
In most cases, using exception specifications is useless in C++ because the guarantee is provided at the cost of a general failure of the program is something throws and shouldn't.