LINUX.ORG.RU

libnotify+gtk2+threads in c++ = Ошибка сегментирования


0

1

Всем добрый день. пишу простенькую прожку висящую в трее и сообщающую пользователям некую полузную инфу. Столкнулся с проблемой совмещения всплывающих уведомлений через libnotify с gtk_main() циклом находящихся в разных потоках, т.е. первый поток обслуживает gtk_main() для иконки в трее, а во втором потоке крутится цикл который при определенных обстоятельствах и выводит всплывающие уведомления.

Теперь о проблеме более подробно: если вызывать всплывающее уведомление сигналом из иконки (первый поток), то все отрабатывает на ура, но как только всплывающее уведомление должно вызваться во втором потоке прога вылетает с ошибкой сегментирования. Более того, если из всего кода выкинуть саму ф-цию вывода всплывающего сообщения notify_notification_show() и заменить на простой printf (что б видеть что ф-ция отрабатывает), то все отрабатывает без сбоев, но как только вызывается notify_notification_show() вылетает ошибка памяти, причем может 20 раз подряд вылетать ошибка, потом вдруг прога запустилась, цикл 2-3 раза вывел нормально всплывающее уведомление и опять вылетел с ошибкой памяти.

Гугл оказался неразговорчив на эту тему, поиск по форуму не дал ничего похожего. Может кто писал что-то подобное и знает как правильно нужно сделать? Вобщем нужна помощь в данном вопросе. Хочется разобраться именно с libnotify и не прибегать к костылям типа system(«notify-send text»)

сам код примера:

#include <pthread.h>
#include <semaphore.h>
#include <gtk/gtk.h>
#include <libnotify/notify.h>
#include <unistd.h>
#include <cstdlib>

using namespace std;

class sysTrayIcon;
class notify;

pthread_t thread1, thread2;
int threadID1, threadID2;
sem_t sem;

sysTrayIcon *picon;
notify *pnotify;

void *thread_func_notify(void *);
void *thread_func_tray(void *);


static void tray_icon_activated(GtkStatusIcon *, gpointer);
static void tray_icon_quit_prog(GtkStatusIcon *, gpointer);
static void tray_icon_popup_menu(GtkStatusIcon *, guint, guint32, gpointer);

class sysTrayIcon
{
        const gchar *icon_file;
        GtkStatusIcon *trayIcon;
        GtkWidget *menu, *menuitem;

    public:
        sysTrayIcon();
        void show();
};


class notify
{
        NotifyNotification *idNotify;

    public:
        notify();
        void show(unsigned long);
};



sysTrayIcon::sysTrayIcon()
{
    icon_file = "path_to_file.png";
}

void sysTrayIcon::show()
{
    gtk_init(NULL,NULL);

    trayIcon = gtk_status_icon_new();
    gtk_status_icon_set_from_file(trayIcon,icon_file);
    gtk_status_icon_set_visible(trayIcon,true);
    g_signal_connect(trayIcon,"activate",G_CALLBACK(tray_icon_activated),NULL);

    menu = gtk_menu_new();

    menuitem = gtk_menu_item_new_with_label("Выйти");
    g_signal_connect(G_OBJECT(menuitem),"activate",G_CALLBACK(tray_icon_quit_prog),NULL);
    gtk_menu_shell_append(GTK_MENU_SHELL(menu),menuitem);

    gtk_widget_show_all(menu);
    g_signal_connect(trayIcon,"popup-menu",G_CALLBACK(tray_icon_popup_menu),menu);

    sem_post(&sem);

    gtk_main();
}

static void tray_icon_activated(GtkStatusIcon *icon, gpointer data)
{
    pnotify->show(5);
}

static void tray_icon_quit_prog(GtkStatusIcon *icon, gpointer data)
{
    pthread_cancel(thread2);
    pthread_cancel(thread1);
    gtk_main_quit();
}

static void tray_icon_popup_menu(GtkStatusIcon *trayIcon, guint button, guint32 activate_time, gpointer menu)
{
    gtk_menu_popup(GTK_MENU(menu),NULL,NULL,NULL,trayIcon,button,activate_time);
}










notify::notify()
{
    notify_init("test init");
    idNotify = notify_notification_new("test","test",NULL,NULL);
}

void notify::show(unsigned long sec)
{
    notify_notification_update(idNotify,"test notify","body of notification",NULL);
    notify_notification_set_timeout(idNotify,sec*1000);
    printf("ok\n");
    notify_notification_show(idNotify,NULL);
}



