Skip to content

Commit

Permalink
refactor: Don't use pycapsule for cb state
Browse files Browse the repository at this point in the history
  • Loading branch information
nickelpro committed Dec 22, 2024
1 parent 2355f88 commit defabc8
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 65 deletions.
13 changes: 10 additions & 3 deletions src/ModNanoroute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ namespace {
int nrmod_traverse(PyObject* mod, visitproc visit, void* arg) {
auto state {static_cast<NanorouteState*>(PyModule_GetState(mod))};
Py_VISIT(state->RouterType);
Py_VISIT(state->CBClosureType);
return 0;
}

int nrmod_clear(PyObject* mod) {
auto state {static_cast<NanorouteState*>(PyModule_GetState(mod))};
Py_CLEAR(state->RouterType);
Py_XDECREF(state->req_meth_string);
Py_XDECREF(state->path_info_string);
Py_XDECREF(state->wsgi_key_string);
Py_CLEAR(state->CBClosureType);
Py_CLEAR(state->req_meth_string);
Py_CLEAR(state->path_info_string);
Py_CLEAR(state->wsgi_key_string);
return 0;
}

Expand All @@ -38,6 +40,11 @@ int nrmod_exec(PyObject* mod) {
if(PyModule_AddType(mod, state->RouterType) < 0)
return -1;

state->CBClosureType = reinterpret_cast<PyTypeObject*>(
PyType_FromModuleAndSpec(mod, &cbclosure_spec, nullptr));
if(!state->CBClosureType)
return -1;

state->req_meth_string = PyUnicode_InternFromString("REQUEST_METHOD");
if(!state->req_meth_string)
return -1;
Expand Down
1 change: 1 addition & 0 deletions src/ModNanoroute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace nanoroute {

struct NanorouteState {
PyTypeObject* RouterType;
PyTypeObject* CBClosureType;
PyObject* req_meth_string;
PyObject* path_info_string;
PyObject* wsgi_key_string;
Expand Down
122 changes: 61 additions & 61 deletions src/PyRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <Python.h>

#define Py_BUILD_CORE
#include <internal/pycore_capsule.h>
#include <internal/pycore_modsupport.h>

#include "HTTPPerfectHash.hpp"
Expand Down Expand Up @@ -51,6 +50,14 @@ std::array router_slots {
slot__(Py_tp_new, PyRouter::new_),
slot__(),
};

std::array cbclosure_slots {
slot__(Py_tp_dealloc, PyCBClosure::dealloc),
slot__(Py_tp_traverse, PyCBClosure::traverse),
slot__(Py_tp_clear, PyCBClosure::clear),
slot__(),
};

} // namespace

PyType_Spec router_spec {
Expand All @@ -61,80 +68,71 @@ PyType_Spec router_spec {
.slots = router_slots.data(),
};

namespace {

struct RegisterClosure {
PyRouter* pyrouter;
PyObject* route;
std::vector<HTTPMethod> meths;
PyType_Spec cbclosure_spec {
.name = "nanoroute.cbclosure",
.basicsize = sizeof(PyCBClosure),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_IMMUTABLETYPE),
.slots = cbclosure_slots.data(),
};

static PyObject* alloc(PyRouter* router, PyObject* route, HTTPMethod meth) {
return capsulize(new RegisterClosure {router, route, {meth}});
}
namespace {
template <std::size_t N> void type_error(const char (&str)[N], PyObject* obj) {
PyObject* name {PyType_GetName(Py_TYPE(obj))};
PyErr_Format(PyExc_TypeError, str, name);
Py_DECREF(name);
}
} // namespace

static PyObject* alloc(PyRouter* router, PyObject* route,
std::vector<HTTPMethod> meths) {
return capsulize(new RegisterClosure {router, route, std::move(meths)});
}
PyCBClosure* PyCBClosure::internal_new(PyTypeObject* subtype,
PyRouter* pyrouter, PyObject* route, std::vector<HTTPMethod> meths) {

private:
static PyObject* capsulize(RegisterClosure* rg) {
auto cap {PyCapsule_New(rg, nullptr, RegisterClosure::dealloc)};
auto ptr {new(subtype->tp_alloc(subtype, sizeof(PyCBClosure)))
PyCBClosure(pyrouter, route, std::move(meths))};

if(cap) {
Py_INCREF(rg->pyrouter);
Py_INCREF(rg->route);
_PyCapsule_SetTraverse(cap, traverse, clear);
} else {
delete rg;
}
return cap;
}
if(!ptr)
return ptr;

static int traverse(PyObject* cap, visitproc visit, void* arg) {
auto rg {static_cast<RegisterClosure*>(PyCapsule_GetPointer(cap, nullptr))};
Py_VISIT(rg->pyrouter);
return 0;
}
Py_INCREF(pyrouter);
Py_INCREF(route);
return ptr;
}

static int clear(PyObject* cap) {
auto rg {static_cast<RegisterClosure*>(PyCapsule_GetPointer(cap, nullptr))};
Py_CLEAR(rg->pyrouter);
return 0;
}
int PyCBClosure::traverse(PyCBClosure* self, visitproc visit, void* arg) {
Py_VISIT(self->pyrouter);
return 0;
}

static void dealloc(PyObject* cap) {
auto rg {static_cast<RegisterClosure*>(PyCapsule_GetPointer(cap, nullptr))};
Py_CLEAR(rg->pyrouter);
Py_DECREF(rg->route);
delete rg;
}
};
int PyCBClosure::clear(PyCBClosure* self) {
Py_CLEAR(self->pyrouter);
return 0;
}

template <std::size_t N> void type_error(const char (&str)[N], PyObject* obj) {
PyObject* name {PyType_GetName(Py_TYPE(obj))};
PyErr_Format(PyExc_TypeError, str, name);
Py_DECREF(name);
void PyCBClosure::dealloc(PyCBClosure* self) {
PyObject_GC_UnTrack(self);
clear(self);
auto tp {Py_TYPE(self)};
self->~PyCBClosure();
tp->tp_free(self);
Py_DECREF(tp);
}
} // namespace

PyObject* PyRouter::register_route(PyObject* self, PyObject* const* args,
PyObject* PyRouter::register_route(PyCBClosure* self, PyObject* const* args,
Py_ssize_t nargs) {
PyObject* pyo;
if(!_PyArg_ParseStack(args, nargs, "O:register_route", &pyo))
return nullptr;

auto rg {static_cast<RegisterClosure*>(PyCapsule_GetPointer(self, nullptr))};
Py_ssize_t sz;
const char* c {PyUnicode_AsUTF8AndSize(rg->route, &sz)};
const char* c {PyUnicode_AsUTF8AndSize(self->route, &sz)};
if(!c)
return nullptr;
std::string_view route {c, static_cast<std::size_t>(sz)};

for(auto meth : rg->meths) {
for(auto meth : self->meths) {
Py_INCREF(pyo);
try {
rg->pyrouter->httprouter_.reg_route(meth, route, pyo);
self->pyrouter->httprouter_.reg_route(meth, route, pyo);
} catch(const std::exception& e) {
Py_DECREF(pyo);
PyErr_SetString(PyExc_TypeError, e.what());
Expand Down Expand Up @@ -394,27 +392,29 @@ PyMethodDef register_route_def {
} // namespace

PyObject* PyRouter::route_(HTTPMethod meth, PyObject* route_def) {
auto cap {RegisterClosure::alloc(this, route_def, meth)};
if(!cap)
auto closure {PyCBClosure::internal_new(state_->CBClosureType, this,
route_def, {meth})};
if(!closure)
return nullptr;

auto f {PyCFunction_New(&register_route_def, cap)};
auto f {PyCFunction_New(&register_route_def, closure)};
if(!f) {
Py_DECREF(cap);
Py_DECREF(closure);
return nullptr;
}

return f;
}

PyObject* PyRouter::route_(std::vector<HTTPMethod> meth, PyObject* route_def) {
auto cap {RegisterClosure::alloc(this, route_def, std::move(meth))};
if(!cap)
auto closure {PyCBClosure::internal_new(state_->CBClosureType, this,
route_def, std::move(meth))};
if(!closure)
return nullptr;

auto f {PyCFunction_New(&register_route_def, cap)};
auto f {PyCFunction_New(&register_route_def, closure)};
if(!f) {
Py_DECREF(cap);
Py_DECREF(closure);
return nullptr;
}

Expand Down
25 changes: 24 additions & 1 deletion src/PyRouter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
namespace nanoroute {

extern PyType_Spec router_spec;
extern PyType_Spec cbclosure_spec;

struct PyCBClosure;

struct PyRouter : PyObject {

Expand All @@ -28,7 +31,7 @@ struct PyRouter : PyObject {
static PyObject* wsgi_app(PyRouter* self, PyObject* const* args,
Py_ssize_t nargs);

static PyObject* register_route(PyObject* self, PyObject* const* args,
static PyObject* register_route(PyCBClosure* self, PyObject* const* args,
Py_ssize_t nargs);

static PyRouter* new_(PyTypeObject* subtype, PyObject* /*args*/,
Expand All @@ -49,6 +52,26 @@ struct PyRouter : PyObject {
NanorouteState* state_;
};

struct PyCBClosure : PyObject {
static PyCBClosure* internal_new(PyTypeObject* subtype, PyRouter* pyrouter,
PyObject* route, std::vector<HTTPMethod> meths);

private:
PyCBClosure(PyRouter* pyrouter, PyObject* route,
std::vector<HTTPMethod> meths)
: pyrouter {pyrouter}, route {route}, meths {std::move(meths)} {}

public:
static int traverse(PyCBClosure* self, visitproc visit, void* arg);
static int clear(PyCBClosure* self);

static void dealloc(PyCBClosure* self);

PyRouter* pyrouter;
PyObject* route;
std::vector<HTTPMethod> meths;
};

} // namespace nanoroute

#endif // NANOROUTE_PYROUTER_HPP

0 comments on commit defabc8

Please sign in to comment.