#include "promise.qh" .int _ref_count; .void(entity this) _ref_finalize; void ref_init(entity this, int init, void(entity this) finalize) { this._ref_count = init; this._ref_finalize = finalize; } // todo: rename to `ref` entity REF(entity this) { this._ref_count += 1; return this; } entity unref(Promise this) { this._ref_count -= 1; if (!this._ref_count) { LOG_DEBUGF("Finalize entity %i (from %s)", this, this.sourceLoc); this._ref_finalize(this); return NULL; } return this; } enum { PROMISE_PENDING, PROMISE_RESOLVED, PROMISE_REJECTED, }; classfield(Promise) .int _promise_state; classfield(Promise) .entity _promise_result; classfield(Promise) .IntrusiveList _promise_handlers; entityclass(PromiseHandler); classfield(PromiseHandler) .Promise _promise_handler_ret; classfield(PromiseHandler) .entity _promise_handler_data; classfield(PromiseHandler) .Promise(Promise ret, entity result, entity userdata) _promise_handler_resolve; classfield(PromiseHandler) .Promise(Promise ret, entity err, entity userdata) _promise_handler_reject; void _Promise_finalize(Promise this) { delete(this); } Promise Promise_new_(Promise this) { ref_init(this, 2, _Promise_finalize); this._promise_result = this; // promises default to being their own result to save on entities return this; } void _Promise_handle(Promise this, PromiseHandler h); void Promise_resolve(Promise this) { if (!this) { LOG_SEVERE("Attempted to resolve a null promise"); return; } if (this._promise_state != PROMISE_PENDING) { LOG_SEVEREF("Resolved non-pending promise %i", this); return; } this._promise_state = PROMISE_RESOLVED; if (this._promise_handlers) { IL_EACH(this._promise_handlers, true, _Promise_handle(this, it)); IL_DELETE(this._promise_handlers); } unref(this); return; } void Promise_reject(Promise this) { if (!this) { LOG_SEVERE("Attempted to reject a null promise"); return; } if (this._promise_state != PROMISE_PENDING) { LOG_SEVEREF("Rejected non-pending promise %i", this); return; } this._promise_state = PROMISE_REJECTED; if (this._promise_handlers) { IL_EACH(this._promise_handlers, true, _Promise_handle(this, it)); IL_DELETE(this._promise_handlers); } unref(this); return; } Promise _Promise_then( Promise this, Promise ret, Promise(Promise ret, entity result, entity userdata) onResolve, Promise(Promise ret, entity result, entity userdata) onReject, entity userdata ); void _Promise_handle(Promise this, PromiseHandler h) { switch (this._promise_state) { case PROMISE_PENDING: if (!this._promise_handlers) { this._promise_handlers = IL_NEW(); } IL_PUSH(this._promise_handlers, h); break; case PROMISE_RESOLVED: { Promise ret = h._promise_handler_ret; Promise p = h._promise_handler_resolve(ret, this._promise_result, h._promise_handler_data); if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret delete(h); break; } case PROMISE_REJECTED: { Promise ret = h._promise_handler_ret; Promise p = h._promise_handler_reject(ret, this._promise_result, h._promise_handler_data); if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret delete(h); break; } } } void _Promise_done( Promise this, Promise(Promise ret, entity result, entity userdata) onResolve, Promise(Promise ret, entity err, entity userdata) onReject, Promise ret, entity userdata ) { PromiseHandler h = new_pure(PromiseHandler); h._promise_handler_ret = ret; h._promise_handler_data = userdata; h._promise_handler_resolve = onResolve; h._promise_handler_reject = onReject; _Promise_handle(this, h); } Promise _Promise_onResolve_default(Promise ret, entity result, entity userdata) { ret._promise_result = result; Promise_resolve(ret); return ret; } Promise _Promise_onReject_default(Promise ret, entity err, entity userdata) { ret._promise_result = err; Promise_reject(ret); return ret; } Promise _Promise_then( Promise this, Promise ret, Promise(Promise ret, entity result, entity userdata) onResolve, Promise(Promise ret, entity result, entity userdata) onReject, entity userdata ) { _Promise_done( this, (onResolve ? onResolve : _Promise_onResolve_default), (onReject ? onReject : _Promise_onReject_default), ret, userdata ); return ret; } Promise Promise_then_( Promise this, Promise ret, Promise(Promise ret, entity result, entity userdata) onResolve, entity userdata ) { unref(ret); // ret is a temporary return _Promise_then(this, ret, onResolve, func_null, userdata); } Promise Promise_catch_( Promise this, Promise ret, Promise(Promise ret, entity result, entity userdata) onReject, entity userdata ) { unref(ret); // ret is a temporary return _Promise_then(this, ret, func_null, onReject, userdata); } // utils #ifndef MENUQC Promise Promise_sleep(float n) { Promise p = unref(Promise_new()); setthink(p, Promise_resolve); p.nextthink = time + n; return p; } #endif