File: hsgthread.c

package info (click to toggle)
haskell-gtk 0.11.0-5
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 2,920 kB
  • ctags: 82
  • sloc: haskell: 1,929; ansic: 714; sh: 5; makefile: 3
file content (185 lines) | stat: -rw-r--r-- 5,504 bytes parent folder | download | duplicates (2)
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(&gtk2hs_finalizer_mutex);
#else
    g_static_mutex_init(&gtk2hs_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(&gtk2hs_finalizer_mutex);
#else
    g_static_mutex_lock(&gtk2hs_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, &gtk2hs_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(&gtk2hs_finalizer_mutex);
#else
    g_static_mutex_unlock(&gtk2hs_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(&gtk2hs_finalizer_mutex);
#else
    g_static_mutex_lock(&gtk2hs_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(&gtk2hs_finalizer_mutex);
#else
    g_static_mutex_unlock(&gtk2hs_finalizer_mutex);
#endif
  }

  gdk_threads_leave();

  return FALSE;
}