Merge branch 'contrib_goto_focus'
[notion.git] / libmainloop / defer.c
bloba9a777911daef53b657f6f0e352c0de47459cb50
1 /*
2 * ion/libmainloop/defer.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 /* This file contains routines for deferred execution of potentially
10 * dangerous actions. They're called upon returning to the main
11 * loop.
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>
22 #include "defer.h"
25 DECLSTRUCT(WDeferred){
26 Watch watch;
27 WDeferredAction *action;
28 ExtlFn fn;
29 WDeferred *next, *prev;
30 WDeferred **list;
34 static WDeferred *deferred=NULL;
37 #define N_DBUF 16
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()
49 int i;
51 /* Keeping it simple -- this naive loop should do it
52 * as N_DBUF is small.
54 for(i=0; i<N_DBUF; i++){
55 if(!(dbuf_used&(1<<i))){
56 dbuf_used|=(1<<i);
57 return dbuf+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));
68 return;
70 FREE(d);
74 static void defer_watch_handler(Watch *w, Obj *UNUSED(obj))
76 WDeferred *d=(WDeferred*)w;
78 UNLINK_ITEM(*(WDeferred**)(d->list), d, next, prev);
80 free_defer(d);
82 D(warn(TR("Object destroyed while deferred actions are still pending.")));
86 static bool already_deferred(Obj *obj, WDeferredAction *action,
87 WDeferred *list)
89 WDeferred *d;
91 for(d=list; d!=NULL; d=d->next){
92 if(d->action==action && d->watch.obj==obj)
93 return TRUE;
96 return FALSE;
100 bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action,
101 WDeferred **list)
103 WDeferred *d;
105 if(already_deferred(obj, action, *list))
106 return TRUE;
108 d=alloc_defer();
110 if(d==NULL){
111 warn_err();
112 return FALSE;
115 d->action=action;
116 d->list=list;
117 d->fn=extl_fn_none();
118 watch_init(&(d->watch));
120 if(obj!=NULL)
121 watch_setup(&(d->watch), obj, defer_watch_handler);
123 LINK_ITEM(*list, d, next, prev);
125 return TRUE;
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))
138 return FALSE;
140 return mainloop_defer_action(obj, destroy_obj);
144 bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list)
146 WDeferred *d;
148 d=alloc_defer();
150 if(d==NULL){
151 warn_err();
152 return FALSE;
155 d->action=NULL;
156 d->list=list;
157 d->fn=extl_ref_fn(fn);
158 watch_init(&(d->watch));
160 LINK_ITEM(*list, d, next, prev);
162 return TRUE;
166 /*EXTL_DOC
167 * Defer execution of \var{fn} until the main loop.
169 EXTL_SAFE
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;
181 ExtlFn fn=d->fn;
183 watch_reset(&(d->watch));
184 free_defer(d);
186 if(a!=NULL){
187 /* The deferral should not be on the list, if there
188 * was an object, and it got destroyed.
190 /*if(obj!=NULL)*/
191 a(obj);
192 }else if(fn!=extl_fn_none()){
193 extl_call(fn, NULL, NULL);
194 extl_unref_fn(fn);
199 void mainloop_execute_deferred_on_list(WDeferred **list)
201 while(*list!=NULL){
202 WDeferred *d=*list;
203 UNLINK_ITEM(*list, d, next, prev);
204 do_execute(d);
209 void mainloop_execute_deferred()
211 mainloop_execute_deferred_on_list(&deferred);