summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2024-06-21 22:26:13 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2024-06-21 22:26:13 (GMT)
commitd49b837c891e0490167b51c4a9811cb2e8276588 (patch)
tree9bc499f2d7077a0e715b88c8c71dca1691b6c1df
parentfde070b3d2392333fffec6ac6eff3647dacd8b80 (diff)
Restore and improve work queues.gtk4
-rw-r--r--configure.ac2
-rw-r--r--plugins/pychrysalide/Makefile.am4
-rw-r--r--plugins/pychrysalide/core.c6
-rw-r--r--plugins/pychrysalide/glibext/Makefile.am45
-rw-r--r--plugins/pychrysalide/glibext/module.c9
-rw-r--r--plugins/pychrysalide/glibext/work.c351
-rw-r--r--plugins/pychrysalide/glibext/work.h45
-rw-r--r--plugins/pychrysalide/glibext/workqueue.c586
-rw-r--r--plugins/pychrysalide/glibext/workqueue.h45
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/glibext/Makefile.am8
-rw-r--r--src/glibext/work-int.h31
-rw-r--r--src/glibext/work.c1213
-rw-r--r--src/glibext/work.h106
-rw-r--r--src/glibext/workgroup-int.h59
-rw-r--r--src/glibext/workgroup.c963
-rw-r--r--src/glibext/workgroup.h95
-rw-r--r--src/glibext/workqueue-int.h50
-rw-r--r--src/glibext/workqueue.c916
-rw-r--r--src/glibext/workqueue.h87
-rw-r--r--tests/glibext/work.py26
-rw-r--r--tests/glibext/workqueue.py49
22 files changed, 1435 insertions, 3263 deletions
diff --git a/configure.ac b/configure.ac
index 749812c..fe4a45f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -324,7 +324,7 @@ WARNING_CFLAGS="-Wall -Wimplicit -Wreturn-type -Wunused -Wswitch -Wcomment -Wuni
# _BSD_SOURCE: htobe64, be64toh
# _XOPEN_SOURCE: strdup, snprintf
# _ISOC99_SOURCE: INFINITY; NAN
-# _GNU_SOURCE: strcasestr
+# _GNU_SOURCE: asprintf, strcasestr
# GTK_DISABLE_DEPRECATED: on reste conforme au C99
#COMPLIANCE_FLAGS="-D_BSD_SOURCE -D_GNU_SOURCE -DGTK_DISABLE_DEPRECATED"
COMPLIANCE_CPPFLAGS="-D_DEFAULT_SOURCE -D_GNU_SOURCE"
diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am
index 888de8a..a6abf7f 100644
--- a/plugins/pychrysalide/Makefile.am
+++ b/plugins/pychrysalide/Makefile.am
@@ -49,7 +49,6 @@ AM_CFLAGS = $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) $(TOOLKIT_CFL
# core/libpychrysacore.la \
# debug/libpychrysadebug.la \
# format/libpychrysaformat.la \
-# glibext/libpychrysaglibext.la \
# $(GTKEXT_LIBADD) \
# $(GUI_LIBADD) \
# mangling/libpychrysamangling.la \
@@ -59,6 +58,7 @@ pychrysalide_la_LIBADD = \
analysis/libpychrysaanalysis4.la \
arch/libpychrysaarch4.la \
core/libpychrysacore.la \
+ glibext/libpychrysaglibext.la \
plugins/libpychrysaplugins.la
# -ldl: dladdr(), dlerror()
@@ -76,4 +76,4 @@ dev_HEADERS = $(pychrysalide_la_SOURCES:%c=)
#SUBDIRS = analysis arch common core debug format glibext $(GTKEXT_SUBDIR) $(GUI_SUBDIR) mangling plugins
-SUBDIRS = analysis arch core plugins
+SUBDIRS = analysis arch core glibext plugins
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index 35c0e35..4d744c7 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -60,11 +60,11 @@
#include "struct.h"
#include "analysis/module.h"
#include "arch/module.h"
+#include "glibext/module.h"
/* #include "common/module.h" */
/* #include "core/module.h" */
/* #include "debug/module.h" */
/* #include "format/module.h" */
-/* #include "glibext/module.h" */
/* #ifdef INCLUDE_GTK_SUPPORT */
/* # include "gtkext/module.h" */
/* # include "gui/module.h" */
@@ -645,12 +645,12 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
if (status) status = add_analysis_module(result);
if (status) status = add_arch_module(result);
+ if (status) status = add_glibext_module(result);
/*
if (status) status = add_common_module(result);
if (status) status = add_core_module(result);
if (status) status = add_debug_module(result);
if (status) status = add_format_module(result);
- if (status) status = add_glibext_module(result);
#ifdef INCLUDE_GTK_SUPPORT
if (status) status = add_gtkext_module(result);
if (status) status = add_gui_module(result);
@@ -668,12 +668,12 @@ PyMODINIT_FUNC PyInit_pychrysalide(void)
if (status) status = populate_analysis_module();
if (status) status = populate_arch_module();
+ if (status) status = populate_glibext_module();
/*
if (status) status = populate_common_module();
if (status) status = populate_core_module();
if (status) status = populate_debug_module();
if (status) status = populate_format_module();
- if (status) status = populate_glibext_module();
#ifdef INCLUDE_GTK_SUPPORT
if (status) status = populate_gtkext_module();
if (status) status = populate_gui_module();
diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am
index 2ed2aa5..e9c3756 100644
--- a/plugins/pychrysalide/glibext/Makefile.am
+++ b/plugins/pychrysalide/glibext/Makefile.am
@@ -1,29 +1,34 @@
noinst_LTLIBRARIES = libpychrysaglibext.la
+# libpychrysaglibext_la_SOURCES = \
+# constants.h constants.c \
+# binarycursor.h binarycursor.c \
+# binportion.h binportion.c \
+# buffercache.h buffercache.c \
+# bufferline.h bufferline.c \
+# comparison.h comparison.c \
+# configuration.h configuration.c \
+# linecursor.h linecursor.c \
+# linegen.h linegen.c \
+# module.h module.c \
+# singleton.h singleton.c
+
+# if BUILD_GTK_SUPPORT
+
+# libpychrysaglibext_la_SOURCES += \
+# bufferview.h bufferview.c \
+# loadedpanel.h loadedpanel.c \
+# named.h named.c
+
+# endif
+
libpychrysaglibext_la_SOURCES = \
- constants.h constants.c \
- binarycursor.h binarycursor.c \
- binportion.h binportion.c \
- buffercache.h buffercache.c \
- bufferline.h bufferline.c \
- comparison.h comparison.c \
- configuration.h configuration.c \
- linecursor.h linecursor.c \
- linegen.h linegen.c \
module.h module.c \
- singleton.h singleton.c
-
-if BUILD_GTK_SUPPORT
-
-libpychrysaglibext_la_SOURCES += \
- bufferview.h bufferview.c \
- loadedpanel.h loadedpanel.c \
- named.h named.c
-
-endif
+ work.h work.c \
+ workqueue.h workqueue.c
-libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
-I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c
index 3e4307a..23e7a7d 100644
--- a/plugins/pychrysalide/glibext/module.c
+++ b/plugins/pychrysalide/glibext/module.c
@@ -28,6 +28,7 @@
#include <assert.h>
+/*
#include "binarycursor.h"
#include "binportion.h"
#include "buffercache.h"
@@ -40,6 +41,9 @@
#include "loadedpanel.h"
#include "named.h"
#include "singleton.h"
+*/
+#include "work.h"
+#include "workqueue.h"
#include "../helpers.h"
@@ -106,6 +110,10 @@ bool populate_glibext_module(void)
result = true;
+ if (result) result = ensure_python_generic_work_is_registered();
+ if (result) result = ensure_python_work_queue_is_registered();
+
+ /*
if (result) result = ensure_python_singleton_candidate_is_registered();
if (result) result = ensure_python_binary_cursor_is_registered();
@@ -126,6 +134,7 @@ bool populate_glibext_module(void)
if (result) result = ensure_python_named_widget_is_registered();
#endif
if (result) result = ensure_python_singleton_factory_is_registered();
+ */
assert(result);
diff --git a/plugins/pychrysalide/glibext/work.c b/plugins/pychrysalide/glibext/work.c
new file mode 100644
index 0000000..6a15984
--- /dev/null
+++ b/plugins/pychrysalide/glibext/work.c
@@ -0,0 +1,351 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * work.c - prototypes pour l'équivalent Python du fichier "glibext/work.c"
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "work.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/work-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Initialise la classe des travaux programmés. */
+static void py_generic_work_init_gclass(GGenericWorkClass *, gpointer);
+
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK, py_generic_work_init_gclass);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_generic_work_init(PyObject *, PyObject *, PyObject *);
+
+/* Mène l'opération programmée. */
+static void py_generic_work_run_wrapper(GGenericWork *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Mène l'opération programmée. */
+static PyObject *py_generic_work_process(PyObject *, PyObject *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* unused = données non utilisées ici. *
+* *
+* Description : Initialise la classe des travaux programmés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_generic_work_init_gclass(GGenericWorkClass *class, gpointer unused)
+{
+ class->run = py_generic_work_run_wrapper;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_generic_work_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define GENERIC_WORK_DOC \
+ "GenericWork defines a basic work aimed to get processed in a" \
+ " thread set up by a pychrysalide.glibext.WorkQueue object.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " GenericWork()" \
+ "\n" \
+ "The following method has to be defined for new classes:\n" \
+ "* pychrysalide.glibext.GenericWork._run();\n" \
+ "\n" \
+ "The following signal may be emitted:\n" \
+ "* *work-completed* : when the work has been processed."
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = travail à effectuer. *
+* *
+* Description : Mène l'opération programmée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_generic_work_run_wrapper(GGenericWork *work)
+{
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *pyret; /* Bilan de consultation */
+
+#define GENERIC_WORK_RUN_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _run, "$self", \
+ METH_NOARGS, \
+ "Abstract method used to process the work.\n" \
+ "\n" \
+ "No result is expected from this function." \
+)
+
+ gstate = PyGILState_Ensure();
+
+ pyobj = pygobject_new(G_OBJECT(work));
+
+ if (has_python_method(pyobj, "_run"))
+ {
+ pyret = run_python_method(pyobj, "_run", NULL);
+
+ Py_XDECREF(pyret);
+
+ }
+
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Mène l'opération programmée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_generic_work_process(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ GGenericWork *work; /* Version native */
+
+#define GENERIC_WORK_PROCESS_METHOD PYTHON_METHOD_DEF \
+( \
+ process, "$self", \
+ METH_NOARGS, py_generic_work, \
+ "Process the work.\n" \
+ "\n" \
+ "The function does not return anything.\n" \
+ "\n" \
+ "The *work-completed* signal is emitted when the" \
+ " function terminates." \
+)
+
+ work = G_GENERIC_WORK(pygobject_get(self));
+
+ g_generic_work_process(work);
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_generic_work_type(void)
+{
+ static PyMethodDef py_generic_work_methods[] = {
+ GENERIC_WORK_RUN_WRAPPER,
+ GENERIC_WORK_PROCESS_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_generic_work_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_generic_work_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.GenericWork",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = GENERIC_WORK_DOC,
+
+ .tp_methods = py_generic_work_methods,
+ .tp_getset = py_generic_work_getseters,
+
+ .tp_init = py_generic_work_init,
+ .tp_new = py_generic_work_new,
+
+ };
+
+ return &py_generic_work_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.GenericWork'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_generic_work_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'GenericWork' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_generic_work_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en travail générique. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_generic_work(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_generic_work_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to generic work");
+ break;
+
+ case 1:
+ *((GGenericWork **)dst) = G_GENERIC_WORK(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/work.h b/plugins/pychrysalide/glibext/work.h
new file mode 100644
index 0000000..de9a936
--- /dev/null
+++ b/plugins/pychrysalide/glibext/work.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * work.h - prototypes pour l'équivalent Python du fichier "glibext/work.h"
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_generic_work_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.GenericWork'. */
+bool ensure_python_generic_work_is_registered(void);
+
+/* Tente de convertir en travail générique. */
+int convert_to_generic_work(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H */
diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c
new file mode 100644
index 0000000..d8126be
--- /dev/null
+++ b/plugins/pychrysalide/glibext/workqueue.c
@@ -0,0 +1,586 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * workqueue.c - prototypes pour l'équivalent Python du fichier "glibext/workqueue.c"
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "workqueue.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/workqueue-int.h>
+
+
+#include "work.h"
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+CREATE_DYN_CONSTRUCTOR(work_queue, G_TYPE_WORK_QUEUE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_work_queue_init(PyObject *, PyObject *, PyObject *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Constitue un nouveau groupe de travail. */
+static PyObject *py_work_queue_define_group(PyObject *, PyObject *);
+
+/* Dissout un groupe de travail existant. */
+static PyObject *py_work_queue_delete_group(PyObject *, PyObject *);
+
+/* Place une nouvelle tâche en attente. */
+static PyObject *py_work_queue_schedule(PyObject *, PyObject *);
+
+/* Détermine si un groupe est vide de toute programmation. */
+static PyObject *py_work_queue_is_empty(PyObject *, PyObject *);
+
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+static PyObject *py_work_queue_wait_for_completion(PyObject *, PyObject *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+static PyObject *py_work_queue_wake_up_waiters(PyObject *, PyObject *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define WORK_QUEUE_DOC \
+ "WorkQueue defines a basic work aimed to get processed in a" \
+ " thread setup by a pychrysalide.glibext.WorkQueue instance.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " WorkQueue()"
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Constitue un nouveau groupe de travail. *
+* *
+* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_work_queue_define_group(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ unsigned int count; /* Nombre de threads à activer */
+ int ret; /* Bilan de lecture des args. */
+ GWorkQueue *queue; /* Version native */
+ wgroup_id_t id; /* Identifiant de groupe créé */
+
+#define WORK_QUEUE_DEFINE_GROUP_METHOD PYTHON_METHOD_DEF \
+( \
+ define_group, "$self, /, count=0", \
+ METH_VARARGS, py_work_queue, \
+ "Create a new work group for a work queue.\n" \
+ "\n" \
+ "The *count* argument states the number of threads which will" \
+ " be activated for the processing. If the provided value is" \
+ " zero, then a default value based on the current hardware is" \
+ " selected.\n" \
+ "\n" \
+ "The result is a work group identifier." \
+)
+
+ count = 0;
+
+ ret = PyArg_ParseTuple(args, "|I", &count);
+ if (!ret) return NULL;
+
+ queue = G_WORK_QUEUE(pygobject_get(self));
+
+ id = g_work_queue_define_group(queue, count);
+
+ result = PyLong_FromUnsignedLong(id);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Dissout un groupe de travail existant. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_work_queue_delete_group(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ unsigned long long id; /* Identifiant de groupe ciblé */
+ int ret; /* Bilan de lecture des args. */
+ GWorkQueue *queue; /* Version native */
+
+#define WORK_QUEUE_DELETE_GROUP_METHOD PYTHON_METHOD_DEF \
+( \
+ delete_group, "$self, id", \
+ METH_VARARGS, py_work_queue, \
+ "Destroy a work group previously registered inside a queue.\n" \
+ "\n" \
+ "The *id* argument refers to a work group identifier. It" \
+ " should have been provided by a previous call to" \
+ " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \
+)
+
+ ret = PyArg_ParseTuple(args, "K", &id);
+ if (!ret) return NULL;
+
+ queue = G_WORK_QUEUE(pygobject_get(self));
+
+ g_work_queue_delete_group(queue, id);
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Place une nouvelle tâche en attente. *
+* *
+* Retour : Bilan, qui correspond à l'existence du groupe ciblé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_work_queue_schedule(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ GGenericWork *work; /* Tâche à programmer */
+ unsigned long long id; /* Identifiant de groupe ciblé */
+ int ret; /* Bilan de lecture des args. */
+ GWorkQueue *queue; /* Version native */
+ bool status; /* Bilan de programmation */
+
+#define WORK_QUEUE_SCHEDULE_METHOD PYTHON_METHOD_DEF \
+( \
+ schedule, "$self, work, id", \
+ METH_VARARGS, py_work_queue, \
+ "Schedule a work into the queue processing.\n" \
+ "\n" \
+ "The *work* argument has to point to a" \
+ " pychrysalide.glibext.GenericWork instance, and the *id*" \
+ " argument refers to a work group identifier which should have" \
+ " been provided by a previous call to" \
+ " pychrysalide.glibext.WorkQueue.g_work_queue_define_group().\n"\
+ "\n" \
+ "The result is a boolean value: *True* in case of succes (does" \
+ " the provided identifier exist?), *False* otherwise." \
+)
+
+ ret = PyArg_ParseTuple(args, "O&K", convert_to_generic_work, &work, &id);
+ if (!ret) return NULL;
+
+ queue = G_WORK_QUEUE(pygobject_get(self));
+
+ status = g_work_queue_schedule(queue, work, id);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Détermine si un groupe est vide de toute programmation. *
+* *
+* Retour : Etat du groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_work_queue_is_empty(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ unsigned long long id; /* Identifiant de groupe ciblé */
+ int ret; /* Bilan de lecture des args. */
+ GWorkQueue *queue; /* Version native */
+ bool status; /* Bilan de consultation */
+
+#define WORK_QUEUE_IS_EMPTY_METHOD PYTHON_METHOD_DEF \
+( \
+ is_empty, "$self, id", \
+ METH_VARARGS, py_work_queue, \
+ "Signal if there is some remaining work to get processed by" \
+ " the queue or not.\n" \
+ "\n" \
+ "The *id* argument refers to a work group identifier. It" \
+ " should have been provided by a previous call to" \
+ " pychrysalide.glibext.WorkQueue.g_work_queue_define_group().\n"\
+ "\n" \
+ "The result is a boolean value: *True* if the group does not" \
+ " contain any works or does not exist, *False* otherwise." \
+)
+
+ ret = PyArg_ParseTuple(args, "K", &id);
+ if (!ret) return NULL;
+
+ queue = G_WORK_QUEUE(pygobject_get(self));
+
+ status = g_work_queue_is_empty(queue, id);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Attend que toutes les tâches d'un groupe soient traitées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_work_queue_wait_for_completion(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ unsigned long long rel; /* Durée d'attente */
+ unsigned long long id; /* Identifiant de groupe ciblé */
+ int ret; /* Bilan de lecture des args. */
+ GWorkQueue *queue; /* Version native */
+ bool status; /* Bilan de l'attente menée */
+
+ /**
+ * Python appelle cette fonction, lorsqu'elle est sollicitée dans des scripts,
+ * via la fonction PyObject_Vectorcall(), qui obtient la structure de suivi
+ * du thread courant avec _PyThreadState_GET().
+ *
+ * Un commentaire associé à cette dernière fonction précise :
+ *
+ * The caller must hold the GIL.
+ *
+ * Comme le verrou GIL est posé par Python au moment de l'exécution de
+ * py_work_queue_wait_for_completion(), aucune attente bloquante ne peut
+ * alors être engagée ici : elle bloquerait la tâche _run() d'un travail
+ * réalisé en Python(), comme son déférencement via unref_ojbect() qui
+ * implique une exécution de la fonction pyg_toggle_notify() appelant
+ * PyGILState_Ensure() dans python3-gi.
+ *
+ * Aussi une attente bornée dans le temps est la seule option possible
+ * ici.
+ */
+
+#define WORK_QUEUE_WAIT_FOR_COMPLETION_METHOD PYTHON_METHOD_DEF \
+( \
+ wait_for_completion, "$self, id, /, rel=100000", \
+ METH_VARARGS, py_work_queue, \
+ "Wait until all the works from a given group have been" \
+ " processed.\n" \
+ "\n" \
+ "The *id* argument refers to a work group identifier. It" \
+ " should have been provided by a previous call to" \
+ " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \
+ " The *rel* range provide a relative time to wait (100ms by" \
+ " default).\n" \
+ "\n" \
+ "The returned value is a boolean status: *False* on a timeout," \
+ " *True* otherwise." \
+ "\n" \
+ "_Important note:_\n" \
+ "As this function is called by Python with its Global" \
+ " Interpreter Lock (GIL) held, an endless wait is impossible" \
+ " because it would keep the GIL unreleased whereas others" \
+ " threads (including those which may end the wait) may need to" \
+ " acquire it.\n" \
+ "\n" \
+ "So an unoptimized timed wait is performed here instead, and" \
+ " the returned status has to be checked by the caller in order" \
+ " to schedule another wait if needed." \
+)
+
+ rel = 100 * G_TIME_SPAN_MILLISECOND;
+
+ ret = PyArg_ParseTuple(args, "K|K", &id, &rel);
+ if (!ret) return NULL;
+
+ queue = G_WORK_QUEUE(pygobject_get(self));
+
+ status = g_work_queue_wait_timed_for_completion(queue, id, rel);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = élément d'appel à consulter. *
+* args = arguments fournis pour l'opération. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_work_queue_wake_up_waiters(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à retourner */
+ unsigned long long id; /* Identifiant de groupe ciblé */
+ int ret; /* Bilan de lecture des args. */
+ GWorkQueue *queue; /* Version native */
+
+#define WORK_QUEUE_WAKE_UP_WAITERS_METHOD PYTHON_METHOD_DEF \
+( \
+ wake_up_waiters, "$self, id", \
+ METH_VARARGS, py_work_queue, \
+ "Force a wake up of all threads waiting for processing a given" \
+ " queue work group.\n" \
+ "\n" \
+ "The *id* argument refers to a work group identifier. It" \
+ " should have been provided by a previous call to" \
+ " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \
+)
+
+ ret = PyArg_ParseTuple(args, "K", &id);
+ if (!ret) return NULL;
+
+ queue = G_WORK_QUEUE(pygobject_get(self));
+
+ g_work_queue_wake_up_waiters(queue, id);
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_work_queue_type(void)
+{
+ static PyMethodDef py_work_queue_methods[] = {
+ WORK_QUEUE_DEFINE_GROUP_METHOD,
+ WORK_QUEUE_DELETE_GROUP_METHOD,
+ WORK_QUEUE_SCHEDULE_METHOD,
+ WORK_QUEUE_IS_EMPTY_METHOD,
+ WORK_QUEUE_WAIT_FOR_COMPLETION_METHOD,
+ WORK_QUEUE_WAKE_UP_WAITERS_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_work_queue_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_work_queue_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.WorkQueue",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = WORK_QUEUE_DOC,
+
+ .tp_methods = py_work_queue_methods,
+ .tp_getset = py_work_queue_getseters,
+
+ .tp_init = py_work_queue_init,
+ .tp_new = py_work_queue_new,
+
+ };
+
+ return &py_work_queue_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.WorkQueue'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_work_queue_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'WorkQueue' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_work_queue_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_WORK_QUEUE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en gestionnaire de travaux différés. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_work_queue(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_work_queue_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to work queue");
+ break;
+
+ case 1:
+ *((GWorkQueue **)dst) = G_WORK_QUEUE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/workqueue.h b/plugins/pychrysalide/glibext/workqueue.h
new file mode 100644
index 0000000..3fe426b
--- /dev/null
+++ b/plugins/pychrysalide/glibext/workqueue.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * workqueue.h - prototypes pour l'équivalent Python du fichier "glibext/workqueue.h"
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_work_queue_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.WorkQueue'. */
+bool ensure_python_work_queue_is_registered(void);
+
+/* Tente de convertir en gestionnaire de travaux différés. */
+int convert_to_work_queue(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H */
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 5bcb13a..88d2892 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -8,7 +8,6 @@ libcore_la_SOURCES = \
demanglers.h demanglers.c \
global.h global.c \
logs.h logs.c \
- nproc.h nproc.c \
params.h params.c \
paths.h paths.c \
processors.h processors.c \
@@ -19,6 +18,7 @@ libcore_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS)
libcore4_la_SOURCES = \
logs.h logs.c \
+ nproc.h nproc.c \
paths.h paths.c
libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS)
diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am
index b5ea0f0..3649d50 100644
--- a/src/glibext/Makefile.am
+++ b/src/glibext/Makefile.am
@@ -42,7 +42,13 @@ libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS)
libglibext4_la_SOURCES = \
chrysamarshal.h chrysamarshal.c \
- helpers.h
+ helpers.h \
+ work-int.h \
+ work.h work.c \
+ workgroup-int.h \
+ workgroup.h workgroup.c \
+ workqueue-int.h \
+ workqueue.h workqueue.c
libglibext4_la_CFLAGS = $(TOOLKIT_CFLAGS)
diff --git a/src/glibext/work-int.h b/src/glibext/work-int.h
index 4f84e86..58293e5 100644
--- a/src/glibext/work-int.h
+++ b/src/glibext/work-int.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed-int.h - définitions internes pour la gestion des travaux différés
+ * work-int.h - définitions internes pour la gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,27 +21,26 @@
*/
-#ifndef _GLIBEXT_DELAYED_INT_H
-#define _GLIBEXT_DELAYED_INT_H
+#ifndef _GLIBEXT_WORK_INT_H
+#define _GLIBEXT_WORK_INT_H
-#include "delayed.h"
+#include "work.h"
-#include "notifier.h"
-#include "../common/dllist.h"
+#include <stdbool.h>
+#include "../common/dllist.h"
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
/* Traite un travail programmé. */
-typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *);
+typedef void (* run_work_fc) (GGenericWork *);
/* Travail différé (instance) */
-struct _GDelayedWork
+struct _GGenericWork
{
GObject parent; /* A laisser en premier */
@@ -54,22 +53,18 @@ struct _GDelayedWork
};
/* Travail différé (classe) */
-struct _GDelayedWorkClass
+struct _GGenericWorkClass
{
GObjectClass parent; /* A laisser en premier */
- run_task_fc run; /* Traitement externalisé */
+ run_work_fc run; /* Traitement externalisé */
/* Signaux */
- void (* work_completed) (GDelayedWork *);
+ void (* work_completed) (GGenericWork *);
};
-#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link)
-#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link)
-
-
-#endif /* _GLIBEXT_DELAYED_INT_H */
+#endif /* _GLIBEXT_WORK_INT_H */
diff --git a/src/glibext/work.c b/src/glibext/work.c
index 6b5ac35..218fcf8 100644
--- a/src/glibext/work.c
+++ b/src/glibext/work.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed.c - gestion des travaux différés
+ * work.c - gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,178 +21,29 @@
*/
-#include "delayed.h"
+#include "work.h"
-#include <assert.h>
-#include <inttypes.h>
-#include <malloc.h>
-#include <stdio.h>
-#include <string.h>
+#include "work-int.h"
-#include "delayed-int.h"
-#include "../core/nproc.h"
-#ifdef INCLUDE_GTK_SUPPORT
-# include "../gui/core/global.h"
-#endif
-
-
-
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-
/* Initialise la classe des travaux différés. */
-static void g_delayed_work_class_init(GDelayedWorkClass *);
+static void g_generic_work_class_init(GGenericWorkClass *);
/* Initialise une instance de travail différé. */
-static void g_delayed_work_init(GDelayedWork *);
+static void g_generic_work_init(GGenericWork *);
/* Supprime toutes les références externes. */
-static void g_delayed_work_dispose(GDelayedWork *);
+static void g_generic_work_dispose(GGenericWork *);
/* Procède à la libération totale de la mémoire. */
-static void g_delayed_work_finalize(GDelayedWork *);
-
-/* Mène l'opération programmée. */
-static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *);
-
-
-
-/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */
-
-
-#define G_TYPE_WORK_GROUP g_work_group_get_type()
-#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup))
-#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type()))
-#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass))
-#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP))
-#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass))
-
-
-/* File de traitement pour un type donné (instance) */
-typedef struct _GWorkGroup
-{
- GObject parent; /* A laisser en premier */
-
- wgroup_id_t id; /* Identifiant de travaux menés*/
-
- GDelayedWork *works; /* Tâches à mener à bien */
- GMutex mutex; /* Verrou pour l'accès */
- GCond cond; /* Réveil pour un traitement */
- GCond wait_cond; /* Réveil d'attente de fin */
- gint pending; /* Tâches en cours d'exécution */
-
- GThread **threads; /* Procédure de traitement */
- guint threads_count; /* Nombre de procédures */
- bool force_exit; /* Procédure d'arrêt */
-
- wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */
- void *data; /* Données à associer */
-
-} GWorkGroup;
-
-/* File de traitement pour un type donné (classe) */
-typedef struct _GWorkGroupClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-} GWorkGroupClass;
-
-
-/* Indique le type défini pour les groupes de travail. */
-static GType g_work_group_get_type(void);
-
-/* Initialise la classe des groupes de travail. */
-static void g_work_group_class_init(GWorkGroupClass *);
-
-/* Initialise une instance de groupe de travail. */
-static void g_work_group_init(GWorkGroup *);
-
-/* Supprime toutes les références externes. */
-static void g_work_group_dispose(GWorkGroup *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_work_group_finalize(GWorkGroup *);
-
-/* Crée un nouveau thread dédié à un type de travaux donné. */
-static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *);
-
-/* Fournit l'identifiant associé à un groupe de travail. */
-static wgroup_id_t g_work_group_get_id(const GWorkGroup *);
-
-/* Place une nouvelle tâche en attente dans une file dédiée. */
-static void g_work_group_schedule(GWorkGroup *, GDelayedWork *);
-
-/* Assure le traitement en différé. */
-static void *g_work_group_process(GWorkGroup *);
-
-/* Détermine si le groupe est vide de toute programmation. */
-static bool g_work_group_is_empty(GWorkGroup *);
-
-/* Attend que toutes les tâches d'un groupe soient traitées. */
-static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *);
-
-/* Modifie les conditions d'attente des fins d'exécutions. */
-static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *);
-
-/* Force un réveil d'une attente en cours pour la confirmer. */
-static void g_work_group_wake_up_waiters(GWorkGroup *);
-
-
+static void g_generic_work_finalize(GGenericWork *);
-/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
-
-
-/* Gestionnaire des travaux différés (instance) */
-struct _GWorkQueue
-{
- GObject parent; /* A laisser en premier */
-
- wgroup_id_t generator; /* Générateur d'identifiants */
-
- GWorkGroup **groups; /* Files de traitement */
- size_t groups_count; /* Nombre de files internes */
- GMutex mutex; /* Verrou pour l'accès */
- GCond wait_all; /* Réveil d'attente globale */
-
-};
-
-/* Gestionnaire des travaux différés (classe) */
-struct _GWorkQueueClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-};
-
-
-/* Initialise la classe des travaux différés. */
-static void g_work_queue_class_init(GWorkQueueClass *);
-
-/* Initialise une instance de gestionnaire de travaux différés. */
-static void g_work_queue_init(GWorkQueue *);
-
-/* Supprime toutes les références externes. */
-static void g_work_queue_dispose(GWorkQueue *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_work_queue_finalize(GWorkQueue *);
-
-/* Donne l'assurance de l'existence d'un groupe de travail. */
-static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *);
-
-/* Fournit le groupe de travail correspondant à un identifiant. */
-static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t);
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* TACHE DIFFEREE DANS LE TEMPS */
-/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les travaux différés. */
-G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT);
+G_DEFINE_TYPE(GGenericWork, g_generic_work, G_TYPE_OBJECT);
/******************************************************************************
@@ -207,19 +58,19 @@ G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT);
* *
******************************************************************************/
-static void g_delayed_work_class_init(GDelayedWorkClass *klass)
+static void g_generic_work_class_init(GGenericWorkClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
- object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose;
- object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize;
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_generic_work_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_generic_work_finalize;
g_signal_new("work-completed",
- G_TYPE_DELAYED_WORK,
+ G_TYPE_GENERIC_WORK,
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(GDelayedWorkClass, work_completed),
+ G_STRUCT_OFFSET(GGenericWorkClass, work_completed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
@@ -239,8 +90,10 @@ static void g_delayed_work_class_init(GDelayedWorkClass *klass)
* *
******************************************************************************/
-static void g_delayed_work_init(GDelayedWork *work)
+static void g_generic_work_init(GGenericWork *work)
{
+ DL_LIST_ITEM_INIT(&work->link);
+
work->completed = false;
g_mutex_init(&work->mutex);
g_cond_init(&work->cond);
@@ -260,12 +113,12 @@ static void g_delayed_work_init(GDelayedWork *work)
* *
******************************************************************************/
-static void g_delayed_work_dispose(GDelayedWork *work)
+static void g_generic_work_dispose(GGenericWork *work)
{
g_mutex_clear(&work->mutex);
g_cond_clear(&work->cond);
- G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work));
+ G_OBJECT_CLASS(g_generic_work_parent_class)->dispose(G_OBJECT(work));
}
@@ -282,281 +135,19 @@ static void g_delayed_work_dispose(GDelayedWork *work)
* *
******************************************************************************/
-static void g_delayed_work_finalize(GDelayedWork *work)
-{
- G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = travail à effectuer. *
-* status = barre de statut à tenir informée. *
-* *
-* Description : Mène l'opération programmée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status)
-{
- G_DELAYED_WORK_GET_CLASS(work)->run(work, status);
-
- g_mutex_lock(&work->mutex);
-
- work->completed = true;
-
- g_cond_signal(&work->cond);
- g_mutex_unlock(&work->mutex);
-
- g_signal_emit_by_name(work, "work-completed");
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = travail à surveiller. *
-* *
-* Description : Attend la fin de l'exécution d'une tâche donnée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_delayed_work_wait_for_completion(GDelayedWork *work)
-{
- g_mutex_lock(&work->mutex);
-
- while (!work->completed)
- g_cond_wait(&work->cond, &work->mutex);
-
- g_mutex_unlock(&work->mutex);
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* THREADS DES TRAITEMENTS DEDIES */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour les groupes de travail. */
-G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe des groupes de travail. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_class_init(GWorkGroupClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose;
- object->finalize = (GObjectFinalizeFunc)g_work_group_finalize;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = instance à initialiser. *
-* *
-* Description : Initialise une instance de groupe de travail. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_init(GWorkGroup *group)
-{
- group->works = NULL;
-
- g_mutex_init(&group->mutex);
- g_cond_init(&group->cond);
- g_cond_init(&group->wait_cond);
-
- g_atomic_int_set(&group->pending, 0);
-
- group->threads = NULL;
- group->threads_count = 0;
- group->force_exit = false;
-
- group->callback = NULL;
- group->data = NULL;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_dispose(GWorkGroup *group)
-{
- guint i; /* Boucle de parcours */
- GDelayedWork *work; /* Travail à oublier */
-
- group->force_exit = true;
-
- /**
- * Concernant la pose du verrou, se référer aux commentaires de la
- * fonction g_work_group_process().
- */
-
- g_mutex_lock(&group->mutex);
-
- g_cond_broadcast(&group->cond);
-
- g_mutex_unlock(&group->mutex);
-
- for (i = 0; i < group->threads_count; i++)
- g_thread_join(group->threads[i]);
-
- while (!dl_list_empty(group->works))
- {
- work = group->works;
- delayed_work_list_del(work, &group->works);
-
- g_object_unref(G_OBJECT(work));
-
- }
-
- g_mutex_clear(&group->mutex);
- g_cond_clear(&group->cond);
- g_cond_clear(&group->wait_cond);
-
- G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_finalize(GWorkGroup *group)
-{
- if (group->threads != NULL)
- free(group->threads);
-
- G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : id = identifiant accordé au nouveau groupe. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Crée un nouveau thread dédié à un type de travaux donné. *
-* *
-* Retour : Structure associée au thread mise en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count)
-{
- GWorkGroup *result; /* Traiteur à retourner */
- guint i; /* Boucle de parcours */
- char name[16]; /* Désignation humaine */
-
- result = g_object_new(G_TYPE_WORK_GROUP, NULL);
-
- result->id = id;
-
- result->threads_count = get_max_online_threads();
-
- if (count != NULL && *count < result->threads_count)
- result->threads_count = *count;
-
- result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *));
-
- for (i = 0; i < result->threads_count; i++)
- {
- snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i);
-
- result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result);
- if (!result->threads[i])
- goto start_error;
-
- }
-
- start_error:
-
- result->threads_count = i;
-
- assert(i > 0);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = gestionnaire des actions à mener. *
-* *
-* Description : Fournit l'identifiant associé à un groupe de travail. *
-* *
-* Retour : Identifiant unique attribué au groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
+static void g_generic_work_finalize(GGenericWork *work)
{
- return group->id;
+ G_OBJECT_CLASS(g_generic_work_parent_class)->finalize(G_OBJECT(work));
}
/******************************************************************************
* *
-* Paramètres : group = gestionnaire des actions à mener. *
-* work = nouvelle tâche à programmer, puis effectuer. *
+* Paramètres : work = travail à traiter. *
+* list = ensemble de travaux à considérer. [OUT] *
* *
-* Description : Place une nouvelle tâche en attente dans une file dédiée. *
+* Description : Intègre un travail dans une liste de tâches à effectuer. *
* *
* Retour : - *
* *
@@ -564,161 +155,19 @@ static wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
* *
******************************************************************************/
-static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
+void g_generic_work_add_to_list(GGenericWork *work, GGenericWork **list)
{
- g_mutex_lock(&group->mutex);
-
- g_atomic_int_inc(&group->pending);
-
- delayed_work_list_add_tail(work, &group->works);
-
- g_cond_signal(&group->cond);
-
- g_mutex_unlock(&group->mutex);
+ dl_list_add_tail(work, list, GGenericWork, link);
}
/******************************************************************************
* *
-* Paramètres : group = gestionnaire des actions à mener. *
-* *
-* Description : Assure le traitement en différé. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void *g_work_group_process(GWorkGroup *group)
-{
- GDelayedWork *work; /* Traitement à mener */
- GtkStatusStack *status; /* Zone d'info éventuelle */
-
- while (1)
- {
- g_mutex_lock(&group->mutex);
-
- while (dl_list_empty(group->works) && !group->force_exit)
- g_cond_wait(&group->cond, &group->mutex);
-
- if (group->force_exit)
- {
- g_mutex_unlock(&group->mutex);
- break;
- }
-
- work = group->works;
- delayed_work_list_del(work, &group->works);
-
- g_mutex_unlock(&group->mutex);
-
-#ifdef INCLUDE_GTK_SUPPORT
- status = get_global_status();
-#else
- status = NULL;
-#endif
- g_delayed_work_process(work, status);
-
- g_object_unref(G_OBJECT(work));
-
- /**
- * Verrou ou pas verrou ?
- *
- * La documentation de la GLib indique que ce n'est pas nécessaire :
- *
- * '''
- * It is good practice to lock the same mutex as the waiting threads
- * while calling this function, though not required.
- * '''
- *
- * Ce conseil se trouve verbatim à l'adresse :
- *
- * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast
- *
- * Dans la pratique, il peut arriver que l'attente de la fonction
- * g_work_group_wait_for_completion() ne soit jamais interrompue.
- *
- * La documentation POSIX est un peu plus orientée :
- *
- * '''
- * The pthread_cond_broadcast() functions may be called by a thread
- * whether or not it currently owns the mutex that threads calling
- * pthread_cond_wait() have associated with the condition variable
- * during their waits; however, if predictable scheduling behavior is
- * required, then that mutex shall be locked by the thread calling
- * pthread_cond_broadcast().
- * '''
- *
- * Ce passage complet est consultable à l'adresse :
- *
- * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html
- *
- * La page de manuel pthread_cond_broadcast(3) est quant à elle plus
- * directrice : aucun complément d'information sur le sujet n'est fourni
- * et les exemples associés utilisent implicement un verrou pendant
- * sont appel.
- */
-
- g_mutex_lock(&group->mutex);
-
- if (g_atomic_int_dec_and_test(&group->pending))
- g_cond_broadcast(&group->wait_cond);
-
- g_mutex_unlock(&group->mutex);
-
- }
-
- return NULL;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = gestionnaire des actions à consulter. *
-* *
-* Description : Détermine si le groupe est vide de toute programmation. *
+* Paramètres : work = travail à traiter. *
+* list = ensemble de travaux à considérer. [OUT] *
* *
-* Retour : Etat du groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static bool g_work_group_is_empty(GWorkGroup *group)
-{
- bool result; /* Etat à retourner */
-
- /**
- * Pour que le résultat soit exploitable, il ne doit pas varier
- * en dehors de la zone couverte par le verrou du groupe avant
- * son utilisation par l'appelant.
- *
- * Il doit donc logiquement y avoir un autre verrou en amont et,
- * comme à priori on ne devrait pas bloquer les groupes principaux
- * pour un traitement particulier, cette procédure ne devrait concerner
- * que des groupes dynamiques.
- */
-
- g_mutex_lock(&group->mutex);
-
- result = dl_list_empty(group->works);
-
- g_mutex_unlock(&group->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = groupe dont les conclusions sont attendues. *
-* queue = queue d'appartenance pour les appels externes. *
-* *
-* Description : Attend que toutes les tâches d'un groupe soient traitées. *
+* Description : Supprime un travail d'une liste de tâches à effectuer. *
* *
* Retour : - *
* *
@@ -726,530 +175,18 @@ static bool g_work_group_is_empty(GWorkGroup *group)
* *
******************************************************************************/
-static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue)
+void g_generic_work_remove_from_list(GGenericWork *work, GGenericWork **list)
{
- wait_for_incoming_works_cb callback; /* Procédure complémentaire */
-
- bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data)
- {
- return false;
- }
-
- callback = group->callback != NULL ? group->callback : no_extra_check;
-
- g_mutex_lock(&group->mutex);
-
- /**
- * On attend que :
- * - la liste des tâches programmées soit vide.
- * - il n'existe plus de tâche en cours.
- * - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
- */
-
- while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data))
- && !group->force_exit)
- {
- g_cond_wait(&group->wait_cond, &group->mutex);
- }
-
- g_mutex_unlock(&group->mutex);
+ dl_list_del(work, list, GGenericWork, link);
}
/******************************************************************************
* *
-* Paramètres : group = groupe dont les paramètres sont à modifier. *
-* callback = éventuelle fonction à appeler ou NULL. *
-* data = données devant accompagner l'appel. *
-* *
-* Description : Modifie les conditions d'attente des fins d'exécutions. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data)
-{
- group->callback = callback;
- group->data = data;
-
-}
-
-
-/******************************************************************************
+* Paramètres : work = travail à effectuer. *
* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Force un réveil d'une attente en cours pour la confirmer. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_wake_up_waiters(GWorkGroup *group)
-{
- /**
- * Concernant la pose du verrou, se référer aux commentaires de la
- * fonction g_work_group_process().
- */
-
- g_mutex_lock(&group->mutex);
-
- g_cond_broadcast(&group->wait_cond);
-
- g_mutex_unlock(&group->mutex);
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* TRAITEMENT DE TACHES DIFFEREES */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour le gestionnaire des travaux différés. */
-G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe des travaux différés. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_class_init(GWorkQueueClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose;
- object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance à initialiser. *
-* *
-* Description : Initialise une instance de gestionnaire de travaux différés. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_init(GWorkQueue *queue)
-{
- queue->generator = 0;
-
- queue->groups = NULL;
- queue->groups_count = 0;
- g_mutex_init(&queue->mutex);
- g_cond_init(&queue->wait_all);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_dispose(GWorkQueue *queue)
-{
- size_t i; /* Boucle de parcours */
-
- g_mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->groups_count; i++)
- g_clear_object(&queue->groups[i]);
-
- g_mutex_unlock(&queue->mutex);
-
- g_mutex_clear(&queue->mutex);
- g_cond_clear(&queue->wait_all);
-
- G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_finalize(GWorkQueue *queue)
-{
- if (queue->groups != NULL)
- free(queue->groups);
-
- G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Créé un nouveau gestionnaire de tâches parallèles. *
-* *
-* Retour : Gestionnaire de traitements mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GWorkQueue *g_work_queue_new(void)
-{
- GWorkQueue *result; /* Instance à retourner */
-
- result = g_object_new(G_TYPE_WORK_QUEUE, NULL);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Donne l'assurance de l'existence d'un groupe de travail. *
-* *
-* Retour : true si un nouveau groupe a été constitué, false sinon. *
-* *
-* Remarques : Le verrou d'accès doit être posé par l'appelant. *
-* *
-******************************************************************************/
-
-static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count)
-{
- bool found; /* Bilan des recherches */
- size_t i; /* Boucle de parcours */
- GWorkGroup *group; /* Groupe à consulter */
-
- assert(!g_mutex_trylock(&queue->mutex));
-
- found = false;
-
- for (i = 0; i < queue->groups_count && !found; i++)
- {
- group = queue->groups[i];
- found = (g_work_group_get_id(group) == id);
- }
-
- if (!found)
- {
- queue->groups_count++;
- queue->groups = (GWorkGroup **)realloc(queue->groups,
- queue->groups_count * sizeof(GWorkGroup *));
-
- group = g_work_group_new(id, count);
- queue->groups[queue->groups_count - 1] = group;
-
- }
-
- return !found;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* *
-* Description : Constitue un nouveau groupe de travail. *
-* *
-* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue)
-{
- wgroup_id_t result; /* Valeur à retourner */
- bool created; /* Bilan d'une tentative */
-
- g_mutex_lock(&queue->mutex);
-
- do
- {
- result = queue->generator++;
- created = g_work_queue_ensure_group_exists(queue, result, NULL);
- }
- while (!created);
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Constitue un nouveau petit groupe de travail. *
-* *
-* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
-{
- wgroup_id_t result; /* Valeur à retourner */
- bool created; /* Bilan d'une tentative */
-
- g_mutex_lock(&queue->mutex);
-
- do
- {
- result = queue->generator++;
- created = g_work_queue_ensure_group_exists(queue, result, &count);
- }
- while (!created);
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Dissout un groupe de travail existant. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id)
-{
- size_t i; /* Boucle de parcours */
- GWorkGroup *group; /* Groupe de travail manipulé */
-#ifndef NDEBUG
- bool found; /* Repérage du groupe visé */
-#endif
-
-#ifndef NDEBUG
- found = false;
-#endif
-
- g_mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->groups_count; i++)
- {
- group = queue->groups[i];
-
- if (g_work_group_get_id(group) == id)
- {
- g_object_unref(G_OBJECT(group));
-
- memmove(&queue->groups[i], &queue->groups[i + 1],
- (queue->groups_count - i - 1) * sizeof(GWorkGroup *));
-
- queue->groups_count--;
- queue->groups = (GWorkGroup **)realloc(queue->groups,
- queue->groups_count * sizeof(GWorkGroup *));
-
-#ifndef NDEBUG
- found = true;
-#endif
-
- break;
-
- }
-
- }
-
- assert(found);
-
- g_cond_broadcast(&queue->wait_all);
-
- g_mutex_unlock(&queue->mutex);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire des actions à mener. *
-* work = nouvelle tâche à programmer, puis effectuer. *
-* id = identifiant du groupe de travail d'affectation. *
-* *
-* Description : Place une nouvelle tâche en attente. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id)
-{
- GWorkGroup *group; /* Groupe de travail à attendre*/
-
- group = g_work_queue_find_group_for_id(queue, id);
- assert(group != NULL);
-
- g_work_group_schedule(group, work);
-
- g_object_unref(G_OBJECT(group));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Fournit le groupe de travail correspondant à un identifiant. *
-* *
-* Retour : Eventuel groupe existant trouvé ou NULL si aucun. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id)
-{
- GWorkGroup *result; /* Trouvaille à retourner */
- size_t i; /* Boucle de parcours */
-
- result = NULL;
-
- g_mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->groups_count; i++)
- if (g_work_group_get_id(queue->groups[i]) == id)
- {
- result = queue->groups[i];
- g_object_ref(G_OBJECT(result));
- break;
- }
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Détermine si un groupe est vide de toute programmation. *
-* *
-* Retour : Etat du groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id)
-{
- bool result; /* Etat à retourner */
- GWorkGroup *group; /* Groupe de travail à attendre*/
-
- group = g_work_queue_find_group_for_id(queue, id);
-
- if (group != NULL)
- {
- result = g_work_group_is_empty(group);
- g_object_unref(G_OBJECT(group));
- }
-
- else
- result = true;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Attend que toutes les tâches d'un groupe soient traitées. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
-{
- GWorkGroup *group; /* Groupe de travail à attendre*/
-
- group = g_work_queue_find_group_for_id(queue, id);
-
- if (group != NULL)
- {
- g_work_group_wait_for_completion(group, queue);
- g_object_unref(G_OBJECT(group));
- }
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* gb_ids = identifiants de groupes globaux. *
-* gb_count = nombre de ces groupes globaux. *
-* *
-* Description : Attend que toutes les tâches de tout groupe soient traitées. *
+* Description : Mène l'opération programmée. *
* *
* Retour : - *
* *
@@ -1257,80 +194,27 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
* *
******************************************************************************/
-void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count)
+void g_generic_work_process(GGenericWork *work)
{
- size_t i; /* Boucle de parcours */
-
- g_mutex_lock(&queue->mutex);
-
- wait_again:
-
- /**
- * Attente d'éventuels groupes isolés.
- */
-
- while (queue->groups_count > gb_count)
- g_cond_wait(&queue->wait_all, &queue->mutex);
-
- g_mutex_unlock(&queue->mutex);
-
- /**
- * Attente des groupes principaux.
- */
-
- for (i = 0; i < gb_count; i++)
- g_work_queue_wait_for_completion(queue, gb_ids[i]);
+ G_GENERIC_WORK_GET_CLASS(work)->run(work);
- /**
- * Si le groupe par défaut a généré de nouveaux groupes, on recommence !
- */
-
- g_mutex_lock(&queue->mutex);
-
- if (queue->groups_count > gb_count)
- goto wait_again;
-
- g_mutex_unlock(&queue->mutex);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
-* callback = éventuelle fonction à appeler ou NULL. *
-* data = données devant accompagner l'appel. *
-* *
-* Description : Modifie les conditions d'attente des fins d'exécutions. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
+ g_mutex_lock(&work->mutex);
-void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data)
-{
- GWorkGroup *group; /* Groupe de travail à traiter */
+ work->completed = true;
- group = g_work_queue_find_group_for_id(queue, id);
+ g_cond_signal(&work->cond);
+ g_mutex_unlock(&work->mutex);
- if (group != NULL)
- {
- g_work_group_set_extra_wait_callback(group, callback, data);
- g_object_unref(G_OBJECT(group));
- }
+ g_signal_emit_by_name(work, "work-completed");
}
/******************************************************************************
* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
+* Paramètres : work = travail à surveiller. *
* *
-* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* Description : Attend la fin de l'exécution d'une tâche donnée. *
* *
* Retour : - *
* *
@@ -1338,16 +222,13 @@ void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wai
* *
******************************************************************************/
-void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id)
+void g_generic_work_wait_for_completion(GGenericWork *work)
{
- GWorkGroup *group; /* Groupe de travail à traiter */
+ g_mutex_lock(&work->mutex);
- group = g_work_queue_find_group_for_id(queue, id);
+ while (!work->completed)
+ g_cond_wait(&work->cond, &work->mutex);
- if (group != NULL)
- {
- g_work_group_wake_up_waiters(group);
- g_object_unref(G_OBJECT(group));
- }
+ g_mutex_unlock(&work->mutex);
}
diff --git a/src/glibext/work.h b/src/glibext/work.h
index 89eed12..1084942 100644
--- a/src/glibext/work.h
+++ b/src/glibext/work.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed.h - prototypes pour la gestion des travaux différés
+ * work.h - prototypes pour la gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,107 +21,31 @@
*/
-#ifndef _GLIBEXT_DELAYED_H
-#define _GLIBEXT_DELAYED_H
+#ifndef _GLIBEXT_WORK_H
+#define _GLIBEXT_WORK_H
-#include <glib-object.h>
-#include <stdbool.h>
-#include <stdint.h>
+#include "helpers.h"
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+#define G_TYPE_GENERIC_WORK (g_generic_work_get_type())
+DECLARE_GTYPE(GGenericWork, g_generic_work, G, GENERIC_WORK);
-#define G_TYPE_DELAYED_WORK g_delayed_work_get_type()
-#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork))
-#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type()))
-#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
-#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK))
-#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+/* Intègre un travail dans une liste de tâches à effectuer. */
+void g_generic_work_add_to_list(GGenericWork *, GGenericWork **);
-/* Travail différé (instance) */
-typedef struct _GDelayedWork GDelayedWork;
+/* Supprime un travail d'une liste de tâches à effectuer. */
+void g_generic_work_remove_from_list(GGenericWork *, GGenericWork **);
-/* Travail différé (classe) */
-typedef struct _GDelayedWorkClass GDelayedWorkClass;
-
-
-/* Indique le type défini pour les travaux différés. */
-GType g_delayed_work_get_type(void);
+/* Mène l'opération programmée. */
+void g_generic_work_process(GGenericWork *);
/* Attend la fin de l'exécution d'une tâche donnée. */
-void g_delayed_work_wait_for_completion(GDelayedWork *);
-
-
-
-/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
-
-
-#define G_TYPE_WORK_QUEUE g_work_queue_get_type()
-#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue))
-#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type()))
-#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass))
-#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE))
-#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass))
-
-
-/* Gestionnaire des travaux différés (instance) */
-typedef struct _GWorkQueue GWorkQueue;
-
-/* Gestionnaire des travaux différés (classe) */
-typedef struct _GWorkQueueClass GWorkQueueClass;
-
-
-/**
- * Identifiant unique pour groupe de travail.
- *
- * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait
- * pas explicitement le type 'unsigned long long'.
- */
-typedef uint64_t wgroup_id_t;
-
-
-/* Indique le type défini pour le gestionnaire des travaux différés. */
-GType g_work_queue_get_type(void);
-
-/* Créé un nouveau gestionnaire de tâches parallèles. */
-GWorkQueue *g_work_queue_new(void);
-
-/* Constitue un nouveau groupe de travail. */
-wgroup_id_t g_work_queue_define_work_group(GWorkQueue *);
-
-/* Constitue un nouveau petit groupe de travail. */
-wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint);
-
-/* Dissout un groupe de travail existant. */
-void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t);
-
-/* Place une nouvelle tâche en attente. */
-void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t);
-
-/* Détermine si un groupe est vide de toute programmation. */
-bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t);
-
-/* Attend que toutes les tâches d'un groupe soient traitées. */
-void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t);
-
-/* Attend que toutes les tâches de tout groupe soient traitées. */
-void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t);
-
-
-/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
-typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *);
-
-
-/* Modifie les conditions d'attente des fins d'exécutions. */
-void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *);
-
-/* Force un réveil d'une attente en cours pour la confirmer. */
-void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t);
+void g_generic_work_wait_for_completion(GGenericWork *);
-#endif /* _GLIBEXT_DELAYED_H */
+#endif /* _GLIBEXT_WORK_H */
diff --git a/src/glibext/workgroup-int.h b/src/glibext/workgroup-int.h
index 4f84e86..7224cf9 100644
--- a/src/glibext/workgroup-int.h
+++ b/src/glibext/workgroup-int.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed-int.h - définitions internes pour la gestion des travaux différés
+ * workgroup-int.h - définitions internes pour la gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,55 +21,40 @@
*/
-#ifndef _GLIBEXT_DELAYED_INT_H
-#define _GLIBEXT_DELAYED_INT_H
+#ifndef _GLIBEXT_WORKGROUP_INT_H
+#define _GLIBEXT_WORKGROUP_INT_H
-#include "delayed.h"
+#include "workgroup.h"
-#include "notifier.h"
-#include "../common/dllist.h"
-
-
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-
-
-/* Traite un travail programmé. */
-typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *);
-
-
-/* Travail différé (instance) */
-struct _GDelayedWork
+/* File de traitement pour un type donné (instance) */
+typedef struct _GWorkGroup
{
GObject parent; /* A laisser en premier */
- DL_LIST_ITEM(link); /* Lien vers les maillons */
+ wgroup_id_t id; /* Identifiant de travaux menés*/
- bool completed; /* Fin de la tâche ? */
- GMutex mutex; /* Accès à la variable */
- GCond cond; /* Attente de changement */
+ GGenericWork *works; /* Tâches à mener à bien */
+ GMutex mutex; /* Verrou pour l'accès */
+ GCond cond; /* Réveil pour un traitement */
+ GCond wait_cond; /* Réveil d'attente de fin */
+ gint pending; /* Tâches en cours d'exécution */
-};
+ GThread **threads; /* Procédure de traitement */
+ guint threads_count; /* Nombre de procédures */
+ bool force_exit; /* Procédure d'arrêt */
-/* Travail différé (classe) */
-struct _GDelayedWorkClass
+} GWorkGroup;
+
+/* File de traitement pour un type donné (classe) */
+typedef struct _GWorkGroupClass
{
GObjectClass parent; /* A laisser en premier */
- run_task_fc run; /* Traitement externalisé */
-
- /* Signaux */
-
- void (* work_completed) (GDelayedWork *);
-
-};
-
-
-#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link)
-#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link)
+} GWorkGroupClass;
-#endif /* _GLIBEXT_DELAYED_INT_H */
+#endif /* _GLIBEXT_WORKGROUP_INT_H */
diff --git a/src/glibext/workgroup.c b/src/glibext/workgroup.c
index 6b5ac35..ba133a9 100644
--- a/src/glibext/workgroup.c
+++ b/src/glibext/workgroup.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed.c - gestion des travaux différés
+ * workgroup.c - gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,88 +21,21 @@
*/
-#include "delayed.h"
+#include "workgroup.h"
#include <assert.h>
#include <inttypes.h>
#include <malloc.h>
#include <stdio.h>
-#include <string.h>
-#include "delayed-int.h"
+#include "workgroup-int.h"
+#include "../common/dllist.h"
#include "../core/nproc.h"
-#ifdef INCLUDE_GTK_SUPPORT
-# include "../gui/core/global.h"
-#endif
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-
-
-/* Initialise la classe des travaux différés. */
-static void g_delayed_work_class_init(GDelayedWorkClass *);
-
-/* Initialise une instance de travail différé. */
-static void g_delayed_work_init(GDelayedWork *);
-
-/* Supprime toutes les références externes. */
-static void g_delayed_work_dispose(GDelayedWork *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_delayed_work_finalize(GDelayedWork *);
-
-/* Mène l'opération programmée. */
-static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *);
-
-
-
-/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */
-
-
-#define G_TYPE_WORK_GROUP g_work_group_get_type()
-#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup))
-#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type()))
-#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass))
-#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP))
-#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass))
-
-
-/* File de traitement pour un type donné (instance) */
-typedef struct _GWorkGroup
-{
- GObject parent; /* A laisser en premier */
-
- wgroup_id_t id; /* Identifiant de travaux menés*/
-
- GDelayedWork *works; /* Tâches à mener à bien */
- GMutex mutex; /* Verrou pour l'accès */
- GCond cond; /* Réveil pour un traitement */
- GCond wait_cond; /* Réveil d'attente de fin */
- gint pending; /* Tâches en cours d'exécution */
-
- GThread **threads; /* Procédure de traitement */
- guint threads_count; /* Nombre de procédures */
- bool force_exit; /* Procédure d'arrêt */
-
- wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */
- void *data; /* Données à associer */
-
-} GWorkGroup;
-
-/* File de traitement pour un type donné (classe) */
-typedef struct _GWorkGroupClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-} GWorkGroupClass;
-
-
-/* Indique le type défini pour les groupes de travail. */
-static GType g_work_group_get_type(void);
-
/* Initialise la classe des groupes de travail. */
static void g_work_group_class_init(GWorkGroupClass *);
@@ -115,237 +48,9 @@ static void g_work_group_dispose(GWorkGroup *);
/* Procède à la libération totale de la mémoire. */
static void g_work_group_finalize(GWorkGroup *);
-/* Crée un nouveau thread dédié à un type de travaux donné. */
-static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *);
-
-/* Fournit l'identifiant associé à un groupe de travail. */
-static wgroup_id_t g_work_group_get_id(const GWorkGroup *);
-
-/* Place une nouvelle tâche en attente dans une file dédiée. */
-static void g_work_group_schedule(GWorkGroup *, GDelayedWork *);
-
/* Assure le traitement en différé. */
static void *g_work_group_process(GWorkGroup *);
-/* Détermine si le groupe est vide de toute programmation. */
-static bool g_work_group_is_empty(GWorkGroup *);
-
-/* Attend que toutes les tâches d'un groupe soient traitées. */
-static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *);
-
-/* Modifie les conditions d'attente des fins d'exécutions. */
-static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *);
-
-/* Force un réveil d'une attente en cours pour la confirmer. */
-static void g_work_group_wake_up_waiters(GWorkGroup *);
-
-
-
-/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
-
-
-/* Gestionnaire des travaux différés (instance) */
-struct _GWorkQueue
-{
- GObject parent; /* A laisser en premier */
-
- wgroup_id_t generator; /* Générateur d'identifiants */
-
- GWorkGroup **groups; /* Files de traitement */
- size_t groups_count; /* Nombre de files internes */
- GMutex mutex; /* Verrou pour l'accès */
- GCond wait_all; /* Réveil d'attente globale */
-
-};
-
-/* Gestionnaire des travaux différés (classe) */
-struct _GWorkQueueClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-};
-
-
-/* Initialise la classe des travaux différés. */
-static void g_work_queue_class_init(GWorkQueueClass *);
-
-/* Initialise une instance de gestionnaire de travaux différés. */
-static void g_work_queue_init(GWorkQueue *);
-
-/* Supprime toutes les références externes. */
-static void g_work_queue_dispose(GWorkQueue *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_work_queue_finalize(GWorkQueue *);
-
-/* Donne l'assurance de l'existence d'un groupe de travail. */
-static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *);
-
-/* Fournit le groupe de travail correspondant à un identifiant. */
-static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t);
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* TACHE DIFFEREE DANS LE TEMPS */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour les travaux différés. */
-G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe des travaux différés. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_class_init(GDelayedWorkClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose;
- object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize;
-
- g_signal_new("work-completed",
- G_TYPE_DELAYED_WORK,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(GDelayedWorkClass, work_completed),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = instance à initialiser. *
-* *
-* Description : Initialise une instance de travail différé. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_init(GDelayedWork *work)
-{
- work->completed = false;
- g_mutex_init(&work->mutex);
- g_cond_init(&work->cond);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_dispose(GDelayedWork *work)
-{
- g_mutex_clear(&work->mutex);
- g_cond_clear(&work->cond);
-
- G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_finalize(GDelayedWork *work)
-{
- G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = travail à effectuer. *
-* status = barre de statut à tenir informée. *
-* *
-* Description : Mène l'opération programmée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status)
-{
- G_DELAYED_WORK_GET_CLASS(work)->run(work, status);
-
- g_mutex_lock(&work->mutex);
-
- work->completed = true;
-
- g_cond_signal(&work->cond);
- g_mutex_unlock(&work->mutex);
-
- g_signal_emit_by_name(work, "work-completed");
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = travail à surveiller. *
-* *
-* Description : Attend la fin de l'exécution d'une tâche donnée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_delayed_work_wait_for_completion(GDelayedWork *work)
-{
- g_mutex_lock(&work->mutex);
-
- while (!work->completed)
- g_cond_wait(&work->cond, &work->mutex);
-
- g_mutex_unlock(&work->mutex);
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* THREADS DES TRAITEMENTS DEDIES */
-/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les groupes de travail. */
@@ -390,7 +95,7 @@ static void g_work_group_class_init(GWorkGroupClass *klass)
static void g_work_group_init(GWorkGroup *group)
{
- group->works = NULL;
+ DL_LIST_HEAD_INIT(group->works);
g_mutex_init(&group->mutex);
g_cond_init(&group->cond);
@@ -402,9 +107,6 @@ static void g_work_group_init(GWorkGroup *group)
group->threads_count = 0;
group->force_exit = false;
- group->callback = NULL;
- group->data = NULL;
-
}
@@ -423,7 +125,7 @@ static void g_work_group_init(GWorkGroup *group)
static void g_work_group_dispose(GWorkGroup *group)
{
guint i; /* Boucle de parcours */
- GDelayedWork *work; /* Travail à oublier */
+ GGenericWork *work; /* Travail à oublier */
group->force_exit = true;
@@ -444,9 +146,9 @@ static void g_work_group_dispose(GWorkGroup *group)
while (!dl_list_empty(group->works))
{
work = group->works;
- delayed_work_list_del(work, &group->works);
+ g_generic_work_remove_from_list(work, &group->works);
- g_object_unref(G_OBJECT(work));
+ unref_object(work);
}
@@ -484,7 +186,7 @@ static void g_work_group_finalize(GWorkGroup *group)
/******************************************************************************
* *
* Paramètres : id = identifiant accordé au nouveau groupe. *
-* count = quantité de threads à allouer. *
+* count = quantité de threads à allouer (0 pour un défaut). *
* *
* Description : Crée un nouveau thread dédié à un type de travaux donné. *
* *
@@ -494,33 +196,47 @@ static void g_work_group_finalize(GWorkGroup *group)
* *
******************************************************************************/
-static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count)
+GWorkGroup *g_work_group_new(wgroup_id_t id, guint count)
{
- GWorkGroup *result; /* Traiteur à retourner */
+ GWorkGroup *result; /* Traiteur à retourner */
guint i; /* Boucle de parcours */
- char name[16]; /* Désignation humaine */
+ int ret; /* Bilan d'un appel */
+ char *name; /* Désignation humaine */
result = g_object_new(G_TYPE_WORK_GROUP, NULL);
result->id = id;
- result->threads_count = get_max_online_threads();
+ if (count == 0)
+ count = get_max_online_threads();
- if (count != NULL && *count < result->threads_count)
- result->threads_count = *count;
+ result->threads_count = count;
- result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *));
+ result->threads = calloc(result->threads_count, sizeof(GThread *));
for (i = 0; i < result->threads_count; i++)
{
- snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i);
+ /**
+ * La documentation précise :
+ *
+ * Some systems restrict the length of name to 16 bytes.
+ *
+ * On laisse ces systèmes tronquer.
+ */
+
+ ret = asprintf(&name, "wgrp_%" PRIu64 "-%u", id, i);
+ if (ret == -1) goto naming_error;
result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result);
+
+ free(name);
+
if (!result->threads[i])
goto start_error;
}
+ naming_error:
start_error:
result->threads_count = i;
@@ -544,7 +260,7 @@ static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count)
* *
******************************************************************************/
-static wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
+wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
{
return group->id;
@@ -564,13 +280,14 @@ static wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
* *
******************************************************************************/
-static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
+void g_work_group_schedule(GWorkGroup *group, GGenericWork *work)
{
g_mutex_lock(&group->mutex);
g_atomic_int_inc(&group->pending);
- delayed_work_list_add_tail(work, &group->works);
+ ref_object(work);
+ g_generic_work_add_to_list(work, &group->works);
g_cond_signal(&group->cond);
@@ -593,8 +310,7 @@ static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
static void *g_work_group_process(GWorkGroup *group)
{
- GDelayedWork *work; /* Traitement à mener */
- GtkStatusStack *status; /* Zone d'info éventuelle */
+ GGenericWork *work; /* Traitement à mener */
while (1)
{
@@ -610,18 +326,13 @@ static void *g_work_group_process(GWorkGroup *group)
}
work = group->works;
- delayed_work_list_del(work, &group->works);
+ g_generic_work_remove_from_list(work, &group->works);
g_mutex_unlock(&group->mutex);
-#ifdef INCLUDE_GTK_SUPPORT
- status = get_global_status();
-#else
- status = NULL;
-#endif
- g_delayed_work_process(work, status);
+ g_generic_work_process(work);
- g_object_unref(G_OBJECT(work));
+ unref_object(work);
/**
* Verrou ou pas verrou ?
@@ -687,7 +398,7 @@ static void *g_work_group_process(GWorkGroup *group)
* *
******************************************************************************/
-static bool g_work_group_is_empty(GWorkGroup *group)
+bool g_work_group_is_empty(GWorkGroup *group)
{
bool result; /* Etat à retourner */
@@ -716,7 +427,6 @@ static bool g_work_group_is_empty(GWorkGroup *group)
/******************************************************************************
* *
* Paramètres : group = groupe dont les conclusions sont attendues. *
-* queue = queue d'appartenance pour les appels externes. *
* *
* Description : Attend que toutes les tâches d'un groupe soient traitées. *
* *
@@ -726,17 +436,8 @@ static bool g_work_group_is_empty(GWorkGroup *group)
* *
******************************************************************************/
-static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue)
+void g_work_group_wait_for_completion(GWorkGroup *group)
{
- wait_for_incoming_works_cb callback; /* Procédure complémentaire */
-
- bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data)
- {
- return false;
- }
-
- callback = group->callback != NULL ? group->callback : no_extra_check;
-
g_mutex_lock(&group->mutex);
/**
@@ -746,7 +447,7 @@ static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queu
* - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
*/
- while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data))
+ while ((g_atomic_int_get(&group->pending) > 0)
&& !group->force_exit)
{
g_cond_wait(&group->wait_cond, &group->mutex);
@@ -759,456 +460,43 @@ static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queu
/******************************************************************************
* *
-* Paramètres : group = groupe dont les paramètres sont à modifier. *
-* callback = éventuelle fonction à appeler ou NULL. *
-* data = données devant accompagner l'appel. *
+* Paramètres : group = groupe dont les conclusions sont attendues. *
+* rel = durée relative à patienter au max. en microsecondes. *
* *
-* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* Description : Attend que toutes les tâches d'un groupe soient traitées. *
* *
-* Retour : - *
+* Retour : Bilan de l'attente : false en cas d'expiration, true sinon. *
* *
-* Remarques : - *
+* Remarques : Cette fonction est originellement dédiée à un usage Python. *
* *
******************************************************************************/
-static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data)
+bool g_work_group_wait_timed_for_completion(GWorkGroup *group, gint64 rel)
{
- group->callback = callback;
- group->data = data;
+ bool result; /* Bilan d'attente à renvoyer */
+ gint64 end_time; /* Borne de fin de l'attente */
-}
+ result = true;
+ g_mutex_lock(&group->mutex);
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Force un réveil d'une attente en cours pour la confirmer. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
+ end_time = g_get_monotonic_time() + rel;
-static void g_work_group_wake_up_waiters(GWorkGroup *group)
-{
/**
- * Concernant la pose du verrou, se référer aux commentaires de la
- * fonction g_work_group_process().
+ * On attend que :
+ * - la liste des tâches programmées soit vide.
+ * - il n'existe plus de tâche en cours.
+ * - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
*/
- g_mutex_lock(&group->mutex);
-
- g_cond_broadcast(&group->wait_cond);
-
- g_mutex_unlock(&group->mutex);
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* TRAITEMENT DE TACHES DIFFEREES */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour le gestionnaire des travaux différés. */
-G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe des travaux différés. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_class_init(GWorkQueueClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose;
- object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance à initialiser. *
-* *
-* Description : Initialise une instance de gestionnaire de travaux différés. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_init(GWorkQueue *queue)
-{
- queue->generator = 0;
-
- queue->groups = NULL;
- queue->groups_count = 0;
- g_mutex_init(&queue->mutex);
- g_cond_init(&queue->wait_all);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_dispose(GWorkQueue *queue)
-{
- size_t i; /* Boucle de parcours */
-
- g_mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->groups_count; i++)
- g_clear_object(&queue->groups[i]);
-
- g_mutex_unlock(&queue->mutex);
-
- g_mutex_clear(&queue->mutex);
- g_cond_clear(&queue->wait_all);
-
- G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_queue_finalize(GWorkQueue *queue)
-{
- if (queue->groups != NULL)
- free(queue->groups);
-
- G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Créé un nouveau gestionnaire de tâches parallèles. *
-* *
-* Retour : Gestionnaire de traitements mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GWorkQueue *g_work_queue_new(void)
-{
- GWorkQueue *result; /* Instance à retourner */
-
- result = g_object_new(G_TYPE_WORK_QUEUE, NULL);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Donne l'assurance de l'existence d'un groupe de travail. *
-* *
-* Retour : true si un nouveau groupe a été constitué, false sinon. *
-* *
-* Remarques : Le verrou d'accès doit être posé par l'appelant. *
-* *
-******************************************************************************/
-
-static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count)
-{
- bool found; /* Bilan des recherches */
- size_t i; /* Boucle de parcours */
- GWorkGroup *group; /* Groupe à consulter */
-
- assert(!g_mutex_trylock(&queue->mutex));
-
- found = false;
-
- for (i = 0; i < queue->groups_count && !found; i++)
- {
- group = queue->groups[i];
- found = (g_work_group_get_id(group) == id);
- }
-
- if (!found)
- {
- queue->groups_count++;
- queue->groups = (GWorkGroup **)realloc(queue->groups,
- queue->groups_count * sizeof(GWorkGroup *));
-
- group = g_work_group_new(id, count);
- queue->groups[queue->groups_count - 1] = group;
-
- }
-
- return !found;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* *
-* Description : Constitue un nouveau groupe de travail. *
-* *
-* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue)
-{
- wgroup_id_t result; /* Valeur à retourner */
- bool created; /* Bilan d'une tentative */
-
- g_mutex_lock(&queue->mutex);
-
- do
- {
- result = queue->generator++;
- created = g_work_queue_ensure_group_exists(queue, result, NULL);
- }
- while (!created);
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Constitue un nouveau petit groupe de travail. *
-* *
-* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
-{
- wgroup_id_t result; /* Valeur à retourner */
- bool created; /* Bilan d'une tentative */
-
- g_mutex_lock(&queue->mutex);
-
- do
- {
- result = queue->generator++;
- created = g_work_queue_ensure_group_exists(queue, result, &count);
- }
- while (!created);
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Dissout un groupe de travail existant. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id)
-{
- size_t i; /* Boucle de parcours */
- GWorkGroup *group; /* Groupe de travail manipulé */
-#ifndef NDEBUG
- bool found; /* Repérage du groupe visé */
-#endif
-
-#ifndef NDEBUG
- found = false;
-#endif
-
- g_mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->groups_count; i++)
- {
- group = queue->groups[i];
-
- if (g_work_group_get_id(group) == id)
- {
- g_object_unref(G_OBJECT(group));
-
- memmove(&queue->groups[i], &queue->groups[i + 1],
- (queue->groups_count - i - 1) * sizeof(GWorkGroup *));
-
- queue->groups_count--;
- queue->groups = (GWorkGroup **)realloc(queue->groups,
- queue->groups_count * sizeof(GWorkGroup *));
-
-#ifndef NDEBUG
- found = true;
-#endif
-
- break;
-
- }
-
- }
-
- assert(found);
-
- g_cond_broadcast(&queue->wait_all);
-
- g_mutex_unlock(&queue->mutex);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire des actions à mener. *
-* work = nouvelle tâche à programmer, puis effectuer. *
-* id = identifiant du groupe de travail d'affectation. *
-* *
-* Description : Place une nouvelle tâche en attente. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id)
-{
- GWorkGroup *group; /* Groupe de travail à attendre*/
-
- group = g_work_queue_find_group_for_id(queue, id);
- assert(group != NULL);
-
- g_work_group_schedule(group, work);
-
- g_object_unref(G_OBJECT(group));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Fournit le groupe de travail correspondant à un identifiant. *
-* *
-* Retour : Eventuel groupe existant trouvé ou NULL si aucun. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id)
-{
- GWorkGroup *result; /* Trouvaille à retourner */
- size_t i; /* Boucle de parcours */
-
- result = NULL;
-
- g_mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->groups_count; i++)
- if (g_work_group_get_id(queue->groups[i]) == id)
- {
- result = queue->groups[i];
- g_object_ref(G_OBJECT(result));
- break;
- }
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Détermine si un groupe est vide de toute programmation. *
-* *
-* Retour : Etat du groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id)
-{
- bool result; /* Etat à retourner */
- GWorkGroup *group; /* Groupe de travail à attendre*/
-
- group = g_work_queue_find_group_for_id(queue, id);
-
- if (group != NULL)
+ while ((g_atomic_int_get(&group->pending) > 0)
+ && !group->force_exit)
{
- result = g_work_group_is_empty(group);
- g_object_unref(G_OBJECT(group));
+ result = g_cond_wait_until(&group->wait_cond, &group->mutex, end_time);
+ if (!result) break;
}
- else
- result = true;
+ g_mutex_unlock(&group->mutex);
return result;
@@ -1217,39 +505,10 @@ bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id)
/******************************************************************************
* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Attend que toutes les tâches d'un groupe soient traitées. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
-{
- GWorkGroup *group; /* Groupe de travail à attendre*/
-
- group = g_work_queue_find_group_for_id(queue, id);
-
- if (group != NULL)
- {
- g_work_group_wait_for_completion(group, queue);
- g_object_unref(G_OBJECT(group));
- }
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* gb_ids = identifiants de groupes globaux. *
-* gb_count = nombre de ces groupes globaux. *
+* id = identifiant d'un groupe de travail. *
* *
-* Description : Attend que toutes les tâches de tout groupe soient traitées. *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
* *
* Retour : - *
* *
@@ -1257,97 +516,17 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
* *
******************************************************************************/
-void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count)
+void g_work_group_wake_up_waiters(GWorkGroup *group)
{
- size_t i; /* Boucle de parcours */
-
- g_mutex_lock(&queue->mutex);
-
- wait_again:
-
/**
- * Attente d'éventuels groupes isolés.
- */
-
- while (queue->groups_count > gb_count)
- g_cond_wait(&queue->wait_all, &queue->mutex);
-
- g_mutex_unlock(&queue->mutex);
-
- /**
- * Attente des groupes principaux.
- */
-
- for (i = 0; i < gb_count; i++)
- g_work_queue_wait_for_completion(queue, gb_ids[i]);
-
- /**
- * Si le groupe par défaut a généré de nouveaux groupes, on recommence !
+ * Concernant la pose du verrou, se référer aux commentaires de la
+ * fonction g_work_group_process().
*/
- g_mutex_lock(&queue->mutex);
-
- if (queue->groups_count > gb_count)
- goto wait_again;
-
- g_mutex_unlock(&queue->mutex);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
-* callback = éventuelle fonction à appeler ou NULL. *
-* data = données devant accompagner l'appel. *
-* *
-* Description : Modifie les conditions d'attente des fins d'exécutions. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data)
-{
- GWorkGroup *group; /* Groupe de travail à traiter */
-
- group = g_work_queue_find_group_for_id(queue, id);
-
- if (group != NULL)
- {
- g_work_group_set_extra_wait_callback(group, callback, data);
- g_object_unref(G_OBJECT(group));
- }
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Force un réveil d'une attente en cours pour la confirmer. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id)
-{
- GWorkGroup *group; /* Groupe de travail à traiter */
+ g_mutex_lock(&group->mutex);
- group = g_work_queue_find_group_for_id(queue, id);
+ g_cond_broadcast(&group->wait_cond);
- if (group != NULL)
- {
- g_work_group_wake_up_waiters(group);
- g_object_unref(G_OBJECT(group));
- }
+ g_mutex_unlock(&group->mutex);
}
diff --git a/src/glibext/workgroup.h b/src/glibext/workgroup.h
index 89eed12..1a003c8 100644
--- a/src/glibext/workgroup.h
+++ b/src/glibext/workgroup.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed.h - prototypes pour la gestion des travaux différés
+ * workgroup.h - prototypes pour la gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,58 +21,22 @@
*/
-#ifndef _GLIBEXT_DELAYED_H
-#define _GLIBEXT_DELAYED_H
+#ifndef _GLIBEXT_WORKGROUP_H
+#define _GLIBEXT_WORKGROUP_H
-#include <glib-object.h>
#include <stdbool.h>
#include <stdint.h>
+#include "helpers.h"
+#include "work.h"
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-#define G_TYPE_DELAYED_WORK g_delayed_work_get_type()
-#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork))
-#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type()))
-#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
-#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK))
-#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+#define G_TYPE_WORK_GROUP (g_work_group_get_type())
-
-/* Travail différé (instance) */
-typedef struct _GDelayedWork GDelayedWork;
-
-/* Travail différé (classe) */
-typedef struct _GDelayedWorkClass GDelayedWorkClass;
-
-
-/* Indique le type défini pour les travaux différés. */
-GType g_delayed_work_get_type(void);
-
-/* Attend la fin de l'exécution d'une tâche donnée. */
-void g_delayed_work_wait_for_completion(GDelayedWork *);
-
-
-
-/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
-
-
-#define G_TYPE_WORK_QUEUE g_work_queue_get_type()
-#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue))
-#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type()))
-#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass))
-#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE))
-#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass))
-
-
-/* Gestionnaire des travaux différés (instance) */
-typedef struct _GWorkQueue GWorkQueue;
-
-/* Gestionnaire des travaux différés (classe) */
-typedef struct _GWorkQueueClass GWorkQueueClass;
+DECLARE_GTYPE(GWorkGroup, g_work_group, G, WORK_GROUP);
/**
@@ -84,44 +48,27 @@ typedef struct _GWorkQueueClass GWorkQueueClass;
typedef uint64_t wgroup_id_t;
-/* Indique le type défini pour le gestionnaire des travaux différés. */
-GType g_work_queue_get_type(void);
-
-/* Créé un nouveau gestionnaire de tâches parallèles. */
-GWorkQueue *g_work_queue_new(void);
-
-/* Constitue un nouveau groupe de travail. */
-wgroup_id_t g_work_queue_define_work_group(GWorkQueue *);
-
-/* Constitue un nouveau petit groupe de travail. */
-wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint);
+/* Crée un nouveau thread dédié à un type de travaux donné. */
+GWorkGroup *g_work_group_new(wgroup_id_t, guint);
-/* Dissout un groupe de travail existant. */
-void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t);
+/* Fournit l'identifiant associé à un groupe de travail. */
+wgroup_id_t g_work_group_get_id(const GWorkGroup *);
-/* Place une nouvelle tâche en attente. */
-void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t);
+/* Place une nouvelle tâche en attente dans une file dédiée. */
+void g_work_group_schedule(GWorkGroup *, GGenericWork *);
-/* Détermine si un groupe est vide de toute programmation. */
-bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t);
+/* Détermine si le groupe est vide de toute programmation. */
+bool g_work_group_is_empty(GWorkGroup *);
/* Attend que toutes les tâches d'un groupe soient traitées. */
-void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t);
+void g_work_group_wait_for_completion(GWorkGroup *);
-/* Attend que toutes les tâches de tout groupe soient traitées. */
-void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t);
-
-
-/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
-typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *);
-
-
-/* Modifie les conditions d'attente des fins d'exécutions. */
-void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *);
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+bool g_work_group_wait_timed_for_completion(GWorkGroup *, gint64);
/* Force un réveil d'une attente en cours pour la confirmer. */
-void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t);
+void g_work_group_wake_up_waiters(GWorkGroup *);
-#endif /* _GLIBEXT_DELAYED_H */
+#endif /* _GLIBEXT_WORKGROUP_H */
diff --git a/src/glibext/workqueue-int.h b/src/glibext/workqueue-int.h
index 4f84e86..40afa19 100644
--- a/src/glibext/workqueue-int.h
+++ b/src/glibext/workqueue-int.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed-int.h - définitions internes pour la gestion des travaux différés
+ * workqueue-int.h - définitions internes pour la gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,55 +21,35 @@
*/
-#ifndef _GLIBEXT_DELAYED_INT_H
-#define _GLIBEXT_DELAYED_INT_H
+#ifndef _GLIBEXT_WORKQUEUE_INT_H
+#define _GLIBEXT_WORKQUEUE_INT_H
-#include "delayed.h"
+#include "workqueue.h"
-#include "notifier.h"
-#include "../common/dllist.h"
-
-
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-
-
-/* Traite un travail programmé. */
-typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *);
-
-
-/* Travail différé (instance) */
-struct _GDelayedWork
+/* Gestionnaire des travaux différés (instance) */
+struct _GWorkQueue
{
GObject parent; /* A laisser en premier */
- DL_LIST_ITEM(link); /* Lien vers les maillons */
+ wgroup_id_t generator; /* Générateur d'identifiants */
- bool completed; /* Fin de la tâche ? */
- GMutex mutex; /* Accès à la variable */
- GCond cond; /* Attente de changement */
+ GWorkGroup **groups; /* Files de traitement */
+ size_t groups_count; /* Nombre de files internes */
+ GMutex mutex; /* Verrou pour l'accès */
+ GCond wait_all; /* Réveil d'attente globale */
};
-/* Travail différé (classe) */
-struct _GDelayedWorkClass
+/* Gestionnaire des travaux différés (classe) */
+struct _GWorkQueueClass
{
GObjectClass parent; /* A laisser en premier */
- run_task_fc run; /* Traitement externalisé */
-
- /* Signaux */
-
- void (* work_completed) (GDelayedWork *);
-
};
-#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link)
-#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link)
-
-
-#endif /* _GLIBEXT_DELAYED_INT_H */
+#endif /* _GLIBEXT_WORKQUEUE_INT_H */
diff --git a/src/glibext/workqueue.c b/src/glibext/workqueue.c
index 6b5ac35..ad6ede6 100644
--- a/src/glibext/workqueue.c
+++ b/src/glibext/workqueue.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed.c - gestion des travaux différés
+ * workqueue.c - gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,149 +21,15 @@
*/
-#include "delayed.h"
+#include "workqueue.h"
#include <assert.h>
-#include <inttypes.h>
#include <malloc.h>
-#include <stdio.h>
-#include <string.h>
-
-
-#include "delayed-int.h"
-#include "../core/nproc.h"
-#ifdef INCLUDE_GTK_SUPPORT
-# include "../gui/core/global.h"
-#endif
+#include "workqueue-int.h"
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-
-
-/* Initialise la classe des travaux différés. */
-static void g_delayed_work_class_init(GDelayedWorkClass *);
-
-/* Initialise une instance de travail différé. */
-static void g_delayed_work_init(GDelayedWork *);
-
-/* Supprime toutes les références externes. */
-static void g_delayed_work_dispose(GDelayedWork *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_delayed_work_finalize(GDelayedWork *);
-
-/* Mène l'opération programmée. */
-static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *);
-
-
-
-/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */
-
-
-#define G_TYPE_WORK_GROUP g_work_group_get_type()
-#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup))
-#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type()))
-#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass))
-#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP))
-#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass))
-
-
-/* File de traitement pour un type donné (instance) */
-typedef struct _GWorkGroup
-{
- GObject parent; /* A laisser en premier */
-
- wgroup_id_t id; /* Identifiant de travaux menés*/
-
- GDelayedWork *works; /* Tâches à mener à bien */
- GMutex mutex; /* Verrou pour l'accès */
- GCond cond; /* Réveil pour un traitement */
- GCond wait_cond; /* Réveil d'attente de fin */
- gint pending; /* Tâches en cours d'exécution */
-
- GThread **threads; /* Procédure de traitement */
- guint threads_count; /* Nombre de procédures */
- bool force_exit; /* Procédure d'arrêt */
-
- wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */
- void *data; /* Données à associer */
-
-} GWorkGroup;
-
-/* File de traitement pour un type donné (classe) */
-typedef struct _GWorkGroupClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-} GWorkGroupClass;
-
-
-/* Indique le type défini pour les groupes de travail. */
-static GType g_work_group_get_type(void);
-
-/* Initialise la classe des groupes de travail. */
-static void g_work_group_class_init(GWorkGroupClass *);
-
-/* Initialise une instance de groupe de travail. */
-static void g_work_group_init(GWorkGroup *);
-
-/* Supprime toutes les références externes. */
-static void g_work_group_dispose(GWorkGroup *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_work_group_finalize(GWorkGroup *);
-
-/* Crée un nouveau thread dédié à un type de travaux donné. */
-static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *);
-
-/* Fournit l'identifiant associé à un groupe de travail. */
-static wgroup_id_t g_work_group_get_id(const GWorkGroup *);
-
-/* Place une nouvelle tâche en attente dans une file dédiée. */
-static void g_work_group_schedule(GWorkGroup *, GDelayedWork *);
-
-/* Assure le traitement en différé. */
-static void *g_work_group_process(GWorkGroup *);
-
-/* Détermine si le groupe est vide de toute programmation. */
-static bool g_work_group_is_empty(GWorkGroup *);
-
-/* Attend que toutes les tâches d'un groupe soient traitées. */
-static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *);
-
-/* Modifie les conditions d'attente des fins d'exécutions. */
-static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *);
-
-/* Force un réveil d'une attente en cours pour la confirmer. */
-static void g_work_group_wake_up_waiters(GWorkGroup *);
-
-
-
-/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
-
-
-/* Gestionnaire des travaux différés (instance) */
-struct _GWorkQueue
-{
- GObject parent; /* A laisser en premier */
-
- wgroup_id_t generator; /* Générateur d'identifiants */
-
- GWorkGroup **groups; /* Files de traitement */
- size_t groups_count; /* Nombre de files internes */
- GMutex mutex; /* Verrou pour l'accès */
- GCond wait_all; /* Réveil d'attente globale */
-
-};
-
-/* Gestionnaire des travaux différés (classe) */
-struct _GWorkQueueClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-};
/* Initialise la classe des travaux différés. */
@@ -179,641 +45,13 @@ static void g_work_queue_dispose(GWorkQueue *);
static void g_work_queue_finalize(GWorkQueue *);
/* Donne l'assurance de l'existence d'un groupe de travail. */
-static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *);
+static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, guint);
/* Fournit le groupe de travail correspondant à un identifiant. */
static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t);
-/* ---------------------------------------------------------------------------------- */
-/* TACHE DIFFEREE DANS LE TEMPS */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour les travaux différés. */
-G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe des travaux différés. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_class_init(GDelayedWorkClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose;
- object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize;
-
- g_signal_new("work-completed",
- G_TYPE_DELAYED_WORK,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(GDelayedWorkClass, work_completed),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = instance à initialiser. *
-* *
-* Description : Initialise une instance de travail différé. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_init(GDelayedWork *work)
-{
- work->completed = false;
- g_mutex_init(&work->mutex);
- g_cond_init(&work->cond);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_dispose(GDelayedWork *work)
-{
- g_mutex_clear(&work->mutex);
- g_cond_clear(&work->cond);
-
- G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_finalize(GDelayedWork *work)
-{
- G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = travail à effectuer. *
-* status = barre de statut à tenir informée. *
-* *
-* Description : Mène l'opération programmée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status)
-{
- G_DELAYED_WORK_GET_CLASS(work)->run(work, status);
-
- g_mutex_lock(&work->mutex);
-
- work->completed = true;
-
- g_cond_signal(&work->cond);
- g_mutex_unlock(&work->mutex);
-
- g_signal_emit_by_name(work, "work-completed");
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : work = travail à surveiller. *
-* *
-* Description : Attend la fin de l'exécution d'une tâche donnée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_delayed_work_wait_for_completion(GDelayedWork *work)
-{
- g_mutex_lock(&work->mutex);
-
- while (!work->completed)
- g_cond_wait(&work->cond, &work->mutex);
-
- g_mutex_unlock(&work->mutex);
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* THREADS DES TRAITEMENTS DEDIES */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour les groupes de travail. */
-G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe des groupes de travail. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_class_init(GWorkGroupClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose;
- object->finalize = (GObjectFinalizeFunc)g_work_group_finalize;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = instance à initialiser. *
-* *
-* Description : Initialise une instance de groupe de travail. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_init(GWorkGroup *group)
-{
- group->works = NULL;
-
- g_mutex_init(&group->mutex);
- g_cond_init(&group->cond);
- g_cond_init(&group->wait_cond);
-
- g_atomic_int_set(&group->pending, 0);
-
- group->threads = NULL;
- group->threads_count = 0;
- group->force_exit = false;
-
- group->callback = NULL;
- group->data = NULL;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_dispose(GWorkGroup *group)
-{
- guint i; /* Boucle de parcours */
- GDelayedWork *work; /* Travail à oublier */
-
- group->force_exit = true;
-
- /**
- * Concernant la pose du verrou, se référer aux commentaires de la
- * fonction g_work_group_process().
- */
-
- g_mutex_lock(&group->mutex);
-
- g_cond_broadcast(&group->cond);
-
- g_mutex_unlock(&group->mutex);
-
- for (i = 0; i < group->threads_count; i++)
- g_thread_join(group->threads[i]);
-
- while (!dl_list_empty(group->works))
- {
- work = group->works;
- delayed_work_list_del(work, &group->works);
-
- g_object_unref(G_OBJECT(work));
-
- }
-
- g_mutex_clear(&group->mutex);
- g_cond_clear(&group->cond);
- g_cond_clear(&group->wait_cond);
-
- G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_finalize(GWorkGroup *group)
-{
- if (group->threads != NULL)
- free(group->threads);
-
- G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : id = identifiant accordé au nouveau groupe. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Crée un nouveau thread dédié à un type de travaux donné. *
-* *
-* Retour : Structure associée au thread mise en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count)
-{
- GWorkGroup *result; /* Traiteur à retourner */
- guint i; /* Boucle de parcours */
- char name[16]; /* Désignation humaine */
-
- result = g_object_new(G_TYPE_WORK_GROUP, NULL);
-
- result->id = id;
-
- result->threads_count = get_max_online_threads();
-
- if (count != NULL && *count < result->threads_count)
- result->threads_count = *count;
-
- result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *));
-
- for (i = 0; i < result->threads_count; i++)
- {
- snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i);
-
- result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result);
- if (!result->threads[i])
- goto start_error;
-
- }
-
- start_error:
-
- result->threads_count = i;
-
- assert(i > 0);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = gestionnaire des actions à mener. *
-* *
-* Description : Fournit l'identifiant associé à un groupe de travail. *
-* *
-* Retour : Identifiant unique attribué au groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
-{
- return group->id;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = gestionnaire des actions à mener. *
-* work = nouvelle tâche à programmer, puis effectuer. *
-* *
-* Description : Place une nouvelle tâche en attente dans une file dédiée. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
-{
- g_mutex_lock(&group->mutex);
-
- g_atomic_int_inc(&group->pending);
-
- delayed_work_list_add_tail(work, &group->works);
-
- g_cond_signal(&group->cond);
-
- g_mutex_unlock(&group->mutex);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = gestionnaire des actions à mener. *
-* *
-* Description : Assure le traitement en différé. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void *g_work_group_process(GWorkGroup *group)
-{
- GDelayedWork *work; /* Traitement à mener */
- GtkStatusStack *status; /* Zone d'info éventuelle */
-
- while (1)
- {
- g_mutex_lock(&group->mutex);
-
- while (dl_list_empty(group->works) && !group->force_exit)
- g_cond_wait(&group->cond, &group->mutex);
-
- if (group->force_exit)
- {
- g_mutex_unlock(&group->mutex);
- break;
- }
-
- work = group->works;
- delayed_work_list_del(work, &group->works);
-
- g_mutex_unlock(&group->mutex);
-
-#ifdef INCLUDE_GTK_SUPPORT
- status = get_global_status();
-#else
- status = NULL;
-#endif
- g_delayed_work_process(work, status);
-
- g_object_unref(G_OBJECT(work));
-
- /**
- * Verrou ou pas verrou ?
- *
- * La documentation de la GLib indique que ce n'est pas nécessaire :
- *
- * '''
- * It is good practice to lock the same mutex as the waiting threads
- * while calling this function, though not required.
- * '''
- *
- * Ce conseil se trouve verbatim à l'adresse :
- *
- * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast
- *
- * Dans la pratique, il peut arriver que l'attente de la fonction
- * g_work_group_wait_for_completion() ne soit jamais interrompue.
- *
- * La documentation POSIX est un peu plus orientée :
- *
- * '''
- * The pthread_cond_broadcast() functions may be called by a thread
- * whether or not it currently owns the mutex that threads calling
- * pthread_cond_wait() have associated with the condition variable
- * during their waits; however, if predictable scheduling behavior is
- * required, then that mutex shall be locked by the thread calling
- * pthread_cond_broadcast().
- * '''
- *
- * Ce passage complet est consultable à l'adresse :
- *
- * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html
- *
- * La page de manuel pthread_cond_broadcast(3) est quant à elle plus
- * directrice : aucun complément d'information sur le sujet n'est fourni
- * et les exemples associés utilisent implicement un verrou pendant
- * sont appel.
- */
-
- g_mutex_lock(&group->mutex);
-
- if (g_atomic_int_dec_and_test(&group->pending))
- g_cond_broadcast(&group->wait_cond);
-
- g_mutex_unlock(&group->mutex);
-
- }
-
- return NULL;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = gestionnaire des actions à consulter. *
-* *
-* Description : Détermine si le groupe est vide de toute programmation. *
-* *
-* Retour : Etat du groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static bool g_work_group_is_empty(GWorkGroup *group)
-{
- bool result; /* Etat à retourner */
-
- /**
- * Pour que le résultat soit exploitable, il ne doit pas varier
- * en dehors de la zone couverte par le verrou du groupe avant
- * son utilisation par l'appelant.
- *
- * Il doit donc logiquement y avoir un autre verrou en amont et,
- * comme à priori on ne devrait pas bloquer les groupes principaux
- * pour un traitement particulier, cette procédure ne devrait concerner
- * que des groupes dynamiques.
- */
-
- g_mutex_lock(&group->mutex);
-
- result = dl_list_empty(group->works);
-
- g_mutex_unlock(&group->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = groupe dont les conclusions sont attendues. *
-* queue = queue d'appartenance pour les appels externes. *
-* *
-* Description : Attend que toutes les tâches d'un groupe soient traitées. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue)
-{
- wait_for_incoming_works_cb callback; /* Procédure complémentaire */
-
- bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data)
- {
- return false;
- }
-
- callback = group->callback != NULL ? group->callback : no_extra_check;
-
- g_mutex_lock(&group->mutex);
-
- /**
- * On attend que :
- * - la liste des tâches programmées soit vide.
- * - il n'existe plus de tâche en cours.
- * - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
- */
-
- while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data))
- && !group->force_exit)
- {
- g_cond_wait(&group->wait_cond, &group->mutex);
- }
-
- g_mutex_unlock(&group->mutex);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : group = groupe dont les paramètres sont à modifier. *
-* callback = éventuelle fonction à appeler ou NULL. *
-* data = données devant accompagner l'appel. *
-* *
-* Description : Modifie les conditions d'attente des fins d'exécutions. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data)
-{
- group->callback = callback;
- group->data = data;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
-* *
-* Description : Force un réveil d'une attente en cours pour la confirmer. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_work_group_wake_up_waiters(GWorkGroup *group)
-{
- /**
- * Concernant la pose du verrou, se référer aux commentaires de la
- * fonction g_work_group_process().
- */
-
- g_mutex_lock(&group->mutex);
-
- g_cond_broadcast(&group->wait_cond);
-
- g_mutex_unlock(&group->mutex);
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* TRAITEMENT DE TACHES DIFFEREES */
-/* ---------------------------------------------------------------------------------- */
-
-
/* Indique le type défini pour le gestionnaire des travaux différés. */
G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT);
@@ -946,7 +184,7 @@ GWorkQueue *g_work_queue_new(void)
* *
* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
* id = identifiant d'un groupe de travail. *
-* count = quantité de threads à allouer. *
+* count = quantité de threads à allouer (0 pour un défaut). *
* *
* Description : Donne l'assurance de l'existence d'un groupe de travail. *
* *
@@ -956,7 +194,7 @@ GWorkQueue *g_work_queue_new(void)
* *
******************************************************************************/
-static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count)
+static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, guint count)
{
bool found; /* Bilan des recherches */
size_t i; /* Boucle de parcours */
@@ -975,8 +213,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id,
if (!found)
{
queue->groups_count++;
- queue->groups = (GWorkGroup **)realloc(queue->groups,
- queue->groups_count * sizeof(GWorkGroup *));
+ queue->groups = realloc(queue->groups, queue->groups_count * sizeof(GWorkGroup *));
group = g_work_group_new(id, count);
queue->groups[queue->groups_count - 1] = group;
@@ -991,6 +228,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id,
/******************************************************************************
* *
* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* count = quantité de threads à allouer (0 pour un défaut). *
* *
* Description : Constitue un nouveau groupe de travail. *
* *
@@ -1000,41 +238,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id,
* *
******************************************************************************/
-wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue)
-{
- wgroup_id_t result; /* Valeur à retourner */
- bool created; /* Bilan d'une tentative */
-
- g_mutex_lock(&queue->mutex);
-
- do
- {
- result = queue->generator++;
- created = g_work_queue_ensure_group_exists(queue, result, NULL);
- }
- while (!created);
-
- g_mutex_unlock(&queue->mutex);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
-* count = quantité de threads à allouer. *
-* *
-* Description : Constitue un nouveau petit groupe de travail. *
-* *
-* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
+wgroup_id_t g_work_queue_define_group(GWorkQueue *queue, guint count)
{
wgroup_id_t result; /* Valeur à retourner */
bool created; /* Bilan d'une tentative */
@@ -1044,7 +248,7 @@ wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
do
{
result = queue->generator++;
- created = g_work_queue_ensure_group_exists(queue, result, &count);
+ created = g_work_queue_ensure_group_exists(queue, result, count);
}
while (!created);
@@ -1068,7 +272,7 @@ wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
* *
******************************************************************************/
-void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id)
+void g_work_queue_delete_group(GWorkQueue *queue, wgroup_id_t id)
{
size_t i; /* Boucle de parcours */
GWorkGroup *group; /* Groupe de travail manipulé */
@@ -1088,14 +292,13 @@ void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id)
if (g_work_group_get_id(group) == id)
{
- g_object_unref(G_OBJECT(group));
+ unref_object(group);
memmove(&queue->groups[i], &queue->groups[i + 1],
(queue->groups_count - i - 1) * sizeof(GWorkGroup *));
queue->groups_count--;
- queue->groups = (GWorkGroup **)realloc(queue->groups,
- queue->groups_count * sizeof(GWorkGroup *));
+ queue->groups = realloc(queue->groups, queue->groups_count * sizeof(GWorkGroup *));
#ifndef NDEBUG
found = true;
@@ -1124,22 +327,31 @@ void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id)
* *
* Description : Place une nouvelle tâche en attente. *
* *
-* Retour : - *
+* Retour : Bilan, qui correspond à l'existence du groupe ciblé. *
* *
* Remarques : - *
* *
******************************************************************************/
-void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id)
+bool g_work_queue_schedule(GWorkQueue *queue, GGenericWork *work, wgroup_id_t id)
{
+ bool result; /* Bilan à retourner */
GWorkGroup *group; /* Groupe de travail à attendre*/
group = g_work_queue_find_group_for_id(queue, id);
assert(group != NULL);
- g_work_group_schedule(group, work);
+ result = (group != NULL);
- g_object_unref(G_OBJECT(group));
+ if (result)
+ {
+ g_work_group_schedule(group, work);
+
+ unref_object(group);
+
+ }
+
+ return result;
}
@@ -1170,7 +382,7 @@ static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t
if (g_work_group_get_id(queue->groups[i]) == id)
{
result = queue->groups[i];
- g_object_ref(G_OBJECT(result));
+ ref_object(result);
break;
}
@@ -1204,7 +416,7 @@ bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id)
if (group != NULL)
{
result = g_work_group_is_empty(group);
- g_object_unref(G_OBJECT(group));
+ unref_object(group);
}
else
@@ -1236,9 +448,43 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
if (group != NULL)
{
- g_work_group_wait_for_completion(group, queue);
- g_object_unref(G_OBJECT(group));
+ g_work_group_wait_for_completion(group);
+ unref_object(group);
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* rel = durée relative à patienter au max. en microsecondes. *
+* *
+* Description : Attend que toutes les tâches d'un groupe soient traitées. *
+* *
+* Retour : Bilan de l'attente : false en cas d'expiration, true sinon. *
+* *
+* Remarques : Cette fonction est originellement dédiée à un usage Python. *
+* *
+******************************************************************************/
+
+bool g_work_queue_wait_timed_for_completion(GWorkQueue *queue, wgroup_id_t id, gint64 rel)
+{
+ bool result; /* Bilan d'attente à renvoyer */
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ result = g_work_group_wait_timed_for_completion(group, rel);
+ unref_object(group);
}
+ else
+ result = true;
+
+ return result;
}
@@ -1299,36 +545,6 @@ void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t
* *
* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
* id = identifiant d'un groupe de travail. *
-* callback = éventuelle fonction à appeler ou NULL. *
-* data = données devant accompagner l'appel. *
-* *
-* Description : Modifie les conditions d'attente des fins d'exécutions. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data)
-{
- GWorkGroup *group; /* Groupe de travail à traiter */
-
- group = g_work_queue_find_group_for_id(queue, id);
-
- if (group != NULL)
- {
- g_work_group_set_extra_wait_callback(group, callback, data);
- g_object_unref(G_OBJECT(group));
- }
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
-* id = identifiant d'un groupe de travail. *
* *
* Description : Force un réveil d'une attente en cours pour la confirmer. *
* *
@@ -1347,7 +563,7 @@ void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id)
if (group != NULL)
{
g_work_group_wake_up_waiters(group);
- g_object_unref(G_OBJECT(group));
+ unref_object(group);
}
}
diff --git a/src/glibext/workqueue.h b/src/glibext/workqueue.h
index 89eed12..963d86a 100644
--- a/src/glibext/workqueue.h
+++ b/src/glibext/workqueue.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * delayed.h - prototypes pour la gestion des travaux différés
+ * workqueue.h - prototypes pour la gestion des travaux différés
*
- * Copyright (C) 2009-2018 Cyrille Bagard
+ * Copyright (C) 2009-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,86 +21,34 @@
*/
-#ifndef _GLIBEXT_DELAYED_H
-#define _GLIBEXT_DELAYED_H
+#ifndef _GLIBEXT_WORKQUEUE_H
+#define _GLIBEXT_WORKQUEUE_H
-#include <glib-object.h>
#include <stdbool.h>
-#include <stdint.h>
+#include "helpers.h"
+#include "workgroup.h"
-/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
-#define G_TYPE_DELAYED_WORK g_delayed_work_get_type()
-#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork))
-#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type()))
-#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
-#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK))
-#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+#define G_TYPE_WORK_QUEUE (g_work_queue_get_type())
+DECLARE_GTYPE(GWorkQueue, g_work_queue, G, WORK_QUEUE);
-/* Travail différé (instance) */
-typedef struct _GDelayedWork GDelayedWork;
-
-/* Travail différé (classe) */
-typedef struct _GDelayedWorkClass GDelayedWorkClass;
-
-
-/* Indique le type défini pour les travaux différés. */
-GType g_delayed_work_get_type(void);
-
-/* Attend la fin de l'exécution d'une tâche donnée. */
-void g_delayed_work_wait_for_completion(GDelayedWork *);
-
-
-
-/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
-
-
-#define G_TYPE_WORK_QUEUE g_work_queue_get_type()
-#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue))
-#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type()))
-#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass))
-#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE))
-#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass))
-
-
-/* Gestionnaire des travaux différés (instance) */
-typedef struct _GWorkQueue GWorkQueue;
-
-/* Gestionnaire des travaux différés (classe) */
-typedef struct _GWorkQueueClass GWorkQueueClass;
-
-
-/**
- * Identifiant unique pour groupe de travail.
- *
- * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait
- * pas explicitement le type 'unsigned long long'.
- */
-typedef uint64_t wgroup_id_t;
-
-
-/* Indique le type défini pour le gestionnaire des travaux différés. */
-GType g_work_queue_get_type(void);
/* Créé un nouveau gestionnaire de tâches parallèles. */
GWorkQueue *g_work_queue_new(void);
/* Constitue un nouveau groupe de travail. */
-wgroup_id_t g_work_queue_define_work_group(GWorkQueue *);
-
-/* Constitue un nouveau petit groupe de travail. */
-wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint);
+wgroup_id_t g_work_queue_define_group(GWorkQueue *, guint);
/* Dissout un groupe de travail existant. */
-void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t);
+void g_work_queue_delete_group(GWorkQueue *, wgroup_id_t);
/* Place une nouvelle tâche en attente. */
-void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t);
+bool g_work_queue_schedule(GWorkQueue *, GGenericWork *, wgroup_id_t);
/* Détermine si un groupe est vide de toute programmation. */
bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t);
@@ -108,20 +56,15 @@ bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t);
/* Attend que toutes les tâches d'un groupe soient traitées. */
void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t);
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+bool g_work_queue_wait_timed_for_completion(GWorkQueue *, wgroup_id_t, gint64);
+
/* Attend que toutes les tâches de tout groupe soient traitées. */
void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t);
-
-/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
-typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *);
-
-
-/* Modifie les conditions d'attente des fins d'exécutions. */
-void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *);
-
/* Force un réveil d'une attente en cours pour la confirmer. */
void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t);
-#endif /* _GLIBEXT_DELAYED_H */
+#endif /* _GLIBEXT_WORKQUEUE_H */
diff --git a/tests/glibext/work.py b/tests/glibext/work.py
new file mode 100644
index 0000000..808e25e
--- /dev/null
+++ b/tests/glibext/work.py
@@ -0,0 +1,26 @@
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.glibext import GenericWork
+
+
+class TestWorks(ChrysalideTestCase):
+ """TestCase for glibext.GenericWork"""
+
+ def testBasicWorkImplementation(self):
+ """Implement a basic work."""
+
+ class BasicWork(GenericWork):
+ def __init__(self, lst):
+ super(BasicWork, self).__init__()
+ self._lst = lst
+ def _run(self):
+ self._lst.append('done')
+
+ test = []
+
+ work = BasicWork(test)
+
+ work.process()
+
+ self.assertEqual(len(test), 1)
+ self.assertEqual(test[0], 'done')
diff --git a/tests/glibext/workqueue.py b/tests/glibext/workqueue.py
new file mode 100644
index 0000000..203970b
--- /dev/null
+++ b/tests/glibext/workqueue.py
@@ -0,0 +1,49 @@
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.glibext import GenericWork, WorkQueue
+from threading import Lock
+
+
+class TestWorks(ChrysalideTestCase):
+ """TestCase for glibext.*Work*"""
+
+ def testBasicWorkQueueBehaviour(self):
+ """Check the default basic behaviour of a work queue."""
+
+ queue = WorkQueue()
+
+ ret = queue.is_empty(123)
+ self.assertTrue(ret)
+
+
+ def testWorkScheduling(self):
+ """Check scheduled works results."""
+
+ class SchedulableWork(GenericWork):
+ def __init__(self, lck, val):
+ super(SchedulableWork, self).__init__()
+ self._lck = lck
+ self._val = val
+ def _run(self):
+ self._lck.acquire()
+ self._val['integer'] += 1
+ self._lck.release()
+
+ lock = Lock()
+ value = { 'integer': 0 }
+
+ queue = WorkQueue()
+
+ gid = queue.define_group(4)
+
+ count = 31
+
+ for i in range(count):
+
+ work = SchedulableWork(lock, value)
+ queue.schedule(work, gid)
+
+ while not(queue.wait_for_completion(gid)):
+ pass
+
+ self.assertEqual(value['integer'], count)