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