1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
/* We would use the g_thread_supported macro here, but unfortunately on
* windows GHCi's dynamic linker cannot cope with references to global
* variables imported from dlls.
*
* So instead of asking glib if we (or indeed) anyone else has initialised
* the glib gthread system, we keep track of it ourselves. We still have to
* do it in C land so the state survives :reload in GHCi. So there is the
* danger in a mixed language program, of someone else initialising the
* glib thread system and us not being aware of it. :-(
*
* Besides the interaction with ghci, we provide a variant of g_object_unref
* that is used in all objects of Gtk+ and those libraries that build on Gtk+.
* This variant enqueues the object to be finalized and adds an idle handler
* into the main loop of Gtk+ that will call the actual finalizers on the
* enqueued objects. The aim is to ensure that finalizers for objects that
* may hold Xlib or Win32 resources are only run from the thread that runs the
* main Gtk+ loop. If this is not ensured then bad things happen at least on
* Win32 since that API is making use of thread-local storage that is not
* present if the finalizers, that are run by the GC in a different thread,
* call back into Win32 without this thread-local storage.
*
* Also g_static_mutex_lock and g_static_mutex_unlock cause linking problems
* in ghci on Windows 7 (namely: HSgtk-0.10.5.o: unknown symbol
* `__imp__g_threads_got_initialized'), so we use a Win32 critical section
* instead.
*/
#include <glib.h>
#include <glib/gthread.h>
#include <gdk/gdk.h>
#include "hsgthread.h"
#if defined( WIN32 )
#include <windows.h>
#endif
#undef DEBUG
static int threads_initialised = 0;
#if defined( WIN32 )
static CRITICAL_SECTION gtk2hs_finalizer_mutex;
#else
static GStaticMutex gtk2hs_finalizer_mutex;
#endif
static GSource* gtk2hs_finalizer_source;
static guint gtk2hs_finalizer_id;
static GArray* gtk2hs_finalizers;
gboolean gtk2hs_run_finalizers(gpointer data);
/* Initialize the threads system of Gdk and Gtk. */
void gtk2hs_threads_initialise (void) {
#ifdef DEBUG
printf("gtk2hs_threads_initialise: threads_initialised=%i, g_thread_get_initialized=%i\n",
threads_initialised, g_thread_get_initialized());
#endif
if (!threads_initialised) {
threads_initialised = 1;
#if defined( WIN32 )
InitializeCriticalSection(>k2hs_finalizer_mutex);
#else
g_static_mutex_init(>k2hs_finalizer_mutex);
#endif
g_thread_init(NULL);
gdk_threads_init();
/* from here onwards, the Gdk lock is held */
gdk_threads_enter();
}
}
/* Free an object within the Gtk2Hs lock. */
void gtk2hs_g_object_unref_from_mainloop(gpointer object) {
int mutex_locked = 0;
if (threads_initialised) {
#ifdef DEBUG
printf("acquiring lock to add a %s object at %lx\n",
g_type_name(G_OBJECT_TYPE(object)), (unsigned long) object);
printf("value of lock function is %lx\n",
(unsigned long) g_thread_functions_for_glib_use.mutex_lock);
#endif
#if defined( WIN32 )
EnterCriticalSection(>k2hs_finalizer_mutex);
#else
g_static_mutex_lock(>k2hs_finalizer_mutex);
#endif
mutex_locked = 1;
}
#ifdef DEBUG
if (mutex_locked) printf("within mutex: ");
printf("adding finalizer to a %s object!\n", g_type_name(G_OBJECT_TYPE(object)));
#endif
/* Ensure that the idle handler is still installed and that
the array of objects that are to be finalized exists. */
if (gtk2hs_finalizer_id==0) {
if (gtk2hs_finalizers == NULL)
gtk2hs_finalizers = g_array_new(0, 0, sizeof(gpointer));
#ifdef DEBUG
printf("creating finalizer list.\n");
#endif
if (gtk2hs_finalizer_source != NULL) {
#ifdef DEBUG
printf("re-initializing finalizer source.\n");
#endif
g_source_destroy(gtk2hs_finalizer_source);
g_source_unref(gtk2hs_finalizer_source);
};
gtk2hs_finalizer_source = g_idle_source_new();
g_source_set_callback(gtk2hs_finalizer_source, >k2hs_run_finalizers, 0, 0);
gtk2hs_finalizer_id = g_source_attach(gtk2hs_finalizer_source, NULL);
};
/* Add the object to the list. */
g_array_append_val(gtk2hs_finalizers, object);
if (mutex_locked) {
#ifdef DEBUG
printf("releasing lock to add a %s object at %lx\n",
g_type_name(G_OBJECT_TYPE(object)), (unsigned long) object);
#endif
#if defined( WIN32 )
LeaveCriticalSection(>k2hs_finalizer_mutex);
#else
g_static_mutex_unlock(>k2hs_finalizer_mutex);
#endif
}
}
/* Run the finalizers that have been accumulated. */
gboolean gtk2hs_run_finalizers(gpointer data) {
gint index;
g_assert(gtk2hs_finalizers!=NULL);
gdk_threads_enter();
int mutex_locked = 0;
if (threads_initialised) {
#ifdef DEBUG
printf("acquiring lock to kill objects\n");
#endif
#if defined( WIN32 )
EnterCriticalSection(>k2hs_finalizer_mutex);
#else
g_static_mutex_lock(>k2hs_finalizer_mutex);
#endif
mutex_locked = 1;
}
#ifdef DEBUG
printf("running %i finalizers!\n", gtk2hs_finalizers->len);
#endif
for (index = 0; index < gtk2hs_finalizers->len; index++)
g_object_unref(g_array_index (gtk2hs_finalizers, GObject*, index));
g_array_set_size(gtk2hs_finalizers, 0);
gtk2hs_finalizer_id = 0;
if (mutex_locked) {
#ifdef DEBUG
printf("releasing lock to kill objects\n");
#endif
#if defined( WIN32 )
LeaveCriticalSection(>k2hs_finalizer_mutex);
#else
g_static_mutex_unlock(>k2hs_finalizer_mutex);
#endif
}
gdk_threads_leave();
return FALSE;
}
|