int main(int argc, char *argv[])
{
    sysTrayIcon hicon;
    notify hnotify;
    picon = &hicon;
    pnotify = &hnotify;

    sem_init(&sem,0,0);

    if (pthread_create(&thread1,NULL,thread_func_tray,&threadID1))
    {
        printf("Dont`t creating Tray function\n");
        exit(1);
    }

    sem_wait(&sem);

    if (pthread_create(&thread2,NULL,thread_func_notify,&threadID2))
    {
        printf("Dont`t creating Notifycation function\n");
        exit(1);
    }

        if (pthread_join(thread1,NULL))
    {
        printf("Joining Tray function\n");
        exit(1);
    }

    if (pthread_join(thread2,NULL))
    {
        printf("Joining Notifycation function\n");
        exit(1);
    }

    sem_destroy(&sem);
}

void *thread_func_tray(void *data)
{
    picon->show();
}

void *thread_func_notify(void *data)
{
    sem_post(&sem);

    while (1)
    {
        pnotify->show(3);
        sleep(5);
    }
}

компилю так:

g++ test.cpp -o test -lnotify -lpthread `pkg-config --cflags --libs gtk+-2.0`
и так тоже пробовал:
g++ test.cpp -o test -D_REENTRANT -lnotify -lpthread `pkg-config --cflags --libs gtk+-2.0`



Последнее исправление: Aluminium (всего исправлений: 1)

Aluminium

причем может 20 раз подряд вылетать ошибка, потом вдруг прога запустилась, цикл 2-3 раза вывел нормально всплывающее уведомление и опять вылетел с ошибкой памяти.

Подобные случайным образом возникающие ошибки характерны для ошибок при синхронизации потоков. В GTK не силен, код не читал, но вероятно notify_notification_show() не является потокобезопасной.

m0rph ★★★★★
()
Ответ на: комментарий от m0rph

была мысль на этот счет, пытался гуглить и ничего не нашел. Но если предположить что ф-ция потоконебезопасная, то почему тогда в одном потоке по клику на иконку вызывается без проблем, а в другом потоке вылетает с ошибкой?

Aluminium
() автор топика
Ответ на: комментарий от Aluminium

Возможно особенности реализации - какие-то ресурсы принадлежат основному потоку и используются в main loop. Это если проводить аналогии с Qt.

m0rph ★★★★★
()

Юзай glib'ые функции для создания и управления тредами, а не POSIX. Тоже касается объектов синхронизации.

anonymous
()

notify_notification_show() и заменить на простой printf (что б видеть что ф-ция отрабатывает), то все отрабатывает

glibc'шная реализация printf() потокобезопасная, что будет, если notify_notification_show() обнести лочками?

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

спасибо за наводку, буду курить ман на эту тему

идея залочить поток на время выполнения нотификатора интересная и даже не так часто стала выпадать ошибка, но я заранее знал что это не выход, т.к. утечки памяти не обучали смотреть на блокирование потоков

Aluminium
() автор топика

Могу сказать вкратце, как надо делать cross-thread вызовы.

Вариант 1 (простой, но не очень правильный). В функции, которая обращается к Gtk+ из другой нити, код надо обрамить вызовами gdk_threads_enter и gdk_threads_leave - это обеспечит синхронизированность с основной нитью.

Вариант 2 (правильный). С помощью g_idle_add добавить функцию, которая будет вызвана из цикла обработки GUI-сообщений и сделает то, что надо сделать.

dmitry_vk ★★★
()
Ответ на: комментарий от anonymous

Юзай glib'ые функции для создания и управления тредами, а не POSIX. Тоже касается объектов синхронизации.

Это совсем не обязательно.

dmitry_vk ★★★
()
Ответ на: комментарий от m0rph

В GTK не силен, код не читал, но вероятно notify_notification_show() не является потокобезопасной.

Вообще, весь Gtk+ потоконебезопасен. В документации довольно подробно описано, как можно обращаться к Gtk+ из нескольких нитей: http://developer.gnome.org/gdk/stable/gdk-Threads.html#gdk-Threads.description

dmitry_vk ★★★
()
Ответ на: комментарий от dmitry_vk

по первому варианту и сделал, спасибо анониму за то что толкнул в верном направлении.

по второму варианту обязательно почитаю, спасибо за подсказку, стараюсь придерживаться правильного кода

Aluminium
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.