mirror of
https://github.com/matrix-construct/construct
synced 2025-01-14 00:34:18 +01:00
ircd::js: Add asynchronous state and completion target mechanism.
This commit is contained in:
parent
bfb5948191
commit
edd77d2265
7 changed files with 298 additions and 6 deletions
|
@ -75,7 +75,8 @@ struct context
|
||||||
void handle_timeout() noexcept; // Called by timer after requested time
|
void handle_timeout() noexcept; // Called by timer after requested time
|
||||||
struct timer timer;
|
struct timer timer;
|
||||||
|
|
||||||
std::map<uint64_t, struct task *> tasks; // Active processes
|
// System target
|
||||||
|
struct star *star; // Registered by kernel
|
||||||
|
|
||||||
// JSContext
|
// JSContext
|
||||||
operator JSContext *() const { return get(); }
|
operator JSContext *() const { return get(); }
|
||||||
|
|
50
include/ircd/js/contract.h
Normal file
50
include/ircd/js/contract.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Charybdis Development Team
|
||||||
|
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice is present in all copies.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#define HAVE_IRCD_JS_CONTRACT_H
|
||||||
|
|
||||||
|
namespace ircd {
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
struct contract
|
||||||
|
:std::weak_ptr<struct task>
|
||||||
|
{
|
||||||
|
using closure = std::function<value ()>;
|
||||||
|
|
||||||
|
heap_object future;
|
||||||
|
|
||||||
|
operator const heap_object &() const { return future; }
|
||||||
|
operator heap_object &() { return future; }
|
||||||
|
operator object() const { return future; }
|
||||||
|
operator value() const { return future; }
|
||||||
|
|
||||||
|
// Enter this closure and return or throw to complete the result contract.
|
||||||
|
void operator()(const closure &) noexcept;
|
||||||
|
|
||||||
|
// The future object is constructed using the "future" trap.
|
||||||
|
// The task is automatically found with task::get() in the overload without a task argument.
|
||||||
|
contract(struct task &, object::handle future);
|
||||||
|
contract(object::handle future);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
} // namespace ircd
|
|
@ -87,4 +87,6 @@ inline JSVersion version(const char *const &v) { return JS_StringToVersion(v);
|
||||||
#include "trap_function.h"
|
#include "trap_function.h"
|
||||||
#include "generator.h"
|
#include "generator.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "contract.h"
|
||||||
|
#include "star.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
43
include/ircd/js/star.h
Normal file
43
include/ircd/js/star.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Charybdis Development Team
|
||||||
|
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice is present in all copies.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#define HAVE_IRCD_JS_STAR_H
|
||||||
|
|
||||||
|
namespace ircd {
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
struct star
|
||||||
|
{
|
||||||
|
// Active processes by PID. This is managed by the actual task structure itself.
|
||||||
|
// A task pointer in the map will always be valid. When the last shared_ptr to a task is
|
||||||
|
// destroyed the task removes itself and weak references to the task are invalidated.
|
||||||
|
std::map<uint64_t, task *> tasks;
|
||||||
|
|
||||||
|
// Modules conducting some asynchronous operation on behalf of a task maintain state with
|
||||||
|
// a `struct contract` object. When the result is ready and the contract is fulfilled
|
||||||
|
// the contract object inserts itself into this queue (the user does not do this).
|
||||||
|
// The kernel is listening on the other end of this queue.
|
||||||
|
ctx::queue<contract> completion;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
} // namespace ircd
|
|
@ -28,7 +28,11 @@ namespace js {
|
||||||
struct task
|
struct task
|
||||||
:std::enable_shared_from_this<task>
|
:std::enable_shared_from_this<task>
|
||||||
{
|
{
|
||||||
uint64_t pid;
|
uint64_t pid; // unique process ID
|
||||||
|
uint64_t yid; // ID of current yield attempting unification
|
||||||
|
std::map<uint64_t, heap_object> complete; // futures waiting for yield unification
|
||||||
|
std::set<uint64_t> pending; // pending futures awaiting results
|
||||||
|
std::shared_ptr<task> work; // references self when there is unfinished work
|
||||||
struct global global; // global / this / root scope object
|
struct global global; // global / this / root scope object
|
||||||
heap_script main; // main generator wrapper script
|
heap_script main; // main generator wrapper script
|
||||||
struct generator generator; // generator state
|
struct generator generator; // generator state
|
||||||
|
@ -39,9 +43,13 @@ struct task
|
||||||
bool tasks_remove();
|
bool tasks_remove();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool pending_add(const uint64_t &id);
|
||||||
|
bool pending_del(const uint64_t &id);
|
||||||
|
|
||||||
task(const std::string &source);
|
task(const std::string &source);
|
||||||
~task() noexcept;
|
~task() noexcept;
|
||||||
|
|
||||||
|
static bool enter(const std::weak_ptr<task> &, const std::function<void (task &)> &closure);
|
||||||
static task &get(const object &global);
|
static task &get(const object &global);
|
||||||
static task &get();
|
static task &get();
|
||||||
};
|
};
|
||||||
|
|
127
ircd/js.cc
127
ircd/js.cc
|
@ -143,6 +143,55 @@ js::ReportOutOfMemory(ExclusiveContext *const c)
|
||||||
// ircd/js/js.h - With 3rd party (JSAPI) symbols
|
// ircd/js/js.h - With 3rd party (JSAPI) symbols
|
||||||
//
|
//
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// ircd/js/contract.h
|
||||||
|
//
|
||||||
|
|
||||||
|
ircd::js::contract::contract(object::handle future)
|
||||||
|
:contract{task::get(), future}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::js::contract::contract(struct task &task,
|
||||||
|
object::handle future)
|
||||||
|
:std::weak_ptr<struct task>{weak_from(task)}
|
||||||
|
,future{future}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ircd::js::contract::operator()(const closure &func)
|
||||||
|
noexcept try
|
||||||
|
{
|
||||||
|
task::enter(*this, [this, &func]
|
||||||
|
(task &task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
set(future, "value", func());
|
||||||
|
}
|
||||||
|
catch(const jserror &e)
|
||||||
|
{
|
||||||
|
set(future, "error", e.val.get());
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
set(future, "error", string(e.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(cx->star);
|
||||||
|
cx->star->completion.push(*this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
ircd::js::log.critical("contract(%p): %s",
|
||||||
|
(const void *)this,
|
||||||
|
e.what());
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// ircd/js/task.h
|
// ircd/js/task.h
|
||||||
|
@ -154,6 +203,10 @@ try
|
||||||
{
|
{
|
||||||
tasks_insert()
|
tasks_insert()
|
||||||
}
|
}
|
||||||
|
,yid
|
||||||
|
{
|
||||||
|
0
|
||||||
|
}
|
||||||
,global{[this]
|
,global{[this]
|
||||||
{
|
{
|
||||||
// Global object is constructed using the root trap (JSClass) at *tree;
|
// Global object is constructed using the root trap (JSClass) at *tree;
|
||||||
|
@ -187,7 +240,7 @@ try
|
||||||
|
|
||||||
// Run the generator wrapper (main function) returning the generator object.
|
// Run the generator wrapper (main function) returning the generator object.
|
||||||
// The run() closure provides safety for entering the JS engine.
|
// The run() closure provides safety for entering the JS engine.
|
||||||
value state(run([this]
|
value state(js::run([this]
|
||||||
{
|
{
|
||||||
return this->main();
|
return this->main();
|
||||||
}));
|
}));
|
||||||
|
@ -211,17 +264,82 @@ noexcept
|
||||||
tasks_remove();
|
tasks_remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::js::task::enter(const std::weak_ptr<task> &ptr,
|
||||||
|
const std::function<void (task &)> &closure)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const life_guard<struct task> task(ptr);
|
||||||
|
const std::lock_guard<context> lock{*cx};
|
||||||
|
const compartment compartment(task->global);
|
||||||
|
run([&closure, &task]
|
||||||
|
{
|
||||||
|
closure(*task);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(const std::bad_weak_ptr &e)
|
||||||
|
{
|
||||||
|
ircd::js::log.warning("task::enter(%p, closure: %p): expired task",
|
||||||
|
(const void *)&ptr,
|
||||||
|
(const void *)&closure);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::js::task::pending_del(const uint64_t &id)
|
||||||
|
{
|
||||||
|
const auto ret(pending.erase(id));
|
||||||
|
if(!ret)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// When nothing is pending this strong self-reference is dropped and the task
|
||||||
|
// may be allowed to delete itself.
|
||||||
|
if(pending.empty())
|
||||||
|
work.reset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ircd::js::task::pending_add(const uint64_t &id)
|
||||||
|
{
|
||||||
|
const auto iit(pending.emplace(id));
|
||||||
|
if(!iit.second)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If this is the first pending contract for this task a strong self-reference
|
||||||
|
// is placed here to ensure the task lingers until all work is completed.
|
||||||
|
if(pending.size() == 1)
|
||||||
|
work = shared_from_this();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ircd::js::task::tasks_remove()
|
ircd::js::task::tasks_remove()
|
||||||
{
|
{
|
||||||
return cx->tasks.erase(pid);
|
auto &tasks(cx->star->tasks);
|
||||||
|
const auto ret(tasks.erase(pid));
|
||||||
|
log.debug("task(%p) pid[%lu] removed",
|
||||||
|
(const void *)this,
|
||||||
|
pid);
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
ircd::js::task::tasks_insert()
|
ircd::js::task::tasks_insert()
|
||||||
{
|
{
|
||||||
|
auto &tasks(cx->star->tasks);
|
||||||
const uint64_t pid(tasks_next_pid());
|
const uint64_t pid(tasks_next_pid());
|
||||||
const auto iit(cx->tasks.emplace(pid, this));
|
const auto iit(tasks.emplace(pid, this));
|
||||||
|
log.debug("task(%p) pid[%lu] added",
|
||||||
|
(const void *)this,
|
||||||
|
pid);
|
||||||
|
|
||||||
assert(iit.second);
|
assert(iit.second);
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +347,7 @@ ircd::js::task::tasks_insert()
|
||||||
uint64_t
|
uint64_t
|
||||||
ircd::js::task::tasks_next_pid()
|
ircd::js::task::tasks_next_pid()
|
||||||
{
|
{
|
||||||
auto &tasks(cx->tasks);
|
auto &tasks(cx->star->tasks);
|
||||||
return tasks.empty()? 0 : std::prev(std::end(tasks))->first + 1;
|
return tasks.empty()? 0 : std::prev(std::end(tasks))->first + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2115,6 +2233,7 @@ ircd::js::context::context(struct runtime &runtime,
|
||||||
{
|
{
|
||||||
std::bind(&context::handle_timeout, this)
|
std::bind(&context::handle_timeout, this)
|
||||||
}
|
}
|
||||||
|
,star{nullptr}
|
||||||
{
|
{
|
||||||
assert(&runtime == rt); // Trying to construct on thread without runtime thread_local
|
assert(&runtime == rt); // Trying to construct on thread without runtime thread_local
|
||||||
timer.set(opts.timer_limit);
|
timer.set(opts.timer_limit);
|
||||||
|
|
69
modules/future.cc
Normal file
69
modules/future.cc
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Charybdis Development Team
|
||||||
|
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice is present in all copies.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ircd/js/js.h>
|
||||||
|
|
||||||
|
namespace ircd {
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
struct future
|
||||||
|
:trap
|
||||||
|
{
|
||||||
|
uint64_t id_ctr;
|
||||||
|
|
||||||
|
void on_ctor(object &, const args &args) override;
|
||||||
|
|
||||||
|
future();
|
||||||
|
}
|
||||||
|
static future;
|
||||||
|
|
||||||
|
future::future()
|
||||||
|
:trap
|
||||||
|
{
|
||||||
|
"future",
|
||||||
|
JSCLASS_HAS_PRIVATE
|
||||||
|
}
|
||||||
|
,id_ctr{0}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
future::on_ctor(object &obj, const args &args)
|
||||||
|
{
|
||||||
|
auto &task(task::get());
|
||||||
|
|
||||||
|
if(args.has(0) && type(args[0]) == JSTYPE_FUNCTION)
|
||||||
|
set(obj, "callback", args[0]);
|
||||||
|
|
||||||
|
const auto id(++id_ctr);
|
||||||
|
task.pending_add(id);
|
||||||
|
set(obj, "id", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
} // namespace ircd
|
||||||
|
|
||||||
|
using namespace ircd;
|
||||||
|
|
||||||
|
mapi::header IRCD_MODULE
|
||||||
|
{
|
||||||
|
"Fundamental yieldable object for asynchronicity.",
|
||||||
|
};
|
Loading…
Reference in a new issue