Project

General

Profile

Segmentation Fault when using C callback API

Added by Tobias N almost 9 years ago

Hello everyone

I am relatively new to Wt and C overall and I have reached a dead end for me:

I depend on a library that has a C function pointer callback API, and Wt crashes when I use that API. (Using Wt 3.3.4, which comes with my Linux distribution.)

To point out my problem, I have created a minimal example. Instead of my library, it uses a C-Style callback for SIGUSR1, which should be more readily available and suffices to reproduce the error.

The code consists of three classes:

CallbackWrapper (attempts to make a callback available to Wt via Signal/Slot-mechanism based on the information given in the Signal Slot System Description),

MyWidget (displays number of received callbacks on a Website) and

DisplayApplication, the seemingly obligatory Application class where things are being put in motion.

Expected behavior: The Website content text counts +1 for every received SIGUSR1

Actual behavior: Segmentation Fault as soon as the SIGUSR1-Signal is being sent to the Wt-Application

The example works when the callback for SIGUSR1 is replaced with a WTimer.

Any hint towards which part of my thinking/code is erroneous would be greatly appreciated.

If I forgot to mention anything crucial, please let me know.

The code:

#include <Wt/WApplication>
#include <Wt/WObject>
#include <Wt/WSignal>
#include <Wt/WContainerWidget>
#include <Wt/WText>

#include <csignal>


extern "C" {
        void interrupt_func(int signum);
}

class CallbackWrapper;
static CallbackWrapper * device;

class CallbackWrapper : public Wt::WObject {
        public:
                CallbackWrapper(Wt::WObject *parent=0)
                        : Wt::WObject(parent),
                        sig(this)
                {
                        device=this;
                        signal(SIGUSR1, interrupt_func);
                }

                Wt::Signal<>& intReceived() { return sig; }
                void interrupt() { sig.emit(); }

        private:
                Wt::Signal<> sig;
};

void interrupt_func(int signum) {
        device->interrupt();
}



class MyWidget : public Wt::WContainerWidget {
        public:
                MyWidget(Wt::WContainerWidget * parent=0)
                        : Wt::WContainerWidget(parent),
                        callback_wrapper(new CallbackWrapper()),
                        int_nr(0)
        {
                int_nr_text=new Wt::WText("interrupts: 0");
                addWidget(int_nr_text);
                addWidget(new Wt::WBreak());

                callback_wrapper->intReceived().connect(this, &MyWidget::increment_int_nr);
        }

        void increment_int_nr() {
                int_nr_text->setText(Wt::WString::fromUTF8("interrupts: {1}").arg(++int_nr));
        }

        private:
                CallbackWrapper * callback_wrapper;
                int int_nr;
                Wt::WText * int_nr_text;
};

class DisplayApplication : public Wt::WApplication {
        public:
                DisplayApplication(const Wt::WEnvironment& env)
                        : Wt::WApplication(env)
                {
        root()->addWidget(new MyWidget());
                }
};

Wt::WApplication *createApplication(const Wt::WEnvironment& env) {

        return new DisplayApplication(env);
}

int main(int argc, char *argv[]) {

        return Wt::WRun(argc, argv, &createApplication);
}

Compiled with g++ minimal_example.cpp -lwthttp -lwt -o application

Started with ./application --docroot . --http-address 0.0.0.0 --http-port 8065

The USR1-Signal is sent to the running program with kill -USR1 $(pidof "application")

minimal_example.cpp (1.58 KB) minimal_example.cpp Source code in a single file
run.sh (407 Bytes) run.sh Script that triggers error in semi-automated way: start script, open Website, wait for Signal/Crash

Replies (3)

RE: Segmentation Fault when using C callback API - Added by Wim Dumon almost 9 years ago

Hey Tobias,

At first glance, there's a number of alarming things (some of which can be easily resolved).

1. use of signals: I'm not sure if your real application uses signals, but handling signals in unix is not simple (that's an understatement). In multi-threaded applications, it only gets more complex. My advise is not to do this unless you're really familiar with signal hanling.

2. CallBackWrapper uses a global variable 'device', but reassigns that global variable each time a new CallBackWrapper is created. (you also reassign the signal handler upon each creation, which may not be a problem, but which is strange). Only the last created CallBackWraper is relevant, and when it get deleted you'll get a crash when the signal is emitted again, since the signal handler is still there but the data it uses isn't.

3. Wt signals are not thread safe. Normally, the updatelock (see WApplication documentation) takes care of proper locking, after which signals can freely be used within a session, but signal handlers don't (and afaik can't) properly lock the widget tree before accessing it.

It would be better to try this approach:

1. register the signal handler once, in main(), and make it signal a condition variable.

2. in a dedicated thread, wait for the condition variable, and use WServer::post() to inform a list of sessions that something happened.

3. let your post emit a signal in the WApplication object - you're sure that the WApplication object will exist when your posted function is invoked, but you generally can't be sure that other parts of your widget tree won't be modified by then

4. a WApplication object should register its session ID to the pool if objects to be signaled in step (2), and remove itself upon destruction.

Hope this helps,

Wim.

RE: Segmentation Fault when using C callback API - Added by Tobias N almost 9 years ago

Hello Wim,

thank you for taking the time to respond!

As for the alarming things:

  1. I only used the signal because that was a more portable way to integrate a C style callback than using my platform-dependent library. If that causes additional trouble, I will stay away from signals in my experiments.
  2. I am aware of the fact that with every Web site hit the CallbackWrapper is being instantiated in my example. I did not prevent that in order to keep the example as simple as possible, but of course that would be a problem.
  3. That sounds very much like something that I should consider. I will try to understand how Wt uses threads.

Thank you for pointing out a potential solution! I'll try that.

Apparently there's a lot left for me to learn about.

Kind regards

Tobias

RE: Segmentation Fault when using C callback API - Added by Tobias N almost 9 years ago

Hello again Wim,

after reading into WServer::post() and further searching in this forum, I can see that you have a long and rich history of explaining this particular problem to people. Thank you again for pointing me in the right direction (condition_variable & WServer::post() do the job) and your overall support here.

Kind regards

Tobias

    (1-3/3)