]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Promises: initial commit, leaks entities TimePath/promises
authorTimePath <andrew.hardaker1995@gmail.com>
Wed, 25 Apr 2018 11:40:05 +0000 (21:40 +1000)
committerTimePath <andrew.hardaker1995@gmail.com>
Wed, 25 Apr 2018 11:40:05 +0000 (21:40 +1000)
qcsrc/lib/_all.inc
qcsrc/lib/_mod.inc
qcsrc/lib/_mod.qh
qcsrc/lib/promise.qc [new file with mode: 0644]
qcsrc/lib/promise.qh [new file with mode: 0644]
qcsrc/server/command/sv_cmd.qc

index d45dc208e0149d24c195bc5bc71bcf5271bad793..07f360520d4f8b8249f4d1406fe0d821661bfaa0 100644 (file)
 #include "oo.qh"
 #include "p2mathlib.qc"
 #include "progname.qh"
+#include "promise.qc"
 #include "random.qc"
 #include "registry.qh"
 #include "registry_net.qh"
index 115a6a070fe7b076bc651d80ef5933b0f6eaed27..8df43c95f4fbec1957848010211ff54561923750 100644 (file)
@@ -2,6 +2,7 @@
 #include <lib/angle.qc>
 #include <lib/json.qc>
 #include <lib/p2mathlib.qc>
+#include <lib/promise.qc>
 #include <lib/random.qc>
 #include <lib/sortlist.qc>
 #include <lib/test.qc>
index fd97f51b65dc09130abfa19daeb6b2266a20c9f6..8645347d6914cf07c915ff7c088737e2dcf6f8c3 100644 (file)
@@ -2,6 +2,7 @@
 #include <lib/angle.qh>
 #include <lib/json.qh>
 #include <lib/p2mathlib.qh>
+#include <lib/promise.qh>
 #include <lib/random.qh>
 #include <lib/sortlist.qh>
 #include <lib/test.qh>
