]> git.decadent.org.uk Git - ion3.git/blob - libmainloop/defer.c
304a37caf5e3f08620d276d6ed656cdd92c4d608
[ion3.git] / libmainloop / defer.c
1 /*
2  * ion/libmainloop/defer.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2008. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 /* This file contains routines for deferred execution of potentially
10  * dangerous actions. They're called upon returning to the main
11  * loop.
12  */
13
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"
23
24
25 DECLSTRUCT(WDeferred){
26     Watch watch;
27     WDeferredAction *action;
28     ExtlFn fn;
29     WDeferred *next, *prev;
30     WDeferred **list;
31 };
32
33
34 static WDeferred *deferred=NULL;
35
36
37 #define N_DBUF 16
38
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.
42  */
43 static WDeferred dbuf[N_DBUF];
44 static int dbuf_used=0;
45
46
47 static WDeferred *alloc_defer()
48 {
49     int i;
50     
51     /* Keeping it simple -- this naive loop should do it
52      * as N_DBUF is small.
53      */
54     for(i=0; i<N_DBUF; i++){
55         if(!dbuf_used&(1<<i)){
56             dbuf_used|=(1<<i);
57             return dbuf+i;
58         }
59     }
60     return ALLOC(WDeferred);
61 }
62
63
64 static void free_defer(WDeferred *d)
65 {
66     if(d>=dbuf && d<dbuf+N_DBUF){
67         dbuf_used&=~1<<((d-dbuf)/sizeof(WDeferred));
68         return;
69     }
70     FREE(d);
71 }
72
73
74 static void defer_watch_handler(Watch *w, Obj *obj)
75 {
76     WDeferred *d=(WDeferred*)w;
77     
78     UNLINK_ITEM(*(WDeferred**)(d->list), d, next, prev);
79     
80     free_defer(d);
81     
82     D(warn(TR("Object destroyed while deferred actions are still pending.")));
83 }
84
85
86 static bool already_deferred(Obj *obj, WDeferredAction *action, 
87                              WDeferred *list)
88 {
89     WDeferred *d;
90     
91     for(d=list; d!=NULL; d=d->next){
92         if(d->action==action && d->watch.obj==obj)
93             return TRUE;
94     }
95     
96     return FALSE;
97 }
98
99
100 bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, 
101                                    WDeferred **list)
102 {
103     WDeferred *d;
104     
105     if(already_deferred(obj, action, *list))
106         return TRUE;
107     
108     d=alloc_defer();
109     
110     if(d==NULL){
111         warn_err();
112         return FALSE;
113     }
114     
115     d->action=action;
116     d->list=list;
117     d->fn=extl_fn_none();
118     watch_init(&(d->watch));
119
120     if(obj!=NULL)
121         watch_setup(&(d->watch), obj, defer_watch_handler);
122     
123     LINK_ITEM(*list, d, next, prev);
124     
125     return TRUE;
126 }
127
128
129 bool mainloop_defer_action(Obj *obj, WDeferredAction *action)
130 {
131     return mainloop_defer_action_on_list(obj, action, &deferred);
132 }
133
134
135 bool mainloop_defer_destroy(Obj *obj)
136 {
137     if(OBJ_IS_BEING_DESTROYED(obj))
138         return FALSE;
139     
140     return mainloop_defer_action(obj, destroy_obj);
141 }
142     
143
144 bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list)
145 {
146     WDeferred *d;
147     
148     d=alloc_defer();
149     
150     if(d==NULL){
151         warn_err();
152         return FALSE;
153     }
154     
155     d->action=NULL;
156     d->list=list;
157     d->fn=extl_ref_fn(fn);
158     watch_init(&(d->watch));
159     
160     LINK_ITEM(*list, d, next, prev);
161     
162     return TRUE;
163 }
164
165
166 /*EXTL_DOC
167  * Defer execution of \var{fn} until the main loop.
168  */
169 EXTL_SAFE
170 EXTL_EXPORT_AS(mainloop, defer)
171 bool mainloop_defer_extl(ExtlFn fn)
172 {
173     return mainloop_defer_extl_on_list(fn, &deferred);
174 }
175
176
177 static void do_execute(WDeferred *d)
178 {
179     Obj *obj=d->watch.obj;
180     WDeferredAction *a=d->action;
181     ExtlFn fn=d->fn;
182     
183     watch_reset(&(d->watch));
184     free_defer(d);
185     
186     if(a!=NULL){
187         /* The deferral should not be on the list, if there
188          * was an object, and it got destroyed.
189          */
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);
195     }
196 }
197
198
199 void mainloop_execute_deferred_on_list(WDeferred **list)
200 {
201     Obj *obj;
202     void (*action)(Obj*);
203     
204     while(*list!=NULL){
205         WDeferred *d=*list;
206         UNLINK_ITEM(*list, d, next, prev);
207         do_execute(d);
208     }
209 }
210
211
212 void mainloop_execute_deferred()
213 {
214     mainloop_execute_deferred_on_list(&deferred);
215 }
216