commit ceeb88257a9c2c8461654509c006757eaed248c4
parent 4567323fcf69165a27eff9b4bb6ff6d81a6c4696
Author: Jacki <jacki@thejackimonster.de>
Date: Sat, 20 Jul 2024 22:36:52 +0200
Replace mutex synchronization with pipes
Diffstat:
6 files changed, 278 insertions(+), 75 deletions(-)
diff --git a/src/application.c b/src/application.c
@@ -97,6 +97,8 @@ _application_init(MESSENGER_Application *app)
{
g_assert(app);
+ schedule_load_glib(&(app->ui.schedule));
+
ui_messenger_init(app, &(app->ui.messenger));
#ifndef MESSENGER_APPLICATION_NO_PORTAL
@@ -312,11 +314,11 @@ application_init(MESSENGER_Application *app,
_load_ui_stylesheets(app);
+ schedule_init(&(app->chat.schedule));
+ schedule_init(&(app->ui.schedule));
+
app->chat.status = EXIT_FAILURE;
app->chat.tid = 0;
- pipe(app->chat.pipe);
-
- pthread_mutex_init(&(app->chat.mutex), NULL);
app->quarks.widget = g_quark_from_string("messenger_widget");
app->quarks.data = g_quark_from_string("messenger_data");
@@ -441,11 +443,6 @@ application_run(MESSENGER_Application *app)
// Wait for other thread to stop properly
pthread_join(app->chat.tid, NULL);
- close(app->chat.pipe[0]);
- close(app->chat.pipe[1]);
-
- pthread_mutex_destroy(&(app->chat.mutex));
-
GList *list;
// Get rid of open requests
@@ -627,13 +624,10 @@ _application_event_call(gpointer user_data)
call = (MESSENGER_ApplicationEventCall*) user_data;
- // Locking the mutex for synchronization
- pthread_mutex_lock(&(call->app->chat.mutex));
call->event(call->app);
- pthread_mutex_unlock(&(call->app->chat.mutex));
GNUNET_free(call);
- return FALSE;
+ return TRUE;
}
void
@@ -651,7 +645,11 @@ application_call_event(MESSENGER_Application *app,
call->app = app;
call->event = event;
- util_immediate_add(G_SOURCE_FUNC(_application_event_call), call);
+ schedule_sync_run(
+ &(app->ui.schedule),
+ G_SOURCE_FUNC(_application_event_call),
+ call
+ );
}
static gboolean
@@ -664,12 +662,9 @@ _application_sync_event_call(gpointer user_data)
call = (MESSENGER_ApplicationEventCall*) user_data;
call->event(call->app);
- util_scheduler_cleanup();
-
- pthread_mutex_unlock(&(call->app->chat.mutex));
GNUNET_free(call);
- return FALSE;
+ return TRUE;
}
void
@@ -687,11 +682,11 @@ application_call_sync_event(MESSENGER_Application *app,
call->app = app;
call->event = event;
- util_scheduler_cleanup();
- util_immediate_add(G_SOURCE_FUNC(_application_sync_event_call), call);
-
- // Locking the mutex for synchronization
- pthread_mutex_lock(&(app->chat.mutex));
+ schedule_sync_run(
+ &(app->ui.schedule),
+ G_SOURCE_FUNC(_application_sync_event_call),
+ call
+ );
}
typedef struct MESSENGER_ApplicationMessageEventCall
@@ -712,13 +707,10 @@ _application_message_event_call(gpointer user_data)
call = (MESSENGER_ApplicationMessageEventCall*) user_data;
- // Locking the mutex for synchronization
- pthread_mutex_lock(&(call->app->chat.mutex));
call->event(call->app, call->context, call->message);
- pthread_mutex_unlock(&(call->app->chat.mutex));
GNUNET_free(call);
- return FALSE;
+ return TRUE;
}
void
@@ -741,7 +733,27 @@ application_call_message_event(MESSENGER_Application *app,
call->context = context;
call->message = message;
- util_immediate_add(G_SOURCE_FUNC(_application_message_event_call), call);
+ schedule_sync_run(
+ &(app->ui.schedule),
+ G_SOURCE_FUNC(_application_message_event_call),
+ call
+ );
+}
+
+static gboolean
+_application_stop_chat(gpointer user_data)
+{
+ MESSENGER_Application *app = user_data;
+
+ if (app->chat.messenger.idle)
+ GNUNET_SCHEDULER_cancel(app->chat.messenger.idle);
+
+ GNUNET_CHAT_disconnect(app->chat.messenger.handle);
+ GNUNET_CHAT_stop(app->chat.messenger.handle);
+ app->chat.messenger.handle = NULL;
+
+ GNUNET_SCHEDULER_shutdown();
+ return FALSE;
}
void
@@ -750,10 +762,14 @@ application_exit(MESSENGER_Application *app,
{
g_assert(app);
- // Forward a signal to the other thread causing it to shutdown the
- // GNUnet handles of the application.
- write(app->chat.pipe[1], &signal, sizeof(signal));
+ schedule_sync_run(
+ &(app->chat.schedule),
+ G_SOURCE_FUNC(_application_stop_chat),
+ app
+ );
+ schedule_cleanup(&(app->chat.schedule));
+ schedule_cleanup(&(app->ui.schedule));
application_pw_core_cleanup(app);
if (app->pw.context)
diff --git a/src/application.h b/src/application.h
@@ -55,6 +55,7 @@
#include "ui/send_file.h"
#include "ui/settings.h"
+#include "schedule.h"
#include "util.h"
#define MESSENGER_APPLICATION_APPNAME "GNUnet Messenger"
@@ -109,10 +110,9 @@ typedef struct MESSENGER_Application
pthread_t tid;
char *identity;
- int pipe [2];
- pthread_mutex_t mutex;
-
CHAT_MESSENGER_Handle messenger;
+
+ MESSENGER_Schedule schedule;
} chat;
struct {
@@ -139,6 +139,8 @@ typedef struct MESSENGER_Application
UI_FILES_Handle files;
UI_CONTACTS_Handle contacts;
UI_SETTINGS_Handle settings;
+
+ MESSENGER_Schedule schedule;
} ui;
struct {
diff --git a/src/chat/messenger.c b/src/chat/messenger.c
@@ -28,29 +28,6 @@
#include <gnunet/gnunet_chat_lib.h>
static void
-_chat_messenger_quit(void *cls)
-{
- g_assert(cls);
-
- MESSENGER_Application *app = (MESSENGER_Application*) cls;
-
- if (app->chat.messenger.idle)
- GNUNET_SCHEDULER_cancel(app->chat.messenger.idle);
-
- MESSENGER_ApplicationSignal signal;
- int received = read(app->chat.pipe[0], &signal, sizeof(signal));
-
- if (received < 0)
- signal = MESSENGER_FAIL;
-
- GNUNET_CHAT_disconnect(app->chat.messenger.handle);
- GNUNET_CHAT_stop(app->chat.messenger.handle);
- app->chat.messenger.handle = NULL;
-
- GNUNET_SCHEDULER_shutdown();
-}
-
-static void
_chat_messenger_idle(void *cls)
{
g_assert(cls);
@@ -76,9 +53,6 @@ _chat_messenger_message(void *cls,
MESSENGER_Application *app = (MESSENGER_Application*) cls;
- // Locking the mutex for synchronization
- pthread_mutex_lock(&(app->chat.mutex));
-
if (GNUNET_YES == GNUNET_CHAT_message_is_deleted(message))
{
application_call_message_event(
@@ -238,7 +212,6 @@ _chat_messenger_message(void *cls,
}
skip_message_handling:
- pthread_mutex_unlock(&(app->chat.mutex));
return GNUNET_YES;
}
@@ -252,6 +225,8 @@ chat_messenger_run(void *cls,
MESSENGER_Application *app = (MESSENGER_Application*) cls;
+ schedule_load_gnunet(&(app->chat.schedule));
+
// Start libgnunetchat handle
app->chat.messenger.handle = GNUNET_CHAT_start(
cfg,
@@ -259,25 +234,10 @@ chat_messenger_run(void *cls,
app
);
- // Setup pipe to handle closing the application from the other thread
- struct GNUNET_NETWORK_FDSet *fd = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_set_native(fd, app->chat.pipe[0]);
-
- app->chat.messenger.quit = GNUNET_SCHEDULER_add_select(
- GNUNET_SCHEDULER_PRIORITY_URGENT,
- GNUNET_TIME_relative_get_forever_(),
- fd,
- NULL,
- &_chat_messenger_quit,
- app
- );
-
// The idle callback seems to be necessary to not wait too long for
// other events
app->chat.messenger.idle = GNUNET_SCHEDULER_add_now(
&_chat_messenger_idle,
app
);
-
- GNUNET_NETWORK_fdset_destroy(fd);
}
diff --git a/src/meson.build b/src/meson.build
@@ -30,6 +30,7 @@ messenger_gtk_sources = files([
'file.c', 'file.h',
'request.c', 'request.h',
'resources.c', 'resources.h',
+ 'schedule.c', 'schedule.h',
'ui.c', 'ui.h',
'util.c', 'util.h',
'messenger_gtk.c',
diff --git a/src/schedule.c b/src/schedule.c
@@ -0,0 +1,165 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2024 GNUnet e.V.
+
+ GNUnet is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/*
+ * @author Tobias Frisch
+ * @file schedule.c
+ */
+
+#include "schedule.h"
+
+#include <glib-2.0/glib-unix.h>
+
+void
+schedule_init(MESSENGER_Schedule *schedule)
+{
+ g_assert(schedule);
+ memset(schedule, 0, sizeof(MESSENGER_Schedule));
+
+ g_assert(0 == pipe(schedule->push_pipe));
+ g_assert(0 == pipe(schedule->sync_pipe));
+}
+
+static void
+__schedule_setup_push_task(MESSENGER_Schedule *schedule);
+
+static void
+__schedule_pushed_task(void *cls)
+{
+ MESSENGER_Schedule *schedule = cls;
+ gboolean keep;
+ char val;
+
+ g_assert(schedule);
+ schedule->task = NULL;
+
+ g_assert(sizeof(val) == read(schedule->push_pipe[0], &val, sizeof(val)));
+
+ keep = schedule->function(schedule->data);
+
+ schedule->function = NULL;
+ schedule->data = NULL;
+
+ if (keep)
+ __schedule_setup_push_task(schedule);
+
+ g_assert(sizeof(val) == write(schedule->sync_pipe[1], &val, sizeof(val)));
+}
+
+static void
+__schedule_setup_push_task(MESSENGER_Schedule *schedule)
+{
+ struct GNUNET_NETWORK_FDSet *fd = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_set_native(fd, schedule->push_pipe[0]);
+
+ schedule->task = GNUNET_SCHEDULER_add_select(
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_TIME_relative_get_forever_(),
+ fd,
+ NULL,
+ __schedule_pushed_task,
+ schedule
+ );
+
+ GNUNET_NETWORK_fdset_destroy(fd);
+}
+
+void
+schedule_load_gnunet(MESSENGER_Schedule *schedule)
+{
+ g_assert(schedule);
+ __schedule_setup_push_task(schedule);
+}
+
+static gboolean
+__schedule_pushed(gint fd,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ MESSENGER_Schedule *schedule = user_data;
+ gboolean keep;
+ guint task;
+ char val;
+
+ g_assert(schedule);
+ task = schedule->poll;
+ schedule->poll = 0;
+
+ g_assert(sizeof(val) == read(schedule->push_pipe[0], &val, sizeof(val)));
+
+ keep = schedule->function(schedule->data);
+
+ schedule->function = NULL;
+ schedule->data = NULL;
+
+ if (keep)
+ schedule->poll = task;
+
+ g_assert(sizeof(val) == write(schedule->sync_pipe[1], &val, sizeof(val)));
+ return keep;
+}
+
+void
+schedule_load_glib(MESSENGER_Schedule *schedule)
+{
+ g_assert(schedule);
+ schedule->poll = g_unix_fd_add(
+ schedule->push_pipe[0],
+ G_IO_IN,
+ __schedule_pushed,
+ schedule
+ );
+}
+
+void
+schedule_cleanup(MESSENGER_Schedule *schedule)
+{
+ g_assert(schedule);
+
+ if (schedule->task)
+ GNUNET_SCHEDULER_cancel(schedule->task);
+ if (schedule->poll)
+ g_source_remove(schedule->poll);
+
+ close(schedule->push_pipe[0]);
+ close(schedule->push_pipe[1]);
+
+ close(schedule->sync_pipe[0]);
+ close(schedule->sync_pipe[1]);
+
+ memset(schedule, 0, sizeof(MESSENGER_Schedule));
+}
+
+void
+schedule_sync_run(MESSENGER_Schedule *schedule,
+ GSourceFunc function,
+ gpointer data)
+{
+ g_assert((schedule) && (function));
+
+ schedule->function = function;
+ schedule->data = data;
+
+ const char push = 1;
+ char sync;
+
+ g_assert(sizeof(push) == write(schedule->push_pipe[1], &push, sizeof(push)));
+ g_assert(sizeof(sync) == read(schedule->sync_pipe[0], &sync, sizeof(sync)));
+ g_assert(push == sync);
+}
diff --git a/src/schedule.h b/src/schedule.h
@@ -0,0 +1,59 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2024 GNUnet e.V.
+
+ GNUnet is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/*
+ * @author Tobias Frisch
+ * @file schedule.h
+ */
+
+#ifndef SCHEDULE_H_
+#define SCHEDULE_H_
+
+#include <glib-2.0/glib.h>
+#include <gnunet/gnunet_util_lib.h>
+
+typedef struct MESSENGER_Schedule {
+ int push_pipe [2];
+ int sync_pipe [2];
+
+ GSourceFunc function;
+ gpointer data;
+
+ struct GNUNET_SCHEDULER_Task *task;
+ guint poll;
+} MESSENGER_Schedule;
+
+void
+schedule_init(MESSENGER_Schedule *schedule);
+
+void
+schedule_load_gnunet(MESSENGER_Schedule *schedule);
+
+void
+schedule_load_glib(MESSENGER_Schedule *schedule);
+
+void
+schedule_cleanup(MESSENGER_Schedule *schedule);
+
+void
+schedule_sync_run(MESSENGER_Schedule *schedule,
+ GSourceFunc function,
+ gpointer data);
+
+#endif /* SCHEDULE_H_ */