diff --git a/qcsrc/lib/promise.qc b/qcsrc/lib/promise.qc
new file mode 100644 (file)
index 0000000..6e8d9ef
--- /dev/null
@@ -0,0 +1,185 @@
+#include "promise.qh"
+
+enum {
+    PROMISE_PENDING,
+    PROMISE_RESOLVED,
+    PROMISE_REJECTED,
+};
+
+classfield(Promise) .int _promise_state;
+classfield(Promise) .entity _promise_result;
+classfield(Promise) .IntrusiveList _promise_handlers;
+/** non-pending promises with no references may removed. TODO: implement (sv_cmd find Promise) */
+classfield(Promise) .int _promise_refcount;
+
+entityclass(PromiseHandler);
+classfield(PromiseHandler) .Promise _promise_handler_ret;
+classfield(PromiseHandler) .entity _promise_handler_data;
+classfield(PromiseHandler) .int(Promise ret, entity result, entity userdata) _promise_handler_resolve;
+classfield(PromiseHandler) .int(Promise ret, entity err, entity userdata) _promise_handler_reject;
+
+Promise promise_new()
+{
+    Promise p = new_pure(Promise);
+    p._promise_result = p; // promises default to being their own result to save on entities
+    return p;
+}
+
+void promise_ref(Promise this)
+{
+    this._promise_refcount += 1;
+}
+
+void promise_unref(Promise this)
+{
+    this._promise_refcount -= 1;
+}
+
+void promise_handle(Promise this, PromiseHandler h);
+
+int promise_resolve(Promise this)
+{
+    if (!this) {
+        LOG_SEVERE("Attempted to resolve a null promise");
+        return this._promise_state;
+    }
+    if (this._promise_state != PROMISE_PENDING) {
+        LOG_SEVEREF("Resolved non-pending promise %i", this);
+        return this._promise_state;
+    }
+    this._promise_state = PROMISE_RESOLVED;
+    if (this._promise_handlers) {
+        IL_EACH(this._promise_handlers, true, promise_handle(this, it));
+        IL_DELETE(this._promise_handlers);
+    }
+    return this._promise_state;
+}
+
+int promise_reject(Promise this)
+{
+    if (!this) {
+        LOG_SEVERE("Attempted to reject a null promise");
+        return this._promise_state;
+    }
+    if (this._promise_state != PROMISE_PENDING) {
+        LOG_SEVEREF("Rejected non-pending promise %i", this);
+        return this._promise_state;
+    }
+    this._promise_state = PROMISE_REJECTED;
+    if (this._promise_handlers) {
+        IL_EACH(this._promise_handlers, true, promise_handle(this, it));
+        IL_DELETE(this._promise_handlers);
+    }
+    return this._promise_state;
+}
+
+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:
+            h._promise_handler_resolve(h._promise_handler_ret, this._promise_result, h._promise_handler_data);
+            delete(h);
+            break;
+        case PROMISE_REJECTED:
+            h._promise_handler_reject(h._promise_handler_ret, this._promise_result, h._promise_handler_data);
+            delete(h);
+            break;
+    }
+}
+
+void promise_done(
+        Promise this,
+        int(Promise ret, entity result, entity userdata) onResolve,
+        int(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);
+}
+
+int _promise_onResolve_default(Promise ret, entity result, entity userdata)
+{
+    ret._promise_result = result;
+    return promise_resolve(ret);
+}
+
+int _promise_onReject_default(Promise ret, entity err, entity userdata)
+{
+    ret._promise_result = err;
+    return promise_reject(ret);
+}
+
+Promise _promise_then(
+        Promise this,
+        int(Promise ret, entity result, entity userdata) onResolve,
+        int(Promise ret, entity result, entity userdata) onReject,
+        entity userdata
+)
+{
+    Promise ret = promise_new();
+    promise_done(
+            this,
+            (onResolve ? onResolve : _promise_onResolve_default),
+            (onReject ? onReject : _promise_onReject_default),
+            ret,
+            userdata
+    );
+    return ret;
+}
+
+Promise promise_then(
+        Promise this,
+        int(Promise ret, entity result, entity userdata) onResolve,
+        entity userdata
+)
+{
+    return _promise_then(this, onResolve, func_null, userdata);
+}
+
+Promise promise_catch(
+        Promise this,
+        int(Promise ret, entity result, entity userdata) onReject,
+        entity userdata
+)
+{
+    return _promise_then(this, func_null, onReject, userdata);
+}
+
+int promise_test_then(Promise ret, entity result, entity userdata);
+int promise_test_catch(Promise ret, entity err, entity userdata);
+.int pval;
+
+void promise_test()
+{
+    Promise p0 = promise_new(); p0.pval = 5;
+    Promise p = p0;
+    p = promise_then(p, promise_test_then, NULL);
+    p = promise_then(p, promise_test_then, NULL);
+    p = promise_catch(p, promise_test_catch, NULL);
+    promise_resolve(p0);
+}
+
+int promise_test_then(Promise ret, entity result, entity userdata)
+{
+    LOG_INFOF("in .then(): %d", result.pval);
+    ret.pval = 10;
+    return promise_reject(ret);
+}
+
+int promise_test_catch(Promise ret, entity err, entity userdata)
+{
+    LOG_INFOF("in .catch(): %d", err.pval);
+    return promise_resolve(ret);
+}
diff --git a/qcsrc/lib/promise.qh b/qcsrc/lib/promise.qh
new file mode 100644 (file)
index 0000000..a8a95d3
--- /dev/null
@@ -0,0 +1,19 @@
+#pragma once
+
+entityclass(Promise);
+
+Promise promise_new();
+
+/**
+ * notify all Promise_then subscribers that this promise has resolved
+ */
+int promise_resolve(Promise this);
+
+Promise promise_then(Promise this, int(Promise ret, entity result, entity userdata) handler, entity userdata);
+
+/**
+ * notify all Promise_catch subscribers that this promise has rejected
+ */
+int promise_reject(Promise this);
+
+Promise promise_catch(Promise this, int(Promise ret, entity err, entity userdata) handler, entity userdata);
index 6de4507b1708ef54516d9d5db859bdb960a37457..ddbdb606142501c27a326d8368e061728d9f1176 100644 (file)
@@ -1852,3 +1852,7 @@ void GameCommand(string command)
        // nothing above caught the command, must be invalid
        LOG_INFO(((command != "") ? strcat("Unknown server command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try sv_cmd help.");
 }
+
+SERVER_COMMAND(test, "Test Promises") {
+       promise_test();
+}