#include <xs/unievent/Loop.h>
#include <xs/unievent/Handle.h>
#include <xs/unievent/Prepare.h>
#include <xs/unievent/Resolver.h>
#include <xs/CallbackDispatcher.h>
using
namespace
xs;
using
namespace
panda::unievent;
using
panda::unievent::backend::Backend;
static
Object global_loop;
static
Loop* global_loop_for;
static
PrepareSP global_loop_freetmps;
static
PERL_ITHREADS_LOCAL
struct
{
Object default_loop;
Loop* default_loop_for;
PrepareSP default_loop_freetmps;
} tls;
static
Sv::payload_marker_t delay_marker;
static
void
freetmps_on_loop_iter (
const
PrepareSP&) {
FREETMPS;
}
static
Sv _get_default_loop_sv () {
auto
cur = Loop::default_loop();
if
(tls.default_loop_for != cur) {
tls.default_loop_for = cur;
tls.default_loop = xs::out(cur);
tls.default_loop_freetmps =
new
Prepare(cur);
tls.default_loop_freetmps->event.add(freetmps_on_loop_iter);
tls.default_loop_freetmps->weak(
true
);
tls.default_loop_freetmps->start();
}
return
tls.default_loop.ref();
}
static
Sv _get_global_loop_sv () {
auto
cur = Loop::global_loop();
if
(global_loop_for != cur) {
global_loop_for = cur;
if
(cur == Loop::default_loop()) global_loop = _get_default_loop_sv();
else
{
global_loop = xs::out(cur);
global_loop_freetmps =
new
Prepare(cur);
global_loop_freetmps->event.add(freetmps_on_loop_iter);
global_loop_freetmps->weak(
true
);
global_loop_freetmps->start();
}
}
return
global_loop.ref();
}
struct
SvWrapper : panda::Refcnt {
SvWrapper(Sv sv) : sv(sv) {}
Sv sv;
};
MODULE = UniEvent::Loop PACKAGE = UniEvent::Loop
PROTOTYPES: DISABLE
BOOT {
delay_marker.svt_free = [](pTHX_ SV* sv, MAGIC* mg) ->
int
{
auto
id = SvUVX(sv);
auto
w = (panda::weak<LoopSP>*)mg->mg_ptr;
if
(id) {
SvUVX(sv) = 0;
auto
loop = w->lock();
if
(loop) loop->cancel_delay(id);
}
delete
w;
return
0;
};
xs::at_perl_destroy([]() {
global_loop_freetmps =
nullptr
;
global_loop =
nullptr
;
global_loop_for =
nullptr
;
tls.default_loop_freetmps =
nullptr
;
tls.default_loop =
nullptr
;
tls.default_loop_for =
nullptr
;
});
}
void
global_loop (...) : ALIAS(global=1) {
PERL_UNUSED_VAR(ix);
XPUSHs(_get_global_loop_sv());
XSRETURN(1);
}
void
default_loop (...) : ALIAS(
default
=1) {
PERL_UNUSED_VAR(ix);
XPUSHs(_get_default_loop_sv());
XSRETURN(1);
}
Loop* Loop::
new
(Backend* be =
nullptr
) {
RETVAL = make_backref<Loop>(be);
}
bool
Loop::is_default ()
bool
Loop::is_global ()
bool
Loop::alive ()
uint64_t Loop::now ()
void
Loop::update_time ()
bool
Loop::run ()
bool
Loop::run_once ()
bool
Loop::run_nowait ()
void
Loop::stop ()
Array Loop::handles () {
RETVAL = Array::create();
auto
& hl = THIS->handles();
if
(hl.size()) {
RETVAL.reserve(hl.size());
for
(
const
auto
& h : hl) RETVAL.push(xs::out(h));
}
}
Ref Loop::delay (Sub callback) {
if
(!callback)
throw
"callback is required"
;
if
(GIMME_V == G_VOID) {
THIS->delay([=]() { callback.call<
void
>(); });
XSRETURN_EMPTY;
}
auto
ret = Simple((uint64_t)0);
auto
svp = ret.get();
auto
id = THIS->delay([=]() {
SvUVX(svp) = 0;
Sv(svp).payload_detach(&delay_marker);
callback.call<
void
>();
});
SvUVX(svp) = id;
auto
w =
new
panda::weak<LoopSP>(LoopSP(THIS));
ret.payload_attach(w, &delay_marker);
RETVAL = Ref::create(ret);
}
void
Loop::cancel_delay (Ref ref) {
(
void
)THIS;
auto
val = ref.value<Simple>();
if
(val) val.payload_detach(&delay_marker);
}
XSCallbackDispatcher* Loop::fork_event () {
RETVAL = XSCallbackDispatcher::create(THIS->fork_event);
}
ResolverSP Loop::resolver ()
void
Loop::track_load_average (uint32_t for_last_n_seconds)
double
Loop::get_load_average ()
void
Loop::start_debug_tracer (Sub s) {
THIS->new_handle_event.add([s](
const
LoopSP&, Handle* h){
Sv stack = s.call();
h->user_data =
new
SvWrapper(stack);
});
}
void
Loop::watch_active_trace (Sub s) {
for
(
auto
h : THIS->handles()) {
auto
sv = panda::dynamic_pointer_cast<SvWrapper>(h->user_data);
if
(!h->user_data || !h->active() || h->weak()) {
continue
;
}
if
(!sv) {
throw
std::logic_error(
"Trace is broken or somebody else used Handle::user_data"
);
}
s.call(xs::out(h), sv->sv);
}
}
SV* CLONE_SKIP (...) {
XSRETURN_YES;
PERL_UNUSED_VAR(items);
}