2 * ion/libmainloop/defer.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
9 /* This file contains routines for deferred execution of potentially
10 * dangerous actions. They're called upon returning to the main
14 #include <libtu/obj.h>
15 #include <libtu/objp.h>
16 #include <libtu/types.h>
17 #include <libtu/misc.h>
18 #include <libtu/dlist.h>
19 #include <libtu/output.h>
20 #include <libtu/locale.h>
21 #include <libtu/debug.h>
25 DECLSTRUCT(WDeferred
){
27 WDeferredAction
*action
;
29 WDeferred
*next
, *prev
;
34 static WDeferred
*deferred
=NULL
;
39 /* To avoid allocating memory all the time, we use a small
40 * buffer that should be able to contain the small expected
41 * number of simultaneous deferred actions.
43 static WDeferred dbuf
[N_DBUF
];
44 static int dbuf_used
=0;
47 static WDeferred
*alloc_defer()
51 /* Keeping it simple -- this naive loop should do it
54 for(i
=0; i
<N_DBUF
; i
++){
55 if(!(dbuf_used
&(1<<i
))){
60 return ALLOC(WDeferred
);
64 static void free_defer(WDeferred
*d
)
66 if(d
>=dbuf
&& d
<dbuf
+N_DBUF
){
67 dbuf_used
&=~1<<((d
-dbuf
)/sizeof(WDeferred
));
74 static void defer_watch_handler(Watch
*w
, Obj
*obj
)
76 WDeferred
*d
=(WDeferred
*)w
;
78 UNLINK_ITEM(*(WDeferred
**)(d
->list
), d
, next
, prev
);
82 D(warn(TR("Object destroyed while deferred actions are still pending.")));
86 static bool already_deferred(Obj
*obj
, WDeferredAction
*action
,
91 for(d
=list
; d
!=NULL
; d
=d
->next
){
92 if(d
->action
==action
&& d
->watch
.obj
==obj
)
100 bool mainloop_defer_action_on_list(Obj
*obj
, WDeferredAction
*action
,
105 if(already_deferred(obj
, action
, *list
))
117 d
->fn
=extl_fn_none();
118 watch_init(&(d
->watch
));
121 watch_setup(&(d
->watch
), obj
, defer_watch_handler
);
123 LINK_ITEM(*list
, d
, next
, prev
);
129 bool mainloop_defer_action(Obj
*obj
, WDeferredAction
*action
)
131 return mainloop_defer_action_on_list(obj
, action
, &deferred
);
135 bool mainloop_defer_destroy(Obj
*obj
)
137 if(OBJ_IS_BEING_DESTROYED(obj
))
140 return mainloop_defer_action(obj
, destroy_obj
);
144 bool mainloop_defer_extl_on_list(ExtlFn fn
, WDeferred
**list
)
157 d
->fn
=extl_ref_fn(fn
);
158 watch_init(&(d
->watch
));
160 LINK_ITEM(*list
, d
, next
, prev
);
167 * Defer execution of \var{fn} until the main loop.
170 EXTL_EXPORT_AS(mainloop
, defer
)
171 bool mainloop_defer_extl(ExtlFn fn
)
173 return mainloop_defer_extl_on_list(fn
, &deferred
);
177 static void do_execute(WDeferred
*d
)
179 Obj
*obj
=d
->watch
.obj
;
180 WDeferredAction
*a
=d
->action
;
183 watch_reset(&(d
->watch
));
187 /* The deferral should not be on the list, if there
188 * was an object, and it got destroyed.
192 }else if(fn
!=extl_fn_none()){
193 extl_call(fn
, NULL
, NULL
);
199 void mainloop_execute_deferred_on_list(WDeferred
**list
)
202 void (*action
)(Obj
*);
206 UNLINK_ITEM(*list
, d
, next
, prev
);
212 void mainloop_execute_deferred()
214 mainloop_execute_deferred_on_list(&deferred
);