]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/promise.qc
Merge branch 'terencehill/cl_forceplayercolors_3' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / promise.qc
1 #include "promise.qh"
2
3 .int _ref_count;
4 .void(entity this) _ref_finalize;
5
6 void ref_init(entity this, int init, void(entity this) finalize)
7 {
8     this._ref_count = init;
9     this._ref_finalize = finalize;
10 }
11
12 // todo: rename to `ref`
13 entity REF(entity this)
14 {
15     this._ref_count += 1;
16     return this;
17 }
18
19 entity unref(Promise this)
20 {
21     this._ref_count -= 1;
22     if (!this._ref_count) {
23         LOG_DEBUGF("Finalize entity %i (from %s)", this, this.sourceLoc);
24         this._ref_finalize(this);
25         return NULL;
26     }
27     return this;
28 }
29
30 enum {
31     PROMISE_PENDING,
32     PROMISE_RESOLVED,
33     PROMISE_REJECTED,
34 };
35
36 classfield(Promise) .int _promise_state;
37 classfield(Promise) .entity _promise_result;
38 classfield(Promise) .IntrusiveList _promise_handlers;
39
40 entityclass(PromiseHandler);
41 classfield(PromiseHandler) .Promise _promise_handler_ret;
42 classfield(PromiseHandler) .entity _promise_handler_data;
43 classfield(PromiseHandler) .Promise(Promise ret, entity result, entity userdata) _promise_handler_resolve;
44 classfield(PromiseHandler) .Promise(Promise ret, entity err, entity userdata) _promise_handler_reject;
45
46 void _Promise_finalize(Promise this)
47 {
48     delete(this);
49 }
50
51 Promise Promise_new_(Promise this)
52 {
53     ref_init(this, 2, _Promise_finalize);
54     this._promise_result = this; // promises default to being their own result to save on entities
55     return this;
56 }
57
58 void _Promise_handle(Promise this, PromiseHandler h);
59
60 void Promise_resolve(Promise this)
61 {
62     if (!this) {
63         LOG_SEVERE("Attempted to resolve a null promise");
64         return;
65     }
66     if (this._promise_state != PROMISE_PENDING) {
67         LOG_SEVEREF("Resolved non-pending promise %i", this);
68         return;
69     }
70     this._promise_state = PROMISE_RESOLVED;
71     if (this._promise_handlers) {
72         IL_EACH(this._promise_handlers, true, _Promise_handle(this, it));
73         IL_DELETE(this._promise_handlers);
74     }
75     unref(this);
76     return;
77 }
78
79 void Promise_reject(Promise this)
80 {
81     if (!this) {
82         LOG_SEVERE("Attempted to reject a null promise");
83         return;
84     }
85     if (this._promise_state != PROMISE_PENDING) {
86         LOG_SEVEREF("Rejected non-pending promise %i", this);
87         return;
88     }
89     this._promise_state = PROMISE_REJECTED;
90     if (this._promise_handlers) {
91         IL_EACH(this._promise_handlers, true, _Promise_handle(this, it));
92         IL_DELETE(this._promise_handlers);
93     }
94     unref(this);
95     return;
96 }
97
98 Promise _Promise_then(
99         Promise this,
100         Promise ret,
101         Promise(Promise ret, entity result, entity userdata) onResolve,
102         Promise(Promise ret, entity result, entity userdata) onReject,
103         entity userdata
104 );
105
106 void _Promise_handle(Promise this, PromiseHandler h)
107 {
108     switch (this._promise_state) {
109         case PROMISE_PENDING:
110             if (!this._promise_handlers) {
111                 this._promise_handlers = IL_NEW();
112             }
113             IL_PUSH(this._promise_handlers, h);
114             break;
115         case PROMISE_RESOLVED: {
116             Promise ret = h._promise_handler_ret;
117             Promise p = h._promise_handler_resolve(ret, this._promise_result, h._promise_handler_data);
118             if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret
119             delete(h);
120             break;
121         }
122         case PROMISE_REJECTED: {
123             Promise ret = h._promise_handler_ret;
124             Promise p = h._promise_handler_reject(ret, this._promise_result, h._promise_handler_data);
125             if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret
126             delete(h);
127             break;
128         }
129     }
130 }
131
132 void _Promise_done(
133         Promise this,
134         Promise(Promise ret, entity result, entity userdata) onResolve,
135         Promise(Promise ret, entity err, entity userdata) onReject,
136         Promise ret,
137         entity userdata
138 )
139 {
140     PromiseHandler h = new_pure(PromiseHandler);
141     h._promise_handler_ret = ret;
142     h._promise_handler_data = userdata;
143     h._promise_handler_resolve = onResolve;
144     h._promise_handler_reject = onReject;
145     _Promise_handle(this, h);
146 }
147
148 Promise _Promise_onResolve_default(Promise ret, entity result, entity userdata)
149 {
150     ret._promise_result = result;
151     Promise_resolve(ret);
152     return ret;
153 }
154
155 Promise _Promise_onReject_default(Promise ret, entity err, entity userdata)
156 {
157     ret._promise_result = err;
158     Promise_reject(ret);
159     return ret;
160 }
161
162 Promise _Promise_then(
163         Promise this,
164         Promise ret,
165         Promise(Promise ret, entity result, entity userdata) onResolve,
166         Promise(Promise ret, entity result, entity userdata) onReject,
167         entity userdata
168 )
169 {
170     _Promise_done(
171             this,
172             (onResolve ? onResolve : _Promise_onResolve_default),
173             (onReject ? onReject : _Promise_onReject_default),
174             ret,
175             userdata
176     );
177     return ret;
178 }
179
180 Promise Promise_then_(
181         Promise this,
182         Promise ret,
183         Promise(Promise ret, entity result, entity userdata) onResolve,
184         entity userdata
185 )
186 {
187     unref(ret); // ret is a temporary
188     return _Promise_then(this, ret, onResolve, func_null, userdata);
189 }
190
191 Promise Promise_catch_(
192         Promise this,
193         Promise ret,
194         Promise(Promise ret, entity result, entity userdata) onReject,
195         entity userdata
196 )
197 {
198     unref(ret); // ret is a temporary
199     return _Promise_then(this, ret, func_null, onReject, userdata);
200 }
201
202 // utils
203
204 #ifndef MENUQC
205
206 Promise Promise_sleep(float n)
207 {
208     Promise p = unref(Promise_new());
209     setthink(p, Promise_resolve);
210     p.nextthink = time + n;
211     return p;
212 }
213
214 #endif