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