]> git.decadent.org.uk Git - ion3.git/blob - libmainloop/defer.c
[svn-inject] Installing original source of ion3
[ion3.git] / libmainloop / defer.c
1 /*
2  * ion/libmainloop/defer.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
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 bool mainloop_defer_action_on_list(Obj *obj, WDeferredAction *action, 
89                                    WDeferred **list)
90 {
91     WDeferred *d;
92     
93     d=alloc_defer();
94     
95     if(d==NULL){
96         warn_err();
97         return FALSE;
98     }
99     
100     d->action=action;
101     d->list=list;
102     d->fn=extl_fn_none();
103     
104     if(obj!=NULL)
105         watch_setup(&(d->watch), obj, defer_watch_handler);
106     
107     LINK_ITEM(*list, d, next, prev);
108     
109     return TRUE;
110 }
111
112
113 bool mainloop_defer_action(Obj *obj, WDeferredAction *action)
114 {
115     return mainloop_defer_action_on_list(obj, action, &deferred);
116 }
117
118
119 bool mainloop_defer_destroy(Obj *obj)
120 {
121     if(OBJ_IS_BEING_DESTROYED(obj))
122         return FALSE;
123     
124     return mainloop_defer_action(obj, destroy_obj);
125 }
126     
127
128 bool mainloop_defer_extl_on_list(ExtlFn fn, WDeferred **list)
129 {
130     WDeferred *d;
131     
132     d=alloc_defer();
133     
134     if(d==NULL){
135         warn_err();
136         return FALSE;
137     }
138     
139     d->action=NULL;
140     d->list=list;
141     d->fn=extl_ref_fn(fn);
142     
143     watch_init(&(d->watch));
144     
145     LINK_ITEM(*list, d, next, prev);
146     
147     return TRUE;
148 }
149
150
151 /*EXTL_DOC
152  * Defer execution of \var{fn} until the main loop.
153  */
154 EXTL_SAFE
155 EXTL_EXPORT_AS(mainloop, defer)
156 bool mainloop_defer_extl(ExtlFn fn)
157 {
158     return mainloop_defer_extl_on_list(fn, &deferred);
159 }
160
161
162 static void do_execute(WDeferred *d)
163 {
164     Obj *obj=d->watch.obj;
165     WDeferredAction *a=d->action;
166     ExtlFn fn=d->fn;
167     
168     watch_reset(&(d->watch));
169     free_defer(d);
170     
171     if(a!=NULL){
172         if(obj!=NULL)
173             a(obj);
174     }else if(fn!=extl_fn_none()){
175         extl_call(fn, NULL, NULL);
176         extl_unref_fn(fn);
177     }
178 }
179
180
181 void mainloop_execute_deferred_on_list(WDeferred **list)
182 {
183     Obj *obj;
184     void (*action)(Obj*);
185     
186     while(*list!=NULL){
187         WDeferred *d=*list;
188         UNLINK_ITEM(*list, d, next, prev);
189         do_execute(d);
190     }
191 }
192
193
194 void mainloop_execute_deferred()
195 {
196     mainloop_execute_deferred_on_list(&deferred);
197 }
198