Home > OS >  How do I solve this segfault on Glib::Timer::stop()?
How do I solve this segfault on Glib::Timer::stop()?

Time:05-25

bellow is the code according to execution flow and some gdb output, the most relevant functions are start_timer, stop_timer and timeout_timer from Time_Keeper and UI_Controller

u-i-controller.h

#ifndef _U_I_CONTROLLER_H_
#define _U_I_CONTROLLER_H_

#include <unistd.h>
#include <gtkmm.h>
#include <unordered_map>
#include <glibmm/datetime.h>
#include <time-keeper.h>


namespace std{
    template <>
    struct hash<Time_Keeper>
    {
        size_t operator()( Time_Keeper& t) const
        {
            return t.hasheable().hash();
        }
    };
}

class UI_Controller
{
public:
    UI_Controller(Gtk::Builder* refference,Gtk::Application * app);
    void deffine_application(Gtk::Application * app);
    void add_window_to_application(Gtk::Window * window);
protected:

private:
    Gtk::Builder * refference;
    Gtk::ApplicationWindow * content_relations;
    Gtk::Application * app;
    std::vector<Glib::RefPtr<Glib::Object>> widgets;
    std::unordered_map<int,Time_Keeper> bind_time;
    void show_window(Gtk::Window *window);
    void start_timer(Gtk::Widget * selected, int position);
    void stop_timer(int i) { (bind_time[i]).stop_timer (); };
    void restart_timer(int i) { return ;}; // to be done
    void add_timer(int i) { return ;}; //to be done
    bool timeout_timer(Gtk::Label * display,int position);
};

#endif // _U_I_CONTROLLER_H_

u-i-controller.cc

#include "u-i-controller.h"

UI_Controller::UI_Controller(Gtk::Builder * refference, Gtk::Application * app)
{
    deffine_application (app);
    this->refference = refference;
    refference->get_widget("main_window",this->content_relations);
    widgets = refference->get_objects();
    Glib::ustring widget_names = "";
    for (int i=0; i < widgets.size(); i  ){
        widget_names = widget_names dynamic_cast<Gtk::Widget*>(widgets.at(i).get())->get_name() "\n";
        if (dynamic_cast<Gtk::Buildable*>(widgets.at(i).get())->get_name() == (Glib::ustring) "start_timer"){
            //dynamic_cast<Gtk::Widget*>(widgets.at(i).get())->get_ancestor(GTK_TYPE_BOX)
            //dynamic_cast<Gtk::Button*>(widgets.at(i).get())->signal_clicked().connect(sigc::mem_fun(*this,&Controlador_UI::botao_acionado));
            dynamic_cast<Gtk::Button*>(widgets.at(i).get())->signal_clicked().connect(sigc::bind<Gtk::Widget*>(sigc::mem_fun(*this,&UI_Controller::start_timer),dynamic_cast<Gtk::Widget*>(widgets.at(i).get()),i ) );
        }
        if (dynamic_cast<Gtk::Buildable*>(widgets.at(i).get())->get_name() == (Glib::ustring) "stop_timer"){
            dynamic_cast<Gtk::Button*>(widgets.at(i).get())->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&UI_Controller::stop_timer),i ) );
        }
        if (dynamic_cast<Gtk::Buildable*>(widgets.at(i).get())->get_name() == (Glib::ustring) "restart_timer"){
            dynamic_cast<Gtk::Button*>(widgets.at(i).get())->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&UI_Controller::restart_timer),i ) );
        }
        if (dynamic_cast<Gtk::Buildable*>(widgets.at(i).get())->get_name() == (Glib::ustring) "add_timer"){
            dynamic_cast<Gtk::Button*>(widgets.at(i).get())->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&UI_Controller::add_timer),i ) );
        }
    }
    app->run();
}

void UI_Controller::deffine_application(Gtk::Application * app)
{
    this->app = app;
}

void UI_Controller::add_window_to_application (Gtk::Window * window)
{
    app->add_window(*window);
}

void UI_Controller::show_window(Gtk::Window * window)
{
    add_window_to_application(window);
    window->show();
    window->show_all_children();
}

void UI_Controller::start_timer(Gtk::Widget * selected, int position){
    if (bind_time.find(position) == bind_time.end() ){
        bind_time [position] = *(new Time_Keeper());
        (bind_time [position]).start_timer ();
    }
    Gtk::Label * display;
    refference->get_widget("timer_display",display);

    // both 2 variables bellow yet to be managed by this class

    sigc::slot<bool()> my_slot = sigc::bind(sigc::mem_fun(*this,
              &UI_Controller::timeout_timer), display, position);
    auto conn = Glib::signal_timeout().connect(my_slot, 100);
}

