The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#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);
}