File: hsgthread.c

package info (click to toggle)
haskell-gtk 0.15.7-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 3,964 kB
  • sloc: haskell: 3,346; ansic: 826; makefile: 161
file content (231 lines) | stat: -rw-r--r-- 6,750 bytes parent folder | download | duplicates (4)
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* 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.
 */

#define DEFINED_LPTYPELIB
#define DEFINDE_LPTYPEINFO
#define DEFINED_LPTYPECOMP
#define DEFINE_LPCREATETYPEINFO
#define DEFINED_LPDISPATCH

#include <glib.h>
#include <gdk/gdk.h>
#include "hsgthread.h"

#if defined( WIN32 )
#include <windows.h>
#include <stdlib.h>
#include <fcntl.h>
#endif

#undef DEBUG

static int threads_initialised = 0;
#if defined( WIN32 )
static CRITICAL_SECTION gtk2hs_finalizer_mutex;
#else
static GMutex gtk2hs_finalizer_mutex;
#endif
static GSource* gtk2hs_finalizer_source;
static guint gtk2hs_finalizer_id;
static GArray* gtk2hs_finalizers;

gboolean gtk2hs_run_finalizers(gpointer data);

#if defined( WIN32 ) && GLIB_CHECK_VERSION(2,32,0)
static GRecMutex recursive_mutex;

void imp_rec_lock() {
    g_rec_mutex_lock(&recursive_mutex);
}

void imp_rec_unlock() {
    g_rec_mutex_unlock(&recursive_mutex);
}
#endif

/* Initialize the default _fmode on WIN32. */
void gtk2hs_initialise (void) {
#if defined( WIN32 ) && defined( GTK2HS_SET_FMODE_BINARY )
	/* Some Windows GTK binraries (current Fedora MinGW ones) do */
	/* not open files in binary mode.  This is a work around.    */
    HANDLE handle = LoadLibrary("MSVCRT.dll");
    if(!handle) { 
        fprintf(stderr, "Warning: failed to load MSVCRT.dll, ");
        fprintf(stderr, "binary mode was not set!\n");
        return;
    }
    
    int *_fmode_ptr = GetProcAddress(handle, "_fmode");
    if(!_fmode_ptr) {
        fprintf(stderr, "Warning: failed to load address of _fmode from MSVCRT.dll, ");
        fprintf(stderr, "binary mode was not set!\n");
        return;
    }
    *_fmode_ptr = _O_BINARY;
#endif
}

/* 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_mutex_init(&gtk2hs_finalizer_mutex);
#endif

#if defined( WIN32 ) && GLIB_CHECK_VERSION(2,32,0)
    g_rec_mutex_init(&recursive_mutex);
    
    gdk_threads_set_lock_functions(imp_rec_lock, imp_rec_unlock);
#endif
    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_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_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_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_mutex_unlock(&gtk2hs_finalizer_mutex);
#endif
  }

  gdk_threads_leave();

  return FALSE;
}