bool UI_Controller::timeout_timer(Gtk::Label * display,int position){
    if ( ((bind_time [position]).get_active()) ) display->set_text((bind_time [position]).display_timer ());
    return (bind_time [position]).get_active();
}

reduced Time_Keeper class

#ifndef _TIME_KEEPER_H_
#define _TIME_KEEPER_H_

#include <glibmm/datetime.h>
#include<memory>
#include <glibmm/timer.h>

class Time_Keeper
{
public:
    void start_timer();
    void stop_timer();
    // I would rather use the constructor, but I need a default one in order \
    to use the unordered list
    Glib::DateTime hasheable() { return Glib::DateTime::create_now_local(); }
    bool get_active() { return active; };
protected:

private:
    std::shared_ptr<Glib::Timer> timer;
    bool active = false;
};

#endif // _TIME_KEEPER_H_
#include "time-keeper.h"

void Time_Keeper::start_timer(){
    active = true;
    timer = std::shared_ptr<Glib::Timer>(new Glib::Timer);
    timer.get()->start(); 
};

void Time_Keeper::stop_timer(){
    active = false;
    timer.get()->stop();
}

main

#include <gtkmm.h>
#include <iostream>

#include "config.h"


#ifdef ENABLE_NLS
#  include <libintl.h>
#endif

#include "u-i-controller.h"

/* For testing propose use the local (not installed) ui file */
/* #define UI_FILE PACKAGE_DATA_DIR"/ui/time_keeper.ui" */
#define UI_FILE "src/time_keeper.ui"

Gtk::ApplicationWindow * main_win = 0;

void activate_app(Gtk::Application * app)
{
    app->add_window(*main_win);
    main_win->show();
    main_win->show_all_children ();
}

int
main (int argc, char *argv[])
{
    
    auto app = Gtk::Application::create(argc,argv,"org.gtkmm.time_keeper");

    //Load the Glade file and instiate its widgets:
    Glib::RefPtr<Gtk::Builder> builder;
    try
    {
        builder = Gtk::Builder::create_from_file(UI_FILE);
    }
    catch (const Glib::FileError & ex)
    {
        std::cerr << ex.what() << std::endl;
        return 1;
    }
    
    builder->get_widget("main_window", main_win);

    if (main_win)
    {
        app->signal_startup().connect(sigc::bind<Gtk::Application*>(sigc::ptr_fun(&activate_app), app.get() ) );
        UI_Controller * controller = new UI_Controller(builder.get(),app.get());
    }
}

time_keeper.ui

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk " version="3.24"/>
  <object  id="main_window">
    <property name="can-focus">False</property>
    <child>
      <object >
        <property name="visible">True</property>
        <property name="can-focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">13</property>
        <property name="homogeneous">True</property>
        <child>
          <object >
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object >
                <property name="visible">True</property>
                <property name="can-focus">False</property>
                <property name="label" translatable="yes">current activity time</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object >
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="shadow-type">in</property>
                <child>
                  <object >
                    <property name="visible">True</property>
                    <property name="can-focus">False</property>
                    <child>
                      <!-- n-columns=4 n-rows=3 -->
                      <object  id="timer_grid">
                        <property name="visible">True</property>
                        <property name="can-focus">False</property>
                        <child>
                          <object >
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="hexpand">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">0</property>
                            <property name="top-attach">0</property>
                            <property name="width">3</property>
                          </packing>
                        </child>
                        <child>
                          <object  id="start_timer">
                            <property name="label" translatable="yes">Start</property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">0</property>
                            <property name="top-attach">2</property>
                          </packing>
                        </child>
                        <child>
                          <object  id="stop_timer">
                            <property name="label" translatable="yes">Stop</property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">1</property>
                            <property name="top-attach">2</property>
                          </packing>
                        </child>
                        <child>
                          <object  id="restart_timer">
                            <property name="label" translatable="yes">Restart</property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">2</property>
                            <property name="top-attach">2</property>
                          </packing>
                        </child>
                        <child>
                          <object  id="add_timer">
                            <property name="label" translatable="yes"> </property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">3</property>
                            <property name="top-attach">0</property>
                            <property name="height">3</property>
                          </packing>
                        </child>
                        <child>
                          <object  id="timer_display">
                            <property name="visible">True</property>
                            <property name="can-focus">False</property>
                            <property name="hexpand">True</property>
                            <property name="vexpand">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">0</property>
                            <property name="top-attach">1</property>
                            <property name="width">3</property>
                          </packing>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object >
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object >
                <property name="visible">True</property>
                <property name="can-focus">False</property>
                <property name="label" translatable="yes">activity limit</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object >
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="shadow-type">in</property>
                <child>
                  <object >
                    <property name="visible">True</property>
                    <property name="can-focus">False</property>
                    <child>
                      <!-- n-columns=4 n-rows=3 -->
                      <object >
                        <property name="visible">True</property>
                        <property name="can-focus">False</property>
                        <child>
                          <object >
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="hexpand">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">0</property>
                            <property name="top-attach">0</property>
                            <property name="width">3</property>
                          </packing>
                        </child>
                        <child>
                          <object >
                            <property name="label" translatable="yes">Start</property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">0</property>
                            <property name="top-attach">2</property>
                          </packing>
                        </child>
                        <child>
                          <object >
                            <property name="label" translatable="yes">Stop</property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">1</property>
                            <property name="top-attach">2</property>
                          </packing>
                        </child>
                        <child>
                          <object >
                            <property name="label" translatable="yes">Restart</property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">2</property>
                            <property name="top-attach">2</property>
                          </packing>
                        </child>
                        <child>
                          <object >
                            <property name="label" translatable="yes"> </property>
                            <property name="visible">True</property>
                            <property name="can-focus">True</property>
                            <property name="receives-default">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">3</property>
                            <property name="top-attach">0</property>
                            <property name="height">3</property>
                          </packing>
                        </child>
                        <child>
                          <object >
                            <property name="visible">True</property>
                            <property name="can-focus">False</property>
                            <property name="hexpand">True</property>
                            <property name="vexpand">True</property>
                          </object>
                          <packing>
                            <property name="left-attach">0</property>
                            <property name="top-attach">1</property>
                            <property name="width">3</property>
                          </packing>
                        </child>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

