]> git.proxmox.com Git - ceph.git/blame - ceph/src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_bi_thread.c
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / src-separate / duk_bi_thread.c
CommitLineData
7c673cae
FG
1/*
2 * Thread builtins
3 */
4
5#include "duk_internal.h"
6
7/*
8 * Constructor
9 */
10
11DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_context *ctx) {
12 duk_hthread *new_thr;
13 duk_hobject *func;
14
15 /* XXX: need a duk_require_func_or_lfunc_coerce() */
16 if (!duk_is_callable(ctx, 0)) {
17 return DUK_RET_TYPE_ERROR;
18 }
19 func = duk_require_hobject_or_lfunc_coerce(ctx, 0);
20 DUK_ASSERT(func != NULL);
21
22 duk_push_thread(ctx);
23 new_thr = (duk_hthread *) duk_get_hobject(ctx, -1);
24 DUK_ASSERT(new_thr != NULL);
25 new_thr->state = DUK_HTHREAD_STATE_INACTIVE;
26
27 /* push initial function call to new thread stack; this is
28 * picked up by resume().
29 */
30 duk_push_hobject((duk_context *) new_thr, func);
31
32 return 1; /* return thread */
33}
34
35/*
36 * Resume a thread.
37 *
38 * The thread must be in resumable state, either (a) new thread which hasn't
39 * yet started, or (b) a thread which has previously yielded. This method
40 * must be called from an Ecmascript function.
41 *
42 * Args:
43 * - thread
44 * - value
45 * - isError (defaults to false)
46 *
47 * Note: yield and resume handling is currently asymmetric.
48 */
49
50DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
51 duk_hthread *thr = (duk_hthread *) ctx;
52 duk_hthread *thr_resume;
53 duk_tval tv_tmp;
54 duk_tval *tv;
55 duk_hobject *func;
56 duk_hobject *caller_func;
57 duk_small_int_t is_error;
58
59 DUK_DDD(DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T",
60 (duk_tval *) duk_get_tval(ctx, 0),
61 (duk_tval *) duk_get_tval(ctx, 1),
62 (duk_tval *) duk_get_tval(ctx, 2)));
63
64 DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
65 DUK_ASSERT(thr->heap->curr_thread == thr);
66
67 thr_resume = duk_require_hthread(ctx, 0);
68 is_error = (duk_small_int_t) duk_to_boolean(ctx, 2);
69 duk_set_top(ctx, 2);
70
71 /* [ thread value ] */
72
73 /*
74 * Thread state and calling context checks
75 */
76
77 if (thr->callstack_top < 2) {
78 DUK_DD(DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)"));
79 goto state_error;
80 }
81 DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); /* us */
82 DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
83 DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL); /* caller */
84
85 caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2);
86 if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(caller_func)) {
87 DUK_DD(DUK_DDPRINT("resume state invalid: caller must be Ecmascript code"));
88 goto state_error;
89 }
90
91 /* Note: there is no requirement that: 'thr->callstack_preventcount == 1'
92 * like for yield.
93 */
94
95 if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE &&
96 thr_resume->state != DUK_HTHREAD_STATE_YIELDED) {
97 DUK_DD(DUK_DDPRINT("resume state invalid: target thread must be INACTIVE or YIELDED"));
98 goto state_error;
99 }
100
101 DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE ||
102 thr_resume->state == DUK_HTHREAD_STATE_YIELDED);
103
104 /* Further state-dependent pre-checks */
105
106 if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
107 /* no pre-checks now, assume a previous yield() has left things in
108 * tip-top shape (longjmp handler will assert for these).
109 */
110 } else {
111 DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE);
112
113 if ((thr_resume->callstack_top != 0) ||
114 (thr_resume->valstack_top - thr_resume->valstack != 1)) {
115 goto state_invalid_initial;
116 }
117 tv = &thr_resume->valstack_top[-1];
118 DUK_ASSERT(tv >= thr_resume->valstack && tv < thr_resume->valstack_top);
119 if (!DUK_TVAL_IS_OBJECT(tv)) {
120 goto state_invalid_initial;
121 }
122 func = DUK_TVAL_GET_OBJECT(tv);
123 DUK_ASSERT(func != NULL);
124 if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
125 /* Note: cannot be a bound function either right now,
126 * this would be easy to relax though.
127 */
128 goto state_invalid_initial;
129 }
130
131 }
132
133 /*
134 * The error object has been augmented with a traceback and other
135 * info from its creation point -- usually another thread. The
136 * error handler is called here right before throwing, but it also
137 * runs in the resumer's thread. It might be nice to get a traceback
138 * from the resumee but this is not the case now.
139 */
140
141#if defined(DUK_USE_AUGMENT_ERROR_THROW)
142 if (is_error) {
143 DUK_ASSERT_TOP(ctx, 2); /* value (error) is at stack top */
144 duk_err_augment_error_throw(thr); /* in resumer's context */
145 }
146#endif
147
148#ifdef DUK_USE_DEBUG
149 if (is_error) {
150 DUK_DDD(DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T",
151 (duk_tval *) duk_get_tval(ctx, 0),
152 (duk_tval *) duk_get_tval(ctx, 1)));
153 } else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
154 DUK_DDD(DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T",
155 (duk_tval *) duk_get_tval(ctx, 0),
156 (duk_tval *) duk_get_tval(ctx, 1)));
157 } else {
158 DUK_DDD(DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T",
159 (duk_tval *) duk_get_tval(ctx, 0),
160 (duk_tval *) duk_get_tval(ctx, 1)));
161 }
162#endif
163
164 thr->heap->lj.type = DUK_LJ_TYPE_RESUME;
165
166 /* lj value2: thread */
167 DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
168 DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value2);
169 DUK_TVAL_SET_TVAL(&thr->heap->lj.value2, &thr->valstack_bottom[0]);
170 DUK_TVAL_INCREF(thr, &thr->heap->lj.value2);
171 DUK_TVAL_DECREF(thr, &tv_tmp);
172
173 /* lj value1: value */
174 DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top);
175 DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1);
176 DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[1]);
177 DUK_TVAL_INCREF(thr, &thr->heap->lj.value1);
178 DUK_TVAL_DECREF(thr, &tv_tmp);
179
180 thr->heap->lj.iserror = is_error;
181
182 DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */
183 duk_err_longjmp(thr); /* execution resumes in bytecode executor */
184 return 0; /* never here */
185
186 state_invalid_initial:
187 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid initial thread state/stack");
188 return 0; /* never here */
189
190 state_error:
191 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for resume");
192 return 0; /* never here */
193}
194
195/*
196 * Yield the current thread.
197 *
198 * The thread must be in yieldable state: it must have a resumer, and there
199 * must not be any yield-preventing calls (native calls and constructor calls,
200 * currently) in the thread's call stack (otherwise a resume would not be
201 * possible later). This method must be called from an Ecmascript function.
202 *
203 * Args:
204 * - value
205 * - isError (defaults to false)
206 *
207 * Note: yield and resume handling is currently asymmetric.
208 */
209
210DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) {
211 duk_hthread *thr = (duk_hthread *) ctx;
212 duk_tval tv_tmp;
213 duk_hobject *caller_func;
214 duk_small_int_t is_error;
215
216 DUK_DDD(DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T",
217 (duk_tval *) duk_get_tval(ctx, 0),
218 (duk_tval *) duk_get_tval(ctx, 1)));
219
220 DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
221 DUK_ASSERT(thr->heap->curr_thread == thr);
222
223 is_error = (duk_small_int_t) duk_to_boolean(ctx, 1);
224 duk_set_top(ctx, 1);
225
226 /* [ value ] */
227
228 /*
229 * Thread state and calling context checks
230 */
231
232 if (!thr->resumer) {
233 DUK_DD(DUK_DDPRINT("yield state invalid: current thread must have a resumer"));
234 goto state_error;
235 }
236 DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
237
238 if (thr->callstack_top < 2) {
239 DUK_DD(DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)"));
240 goto state_error;
241 }
242 DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); /* us */
243 DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
244 DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL); /* caller */
245
246 caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2);
247 if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(caller_func)) {
248 DUK_DD(DUK_DDPRINT("yield state invalid: caller must be Ecmascript code"));
249 goto state_error;
250 }
251
252 DUK_ASSERT(thr->callstack_preventcount >= 1); /* should never be zero, because we (Duktape.Thread.yield) are on the stack */
253 if (thr->callstack_preventcount != 1) {
254 /* Note: the only yield-preventing call is Duktape.Thread.yield(), hence check for 1, not 0 */
255 DUK_DD(DUK_DDPRINT("yield state invalid: there must be no yield-preventing calls in current thread callstack (preventcount is %ld)",
256 (long) thr->callstack_preventcount));
257 goto state_error;
258 }
259
260 /*
261 * The error object has been augmented with a traceback and other
262 * info from its creation point -- usually the current thread.
263 * The error handler, however, is called right before throwing
264 * and runs in the yielder's thread.
265 */
266
267#if defined(DUK_USE_AUGMENT_ERROR_THROW)
268 if (is_error) {
269 DUK_ASSERT_TOP(ctx, 1); /* value (error) is at stack top */
270 duk_err_augment_error_throw(thr); /* in yielder's context */
271 }
272#endif
273
274#ifdef DUK_USE_DEBUG
275 if (is_error) {
276 DUK_DDD(DUK_DDDPRINT("YIELD ERROR: value=%!T",
277 (duk_tval *) duk_get_tval(ctx, 0)));
278 } else {
279 DUK_DDD(DUK_DDDPRINT("YIELD NORMAL: value=%!T",
280 (duk_tval *) duk_get_tval(ctx, 0)));
281 }
282#endif
283
284 /*
285 * Process yield
286 *
287 * After longjmp(), processing continues in bytecode executor longjmp
288 * handler, which will e.g. update thr->resumer to NULL.
289 */
290
291 thr->heap->lj.type = DUK_LJ_TYPE_YIELD;
292
293 /* lj value1: value */
294 DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
295 DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1);
296 DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[0]);
297 DUK_TVAL_INCREF(thr, &thr->heap->lj.value1);
298 DUK_TVAL_DECREF(thr, &tv_tmp);
299
300 thr->heap->lj.iserror = is_error;
301
302 DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */
303 duk_err_longjmp(thr); /* execution resumes in bytecode executor */
304 return 0; /* never here */
305
306 state_error:
307 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for yield");
308 return 0; /* never here */
309}
310
311DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_context *ctx) {
312 duk_push_current_thread(ctx);
313 return 1;
314}