source: trunk/third/glib2/gobject/gclosure.c @ 18159

Revision 18159, 17.3 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18158, which included commits to RCS files with non-trunk default branches.
Line 
1/* GObject - GLib Type, Object, Parameter and Signal Library
2 * Copyright (C) 2000-2001 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20#include        "gclosure.h"
21
22#include        "gvalue.h"
23#include        <string.h>
24
25
26/* FIXME: need caching allocators
27 */
28
29#define CLOSURE_MAX_REF_COUNT           ((1 << 15) - 1)
30#define CLOSURE_MAX_N_GUARDS            ((1 << 1) - 1)
31#define CLOSURE_MAX_N_FNOTIFIERS        ((1 << 2) - 1)
32#define CLOSURE_MAX_N_INOTIFIERS        ((1 << 8) - 1)
33#define CLOSURE_N_MFUNCS(cl)            ((cl)->meta_marshal + \
34                                         ((cl)->n_guards << 1L))
35/* same as G_CLOSURE_N_NOTIFIERS() (keep in sync) */
36#define CLOSURE_N_NOTIFIERS(cl)         (CLOSURE_N_MFUNCS (cl) + \
37                                         (cl)->n_fnotifiers + \
38                                         (cl)->n_inotifiers)
39enum {
40  FNOTIFY,
41  INOTIFY,
42  PRE_NOTIFY,
43  POST_NOTIFY
44};
45
46
47/* --- functions --- */
48GClosure*
49g_closure_new_simple (guint           sizeof_closure,
50                      gpointer        data)
51{
52  GClosure *closure;
53
54  g_return_val_if_fail (sizeof_closure >= sizeof (GClosure), NULL);
55
56  closure = g_malloc (sizeof_closure);
57  closure->ref_count = 1;
58  closure->meta_marshal = 0;
59  closure->n_guards = 0;
60  closure->n_fnotifiers = 0;
61  closure->n_inotifiers = 0;
62  closure->in_inotify = FALSE;
63  closure->floating = TRUE;
64  closure->derivative_flag = 0;
65  closure->in_marshal = FALSE;
66  closure->is_invalid = FALSE;
67  closure->marshal = NULL;
68  closure->data = data;
69  closure->notifiers = NULL;
70  memset (G_STRUCT_MEMBER_P (closure, sizeof (*closure)), 0, sizeof_closure - sizeof (*closure));
71
72  return closure;
73}
74
75static inline void
76closure_invoke_notifiers (GClosure *closure,
77                          guint     notify_type)
78{
79  /* notifier layout:
80   *     meta_marshal  n_guards    n_guards     n_fnotif.  n_inotifiers
81   * ->[[meta_marshal][pre_guards][post_guards][fnotifiers][inotifiers]]
82   *
83   * CLOSURE_N_MFUNCS(cl)    = meta_marshal + n_guards + n_guards;
84   * CLOSURE_N_NOTIFIERS(cl) = CLOSURE_N_MFUNCS(cl) + n_fnotifiers + n_inotifiers
85   *
86   * constrains/catches:
87   * - closure->notifiers may be reloacted during callback
88   * - closure->n_fnotifiers and closure->n_inotifiers may change during callback
89   * - i.e. callbacks can be removed/added during invocation
90   * - have to prepare for callback removal during invocation (->marshal & ->data)
91   * - have to distinguish (->marshal & ->data) for INOTIFY/FNOTIFY (->in_inotify)
92   * + closure->n_guards is const during PRE_NOTIFY & POST_NOTIFY
93   * + closure->meta_marshal is const for all cases
94   * + none of the callbacks can cause recursion
95   * + closure->n_inotifiers is const 0 during FNOTIFY
96   */
97  switch (notify_type)
98    {
99      GClosureNotifyData *ndata;
100      guint i, offs;
101    case FNOTIFY:
102      while (closure->n_fnotifiers)
103        {
104          register guint n = --closure->n_fnotifiers;
105
106          ndata = closure->notifiers + CLOSURE_N_MFUNCS (closure) + n;
107          closure->marshal = (GClosureMarshal) ndata->notify;
108          closure->data = ndata->data;
109          ndata->notify (ndata->data, closure);
110        }
111      closure->marshal = NULL;
112      closure->data = NULL;
113      break;
114    case INOTIFY:
115      closure->in_inotify = TRUE;
116      while (closure->n_inotifiers)
117        {
118          register guint n = --closure->n_inotifiers;
119
120          ndata = closure->notifiers + CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers + n;
121          closure->marshal = (GClosureMarshal) ndata->notify;
122          closure->data = ndata->data;
123          ndata->notify (ndata->data, closure);
124        }
125      closure->marshal = NULL;
126      closure->data = NULL;
127      closure->in_inotify = FALSE;
128      break;
129    case PRE_NOTIFY:
130      i = closure->n_guards;
131      offs = closure->meta_marshal;
132      while (i--)
133        {
134          ndata = closure->notifiers + offs + i;
135          ndata->notify (ndata->data, closure);
136        }
137      break;
138    case POST_NOTIFY:
139      i = closure->n_guards;
140      offs = closure->meta_marshal + i;
141      while (i--)
142        {
143          ndata = closure->notifiers + offs + i;
144          ndata->notify (ndata->data, closure);
145        }
146      break;
147    }
148}
149
150void
151g_closure_set_meta_marshal (GClosure       *closure,
152                            gpointer        marshal_data,
153                            GClosureMarshal meta_marshal)
154{
155  GClosureNotifyData *notifiers;
156  guint n;
157
158  g_return_if_fail (closure != NULL);
159  g_return_if_fail (meta_marshal != NULL);
160  g_return_if_fail (closure->is_invalid == FALSE);
161  g_return_if_fail (closure->in_marshal == FALSE);
162  g_return_if_fail (closure->meta_marshal == 0);
163
164  n = CLOSURE_N_NOTIFIERS (closure);
165  notifiers = closure->notifiers;
166  closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1);
167  if (notifiers)
168    {
169      /* usually the meta marshal will be setup right after creation, so the
170       * g_memmove() should be rare-case scenario
171       */
172      g_memmove (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0]));
173      g_free (notifiers);
174    }
175  closure->notifiers[0].data = marshal_data;
176  closure->notifiers[0].notify = (GClosureNotify) meta_marshal;
177  closure->meta_marshal = 1;
178}
179
180void
181g_closure_add_marshal_guards (GClosure      *closure,
182                              gpointer       pre_marshal_data,
183                              GClosureNotify pre_marshal_notify,
184                              gpointer       post_marshal_data,
185                              GClosureNotify post_marshal_notify)
186{
187  guint i;
188
189  g_return_if_fail (closure != NULL);
190  g_return_if_fail (pre_marshal_notify != NULL);
191  g_return_if_fail (post_marshal_notify != NULL);
192  g_return_if_fail (closure->is_invalid == FALSE);
193  g_return_if_fail (closure->in_marshal == FALSE);
194  g_return_if_fail (closure->n_guards < CLOSURE_MAX_N_GUARDS);
195
196  closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 2);
197  if (closure->n_inotifiers)
198    closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
199                        closure->n_fnotifiers +
200                        closure->n_inotifiers + 1)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
201                                                                          closure->n_fnotifiers + 0)];
202  if (closure->n_inotifiers > 1)
203    closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
204                        closure->n_fnotifiers +
205                        closure->n_inotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
206                                                                      closure->n_fnotifiers + 1)];
207  if (closure->n_fnotifiers)
208    closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
209                        closure->n_fnotifiers + 1)] = closure->notifiers[CLOSURE_N_MFUNCS (closure) + 0];
210  if (closure->n_fnotifiers > 1)
211    closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
212                        closure->n_fnotifiers)] = closure->notifiers[CLOSURE_N_MFUNCS (closure) + 1];
213  if (closure->n_guards)
214    closure->notifiers[(closure->meta_marshal +
215                        closure->n_guards +
216                        closure->n_guards + 1)] = closure->notifiers[closure->meta_marshal + closure->n_guards];
217  i = closure->n_guards++;
218  closure->notifiers[closure->meta_marshal + i].data = pre_marshal_data;
219  closure->notifiers[closure->meta_marshal + i].notify = pre_marshal_notify;
220  closure->notifiers[closure->meta_marshal + i + 1].data = post_marshal_data;
221  closure->notifiers[closure->meta_marshal + i + 1].notify = post_marshal_notify;
222}
223
224void
225g_closure_add_finalize_notifier (GClosure      *closure,
226                                 gpointer       notify_data,
227                                 GClosureNotify notify_func)
228{
229  guint i;
230
231  g_return_if_fail (closure != NULL);
232  g_return_if_fail (notify_func != NULL);
233  g_return_if_fail (closure->n_fnotifiers < CLOSURE_MAX_N_FNOTIFIERS);
234
235  closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 1);
236  if (closure->n_inotifiers)
237    closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
238                        closure->n_fnotifiers +
239                        closure->n_inotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
240                                                                      closure->n_fnotifiers + 0)];
241  i = CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers++;
242  closure->notifiers[i].data = notify_data;
243  closure->notifiers[i].notify = notify_func;
244}
245
246void
247g_closure_add_invalidate_notifier (GClosure      *closure,
248                                   gpointer       notify_data,
249                                   GClosureNotify notify_func)
250{
251  guint i;
252
253  g_return_if_fail (closure != NULL);
254  g_return_if_fail (notify_func != NULL);
255  g_return_if_fail (closure->is_invalid == FALSE);
256  g_return_if_fail (closure->n_inotifiers < CLOSURE_MAX_N_INOTIFIERS);
257
258  closure->notifiers = g_renew (GClosureNotifyData, closure->notifiers, CLOSURE_N_NOTIFIERS (closure) + 1);
259  i = CLOSURE_N_MFUNCS (closure) + closure->n_fnotifiers + closure->n_inotifiers++;
260  closure->notifiers[i].data = notify_data;
261  closure->notifiers[i].notify = notify_func;
262}
263
264static inline gboolean
265closure_try_remove_inotify (GClosure       *closure,
266                            gpointer       notify_data,
267                            GClosureNotify notify_func)
268{
269  GClosureNotifyData *ndata, *nlast;
270
271  nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (closure) - 1;
272  for (ndata = nlast + 1 - closure->n_inotifiers; ndata <= nlast; ndata++)
273    if (ndata->notify == notify_func && ndata->data == notify_data)
274      {
275        closure->n_inotifiers -= 1;
276        if (ndata < nlast)
277          *ndata = *nlast;
278
279        return TRUE;
280      }
281  return FALSE;
282}
283
284static inline gboolean
285closure_try_remove_fnotify (GClosure       *closure,
286                            gpointer       notify_data,
287                            GClosureNotify notify_func)
288{
289  GClosureNotifyData *ndata, *nlast;
290
291  nlast = closure->notifiers + CLOSURE_N_NOTIFIERS (closure) - closure->n_inotifiers - 1;
292  for (ndata = nlast + 1 - closure->n_fnotifiers; ndata <= nlast; ndata++)
293    if (ndata->notify == notify_func && ndata->data == notify_data)
294      {
295        closure->n_fnotifiers -= 1;
296        if (ndata < nlast)
297          *ndata = *nlast;
298        if (closure->n_inotifiers)
299          closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
300                              closure->n_fnotifiers)] = closure->notifiers[(CLOSURE_N_MFUNCS (closure) +
301                                                                            closure->n_fnotifiers +
302                                                                            closure->n_inotifiers)];
303        return TRUE;
304      }
305  return FALSE;
306}
307
308GClosure*
309g_closure_ref (GClosure *closure)
310{
311  g_return_val_if_fail (closure != NULL, NULL);
312  g_return_val_if_fail (closure->ref_count > 0, NULL);
313  g_return_val_if_fail (closure->ref_count < CLOSURE_MAX_REF_COUNT, NULL);
314
315  closure->ref_count += 1;
316
317  return closure;
318}
319
320void
321g_closure_invalidate (GClosure *closure)
322{
323  g_return_if_fail (closure != NULL);
324
325  if (!closure->is_invalid)
326    {
327      closure->ref_count += 1;  /* preserve floating flag */
328      closure->is_invalid = TRUE;
329      closure_invoke_notifiers (closure, INOTIFY);
330      g_closure_unref (closure);
331    }
332}
333
334void
335g_closure_unref (GClosure *closure)
336{
337  g_return_if_fail (closure != NULL);
338  g_return_if_fail (closure->ref_count > 0);
339
340  if (closure->ref_count == 1)  /* last unref, invalidate first */
341    g_closure_invalidate (closure);
342
343  closure->ref_count -= 1;
344
345  if (closure->ref_count == 0)
346    {
347      closure_invoke_notifiers (closure, FNOTIFY);
348      g_free (closure->notifiers);
349      g_free (closure);
350    }
351}
352
353void
354g_closure_sink (GClosure *closure)
355{
356  g_return_if_fail (closure != NULL);
357  g_return_if_fail (closure->ref_count > 0);
358
359  /* floating is basically a kludge to avoid creating closures
360   * with a ref_count of 0. so the intial ref_count a closure has
361   * is unowned. with invoking g_closure_sink() code may
362   * indicate that it takes over that intiial ref_count.
363   */
364  if (closure->floating)
365    {
366      closure->floating = FALSE;
367      if (closure->ref_count > 1)
368        closure->ref_count -= 1;
369      else
370        g_closure_unref (closure);
371    }
372}
373
374void
375g_closure_remove_invalidate_notifier (GClosure      *closure,
376                                      gpointer       notify_data,
377                                      GClosureNotify notify_func)
378{
379  g_return_if_fail (closure != NULL);
380  g_return_if_fail (notify_func != NULL);
381
382  if (closure->is_invalid && closure->in_inotify && /* account removal of notify_func() while its called */
383      ((gpointer) closure->marshal) == ((gpointer) notify_func) && closure->data == notify_data)
384    closure->marshal = NULL;
385  else if (!closure_try_remove_inotify (closure, notify_data, notify_func))
386    g_warning (G_STRLOC ": unable to remove uninstalled invalidation notifier: %p (%p)",
387               notify_func, notify_data);
388}
389
390void
391g_closure_remove_finalize_notifier (GClosure      *closure,
392                                    gpointer       notify_data,
393                                    GClosureNotify notify_func)
394{
395  g_return_if_fail (closure != NULL);
396  g_return_if_fail (notify_func != NULL);
397
398  if (closure->is_invalid && !closure->in_inotify && /* account removal of notify_func() while its called */
399      ((gpointer) closure->marshal) == ((gpointer) notify_func) && closure->data == notify_data)
400    closure->marshal = NULL;
401  else if (!closure_try_remove_fnotify (closure, notify_data, notify_func))
402    g_warning (G_STRLOC ": unable to remove uninstalled finalization notifier: %p (%p)",
403               notify_func, notify_data);
404}
405
406void
407g_closure_invoke (GClosure       *closure,
408                  GValue /*out*/ *return_value,
409                  guint           n_param_values,
410                  const GValue   *param_values,
411                  gpointer        invocation_hint)
412{
413  g_return_if_fail (closure != NULL);
414
415  if (!closure->is_invalid)
416    {
417      GClosureMarshal marshal;
418      gpointer marshal_data;
419      gboolean in_marshal = closure->in_marshal;
420
421      g_return_if_fail (closure->marshal || closure->meta_marshal);
422
423      closure->ref_count += 1;  /* preserve floating flag */
424      closure->in_marshal = TRUE;
425      if (closure->meta_marshal)
426        {
427          marshal_data = closure->notifiers[0].data;
428          marshal = (GClosureMarshal) closure->notifiers[0].notify;
429        }
430      else
431        {
432          marshal_data = NULL;
433          marshal = closure->marshal;
434        }
435      if (!in_marshal)
436        closure_invoke_notifiers (closure, PRE_NOTIFY);
437      marshal (closure,
438               return_value,
439               n_param_values, param_values,
440               invocation_hint,
441               marshal_data);
442      if (!in_marshal)
443        closure_invoke_notifiers (closure, POST_NOTIFY);
444      closure->in_marshal = in_marshal;
445      g_closure_unref (closure);
446    }
447}
448
449void
450g_closure_set_marshal (GClosure       *closure,
451                       GClosureMarshal marshal)
452{
453  g_return_if_fail (closure != NULL);
454  g_return_if_fail (marshal != NULL);
455
456  if (closure->marshal && closure->marshal != marshal)
457    g_warning ("attempt to override closure->marshal (%p) with new marshal (%p)",
458               closure->marshal, marshal);
459  else
460    closure->marshal = marshal;
461}
462
463GClosure*
464g_cclosure_new (GCallback      callback_func,
465                gpointer       user_data,
466                GClosureNotify destroy_data)
467{
468  GClosure *closure;
469 
470  g_return_val_if_fail (callback_func != NULL, NULL);
471 
472  closure = g_closure_new_simple (sizeof (GCClosure), user_data);
473  if (destroy_data)
474    g_closure_add_finalize_notifier (closure, user_data, destroy_data);
475  ((GCClosure*) closure)->callback = (gpointer) callback_func;
476 
477  return closure;
478}
479
480GClosure*
481g_cclosure_new_swap (GCallback      callback_func,
482                     gpointer       user_data,
483                     GClosureNotify destroy_data)
484{
485  GClosure *closure;
486 
487  g_return_val_if_fail (callback_func != NULL, NULL);
488 
489  closure = g_closure_new_simple (sizeof (GCClosure), user_data);
490  if (destroy_data)
491    g_closure_add_finalize_notifier (closure, user_data, destroy_data);
492  ((GCClosure*) closure)->callback = (gpointer) callback_func;
493  closure->derivative_flag = TRUE;
494 
495  return closure;
496}
497
498static void
499g_type_class_meta_marshal (GClosure       *closure,
500                           GValue /*out*/ *return_value,
501                           guint           n_param_values,
502                           const GValue   *param_values,
503                           gpointer        invocation_hint,
504                           gpointer        marshal_data)
505{
506  GTypeClass *class;
507  gpointer callback;
508  /* GType itype = (GType) closure->data; */
509  guint offset = GPOINTER_TO_UINT (marshal_data);
510 
511  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
512  callback = G_STRUCT_MEMBER (gpointer, class, offset);
513  if (callback)
514    closure->marshal (closure,
515                      return_value,
516                      n_param_values, param_values,
517                      invocation_hint,
518                      callback);
519}
520
521static void
522g_type_iface_meta_marshal (GClosure       *closure,
523                           GValue /*out*/ *return_value,
524                           guint           n_param_values,
525                           const GValue   *param_values,
526                           gpointer        invocation_hint,
527                           gpointer        marshal_data)
528{
529  GTypeClass *class;
530  gpointer callback;
531  GType itype = (GType) closure->data;
532  guint offset = GPOINTER_TO_UINT (marshal_data);
533 
534  class = G_TYPE_INSTANCE_GET_INTERFACE (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
535  callback = G_STRUCT_MEMBER (gpointer, class, offset);
536  if (callback)
537    closure->marshal (closure,
538                      return_value,
539                      n_param_values, param_values,
540                      invocation_hint,
541                      callback);
542}
543
544GClosure*
545g_signal_type_cclosure_new (GType    itype,
546                            guint    struct_offset)
547{
548  GClosure *closure;
549 
550  g_return_val_if_fail (G_TYPE_IS_CLASSED (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
551  g_return_val_if_fail (struct_offset >= sizeof (GTypeClass), NULL);
552 
553  closure = g_closure_new_simple (sizeof (GClosure), (gpointer) itype);
554  if (G_TYPE_IS_INTERFACE (itype))
555    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
556  else
557    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
558 
559  return closure;
560}
Note: See TracBrowser for help on using the repository browser.