execution stack from gdb, yellow line indicates the last executed code executiom stack from gdb

CodePudding user response:

based on NoDakker answer I found where my mistake was on

if (dynamic_cast<Gtk::Buildable*>(widgets.at(i).get())->get_name() == (Glib::ustring) "stop_timer"){
            dynamic_cast<Gtk::Button*>(widgets.at(i).get())->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&UI_Controller::stop_timer),i ) );
        }

on the vector widgets the buttons start_timer and stop_timer have different values, so I end up creating the map bind_time, on an expression inside start_timer with the parameter corresponding with the index value on vector widgets of the stored button start_timer, however when I execute stop_timer I end up trying to do operations on the Time_Keeper of the index actually corresponding to the index of stop_timer on the widgets vector, that Time_Keeper is non existent, NULL

CodePudding user response:

I am not too experienced with C , but I took your code and built the time keeper program to walk it through the start and stop actions. I too got the segmentation fault when the "Stop" button was pressed. So, I added some "cout" statements to track object pointers within the "start_timer" and "stop_timer" functions. What I found was a pointer reference was set up within the "start_timer" function for the timer object. But this pointer reference was not found when executing the complementary "stop_timer" function (it was NULL). Following was the terminal output for the start and stop execution.

Entered timer start
Time keeper timer pointer value: %p0x55dfd78fb390
Entered timer stop
Time keeper timer pointer value: %p0
Segmentation fault (core dumped)

Just for a test case, I created a test time keeper object immediately within the "main.cc" program, calling "start_timer" and "stop_timer".

Time_Keeper *test = new Time_Keeper();
std::cout << "Test time keeper address %p" << test << std::endl;
test->start_timer();
test->stop_timer();

This resulted in a proper shutdown of the timer without a segmentation fault.

Test time keeper address %p0x55dfd7847e50
Entered timer start
Time keeper timer pointer value: %p0x55dfd789e100
Entered timer stop
Time keeper pointer value: %p0x55dfd789e100

I can't say for sure, but either there needs to be a synchronization of the start timer function and the stop timer function to the same mapping iteration. As a simple workaround, I added a static integer variable to the "u-i-controller.h" file to keep track of the iteration reference when the timer was started.

static int poss = 0;

At the point of calling the "stop_timer" function, the index is set to this static integer instead of the sequential input value.

void stop_timer(int i)
{
    (bind_time[poss]).stop_timer ();
}

Then within the "u-i-controller.cc" file, this static variable is set when the timer is started.

    if (bind_time.find(position) == bind_time.end() )
    {
        poss = position;  // Save the timer iteration position.
        bind_time [position] = *(new Time_Keeper());
        (bind_time [position]).start_timer ();
    }

This isn't the most elegant way to handle this as I am sure there are probably better ways to keep things aligned. But it gets the job done for now.

This hopefully wasn't too rambling and sends you in the right direction.

Regards.

  • Related