1 /* GObject - GLib Type, Object, Parameter and Signal Library
2 * Copyright (C) 2000-2001 Red Hat, Inc.
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.
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.
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.
26 /* FIXME: need caching allocators
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 + \
47 /* --- functions --- */
49 g_closure_new_simple (guint sizeof_closure
,
54 g_return_val_if_fail (sizeof_closure
>= sizeof (GClosure
), NULL
);
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
;
69 closure
->notifiers
= NULL
;
70 memset (G_STRUCT_MEMBER_P (closure
, sizeof (*closure
)), 0, sizeof_closure
- sizeof (*closure
));
76 closure_invoke_notifiers (GClosure
*closure
,
80 * meta_marshal n_guards n_guards n_fnotif. n_inotifiers
81 * ->[[meta_marshal][pre_guards][post_guards][fnotifiers][inotifiers]]
83 * CLOSURE_N_MFUNCS(cl) = meta_marshal + n_guards + n_guards;
84 * CLOSURE_N_NOTIFIERS(cl) = CLOSURE_N_MFUNCS(cl) + n_fnotifiers + n_inotifiers
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
99 GClosureNotifyData
*ndata
;
102 while (closure
->n_fnotifiers
)
104 register guint n
= --closure
->n_fnotifiers
;
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
);
111 closure
->marshal
= NULL
;
112 closure
->data
= NULL
;
115 closure
->in_inotify
= TRUE
;
116 while (closure
->n_inotifiers
)
118 register guint n
= --closure
->n_inotifiers
;
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
);
125 closure
->marshal
= NULL
;
126 closure
->data
= NULL
;
127 closure
->in_inotify
= FALSE
;
130 i
= closure
->n_guards
;
131 offs
= closure
->meta_marshal
;
134 ndata
= closure
->notifiers
+ offs
+ i
;
135 ndata
->notify (ndata
->data
, closure
);
139 i
= closure
->n_guards
;
140 offs
= closure
->meta_marshal
+ i
;
143 ndata
= closure
->notifiers
+ offs
+ i
;
144 ndata
->notify (ndata
->data
, closure
);
151 g_closure_set_meta_marshal (GClosure
*closure
,
152 gpointer marshal_data
,
153 GClosureMarshal meta_marshal
)
155 GClosureNotifyData
*notifiers
;
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);
164 n
= CLOSURE_N_NOTIFIERS (closure
);
165 notifiers
= closure
->notifiers
;
166 closure
->notifiers
= g_renew (GClosureNotifyData
, NULL
, CLOSURE_N_NOTIFIERS (closure
) + 1);
169 /* usually the meta marshal will be setup right after creation, so the
170 * g_memmove() should be rare-case scenario
172 g_memmove (closure
->notifiers
+ 1, notifiers
, CLOSURE_N_NOTIFIERS (closure
) * sizeof (notifiers
[0]));
175 closure
->notifiers
[0].data
= marshal_data
;
176 closure
->notifiers
[0].notify
= (GClosureNotify
) meta_marshal
;
177 closure
->meta_marshal
= 1;
181 g_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
)
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
);
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
+
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
;
225 g_closure_add_finalize_notifier (GClosure
*closure
,
226 gpointer notify_data
,
227 GClosureNotify notify_func
)
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
);
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
;
247 g_closure_add_invalidate_notifier (GClosure
*closure
,
248 gpointer notify_data
,
249 GClosureNotify notify_func
)
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
);
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
;
264 static inline gboolean
265 closure_try_remove_inotify (GClosure
*closure
,
266 gpointer notify_data
,
267 GClosureNotify notify_func
)
269 GClosureNotifyData
*ndata
, *nlast
;
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
)
275 closure
->n_inotifiers
-= 1;
284 static inline gboolean
285 closure_try_remove_fnotify (GClosure
*closure
,
286 gpointer notify_data
,
287 GClosureNotify notify_func
)
289 GClosureNotifyData
*ndata
, *nlast
;
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
)
295 closure
->n_fnotifiers
-= 1;
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
)];
309 g_closure_ref (GClosure
*closure
)
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
);
315 closure
->ref_count
+= 1;
321 g_closure_invalidate (GClosure
*closure
)
323 g_return_if_fail (closure
!= NULL
);
325 if (!closure
->is_invalid
)
327 closure
->ref_count
+= 1; /* preserve floating flag */
328 closure
->is_invalid
= TRUE
;
329 closure_invoke_notifiers (closure
, INOTIFY
);
330 g_closure_unref (closure
);
335 g_closure_unref (GClosure
*closure
)
337 g_return_if_fail (closure
!= NULL
);
338 g_return_if_fail (closure
->ref_count
> 0);
340 if (closure
->ref_count
== 1) /* last unref, invalidate first */
341 g_closure_invalidate (closure
);
343 closure
->ref_count
-= 1;
345 if (closure
->ref_count
== 0)
347 closure_invoke_notifiers (closure
, FNOTIFY
);
348 g_free (closure
->notifiers
);
354 g_closure_sink (GClosure
*closure
)
356 g_return_if_fail (closure
!= NULL
);
357 g_return_if_fail (closure
->ref_count
> 0);
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.
364 if (closure
->floating
)
366 closure
->floating
= FALSE
;
367 if (closure
->ref_count
> 1)
368 closure
->ref_count
-= 1;
370 g_closure_unref (closure
);
375 g_closure_remove_invalidate_notifier (GClosure
*closure
,
376 gpointer notify_data
,
377 GClosureNotify notify_func
)
379 g_return_if_fail (closure
!= NULL
);
380 g_return_if_fail (notify_func
!= NULL
);
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
);
391 g_closure_remove_finalize_notifier (GClosure
*closure
,
392 gpointer notify_data
,
393 GClosureNotify notify_func
)
395 g_return_if_fail (closure
!= NULL
);
396 g_return_if_fail (notify_func
!= NULL
);
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
);
407 g_closure_invoke (GClosure
*closure
,
408 GValue
/*out*/ *return_value
,
409 guint n_param_values
,
410 const GValue
*param_values
,
411 gpointer invocation_hint
)
413 g_return_if_fail (closure
!= NULL
);
415 if (!closure
->is_invalid
)
417 GClosureMarshal marshal
;
418 gpointer marshal_data
;
419 gboolean in_marshal
= closure
->in_marshal
;
421 g_return_if_fail (closure
->marshal
|| closure
->meta_marshal
);
423 closure
->ref_count
+= 1; /* preserve floating flag */
424 closure
->in_marshal
= TRUE
;
425 if (closure
->meta_marshal
)
427 marshal_data
= closure
->notifiers
[0].data
;
428 marshal
= (GClosureMarshal
) closure
->notifiers
[0].notify
;
433 marshal
= closure
->marshal
;
436 closure_invoke_notifiers (closure
, PRE_NOTIFY
);
439 n_param_values
, param_values
,
443 closure_invoke_notifiers (closure
, POST_NOTIFY
);
444 closure
->in_marshal
= in_marshal
;
445 g_closure_unref (closure
);
450 g_closure_set_marshal (GClosure
*closure
,
451 GClosureMarshal marshal
)
453 g_return_if_fail (closure
!= NULL
);
454 g_return_if_fail (marshal
!= NULL
);
456 if (closure
->marshal
&& closure
->marshal
!= marshal
)
457 g_warning ("attempt to override closure->marshal (%p) with new marshal (%p)",
458 closure
->marshal
, marshal
);
460 closure
->marshal
= marshal
;
464 g_cclosure_new (GCallback callback_func
,
466 GClosureNotify destroy_data
)
470 g_return_val_if_fail (callback_func
!= NULL
, NULL
);
472 closure
= g_closure_new_simple (sizeof (GCClosure
), user_data
);
474 g_closure_add_finalize_notifier (closure
, user_data
, destroy_data
);
475 ((GCClosure
*) closure
)->callback
= (gpointer
) callback_func
;
481 g_cclosure_new_swap (GCallback callback_func
,
483 GClosureNotify destroy_data
)
487 g_return_val_if_fail (callback_func
!= NULL
, NULL
);
489 closure
= g_closure_new_simple (sizeof (GCClosure
), user_data
);
491 g_closure_add_finalize_notifier (closure
, user_data
, destroy_data
);
492 ((GCClosure
*) closure
)->callback
= (gpointer
) callback_func
;
493 closure
->derivative_flag
= TRUE
;
499 g_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
)
508 /* GType itype = (GType) closure->data; */
509 guint offset
= GPOINTER_TO_UINT (marshal_data
);
511 class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values
+ 0), itype
, GTypeClass
);
512 callback
= G_STRUCT_MEMBER (gpointer
, class, offset
);
514 closure
->marshal (closure
,
516 n_param_values
, param_values
,
522 g_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
)
531 GType itype
= (GType
) closure
->data
;
532 guint offset
= GPOINTER_TO_UINT (marshal_data
);
534 class = G_TYPE_INSTANCE_GET_INTERFACE (g_value_peek_pointer (param_values
+ 0), itype
, GTypeClass
);
535 callback
= G_STRUCT_MEMBER (gpointer
, class, offset
);
537 closure
->marshal (closure
,
539 n_param_values
, param_values
,
545 g_signal_type_cclosure_new (GType itype
,
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
);
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
);
557 g_closure_set_meta_marshal (closure
, GUINT_TO_POINTER (struct_offset
), g_type_class_meta_marshal
);