]> git.decadent.org.uk Git - ion3.git/blob - libmainloop/defer.c
c8d56163761dfd43ddb2549d14e8a9e0c0b571e7
[ion3.git] / libmainloop / defer.c
1 /*
2  * ion/libmainloop/defer.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 /* This file contains routines for deferred execution of potentially
13  * dangerous actions. They're called upon returning to the main
14  * loop.
15  */
16
17 #include <libtu/obj.h>
18 #include <libtu/objp.h>
19 #include <libtu/types.h>
20 #include <libtu/misc.h>
21 #include <libtu/dlist.h>
22 #include <libtu/output.h>
23 #include <libtu/locale.h>
24 #include "defer.h"
25
26
27 DECLSTRUCT(WDeferred){
28     Watch watch;
29     WDeferredAction *action;
30     ExtlFn fn;
31     WDeferred *next, *prev;
32     WDeferred **list;
33 };
34
35
36 static WDeferred *deferred=NULL;
37
38
39 #define N_DBUF 16
40
41 /* To avoid allocating memory all the time, we use a small
42  * buffer that should be able to contain the small expected
43  * number of simultaneous deferred actions.
44  */
45 static WDeferred dbuf[N_DBUF];
46 static int dbuf_used=0;
47
48
49 static WDeferred *alloc_defer()
50 {
51     int i;
52     
53     /* Keeping it simple -- this naive loop should do it
54      * as N_DBUF is small.
55      */
56     for(i=0; i<N_DBUF; i++){
57         if(!dbuf_used&(1<<i)){
58             dbuf_used|=(1<<i);
59             return dbuf+i;
60         }
61     }
62     return ALLOC(WDeferred);
63 }
64
65
66 static void free_defer(WDeferred *d)
67 {
68     if(d>=dbuf && d<dbuf+N_DBUF){
69         dbuf_used&=~1<<((d-dbuf)/sizeof(WDeferred));
70         return;
71     }
72     FREE(d);
73 }
74
75
76 static void defer_watch_handler(Watch *w, Obj *obj)
77 {
78     WDeferred *d=(WDeferred*)w;
79     
80     UNLINK_ITEM(*(WDeferred**)(d->list), d, next, prev);
81     
82     free_defer(d);
83     
84     warn(TR("Object destroyed while deferred actions are still pending."));
85 }
86
87
88 static bool already_deferred(Obj *obj, WDeferredAction *action, 
89                              WDeferred *list)
90 {
91     WDeferred *d;
92     
93     for(d=list; d!=NULL; d=d->next){
94         if(d->action==action && d->watch.obj==obj)
95             return TRUE;
96     }
97     
98     return FALSE;
99 }
100
101
102 bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, 
103                                    WDeferred **list)
104 {
105     WDeferred *d;
106     
107     if(already_deferred(obj, action, *list))
108         return TRUE;
109     
110     d=alloc_defer();
111     
112     if(d==NULL){
113         warn_err();
114         return FALSE;
115     }
116     
117     d->action=action;
118     d->list=list;
119     d->fn=extl_fn_none();
120     
121     if(obj!=NULL)
122         watch_setup(&(d->watch), obj, defer_watch_handler);
123     
124     LINK_ITEM(*list, d, next, prev);
125     
126     return TRUE;
127 }
128
129
130 bool mainloop_defer_action(Obj *obj, WDeferredAction *action)
131 {
132     return mainloop_defer_action_on_list(obj, action, &deferred);
133 }
134
135
136 bool mainloop_defer_destroy(Obj *obj)
137 {
138     if(OBJ_IS_BEING_DESTROYED(obj))
139         return FALSE;
140     
141     return mainloop_defer_action(obj, destroy_obj);
142 }
143     
144
145 bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list)
146 {
147     WDeferred *d;
148     
149     d=alloc_defer();
150     
151     if(d==NULL){
152         warn_err();
153         return FALSE;
154     }
155     
156     d->action=NULL;
157     d->list=list;
158     d->fn=extl_ref_fn(fn);
159     
160     watch_init(&(d->watch));
161     
162     LINK_ITEM(*list, d, next, prev);
163     
164     return TRUE;
165 }
166
167
168 /*EXTL_DOC
169  * Defer execution of \var{fn} until the main loop.
170  */
171 EXTL_SAFE
172 EXTL_EXPORT_AS(mainloop, defer)
173 bool mainloop_defer_extl(ExtlFn fn)
174 {
175     return mainloop_defer_extl_on_list(fn, &deferred);
176 }
177
178
179 static void do_execute(WDeferred *d)
180 {
181     Obj *obj=d->watch.obj;
182     WDeferredAction *a=d->action;
183     ExtlFn fn=d->fn;
184     
185     watch_reset(&(d->watch));
186     free_defer(d);
187     
188     if(a!=NULL){
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