diff --git a/.gitignore b/.gitignore index e0429b7..b3fd9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ wheels/ _liburing.c _liburing.o _liburing.*.so +_uringcore.*.so _uringcore_liburing.*.so diff --git a/docs/phase1-native-ring-decision.md b/docs/phase1-native-ring-decision.md index 19a5d6a..bdb5716 100644 --- a/docs/phase1-native-ring-decision.md +++ b/docs/phase1-native-ring-decision.md @@ -124,7 +124,8 @@ following work blocks that transition: native e2e suite against that instrumented archive. 1. Move the selected implementation behind the canonical `_uringcore` name and replace the comparison-spike module name, stub, tests, and build - configuration. + configuration. This transition was completed after the native NOP request + lifecycle landed. 1. Implement and benchmark the native prepare/submit/reap batch boundary before making claims about syscall or CPU improvements. 1. Continue with the refcounted multi-completion request state machine only diff --git a/docs/phase1-static-liburing-spike.md b/docs/phase1-static-liburing-spike.md index a6494bc..22e8e92 100644 --- a/docs/phase1-static-liburing-spike.md +++ b/docs/phase1-static-liburing-spike.md @@ -1,7 +1,7 @@ # Phase 1 static-liburing ring spike -This is the second native ring implementation spike required by Phase 1 of -the roadmap. It mirrors the lifecycle boundary of the raw-syscall +This was the second native ring implementation spike required by Phase 1 of +the roadmap. It mirrored the lifecycle boundary of the raw-syscall `_uringcore.Ring` with a separate `_uringcore_liburing.Ring` implemented through the pinned liburing submodule. @@ -10,20 +10,20 @@ the module. It therefore has no runtime dependency on a system `liburing.so`. liburing is used under its MIT license, whose notice is included in the package. -Like the raw-syscall spike, this module owns ring initialization and teardown -but does not submit or reap operations and is not wired into the Python +At the comparison boundary, the module owned ring initialization and teardown +but did not submit or reap operations and was not wired into the Python proactor. The build configures and compiles a private archive from the pinned vendored sources, then links that archive into both the existing CFFI module -and this experimental extension. Source builds therefore do not require a -prebuilt archive or a system `liburing.so`. +and the native extension. Source builds therefore do not require a prebuilt +archive or a system `liburing.so`. -The two lifecycle implementations use the same API and tests. The resulting -[backend decision](phase1-native-ring-decision.md) selects the statically -linked, vendored liburing route. This spike is not yet the production backend: -the follow-up work must add full sanitizer coverage, move this implementation -to `_uringcore`, and replace the comparison-spike module name. The raw spike is -preserved separately on `feature/raw-syscall-ring-spike`; it is not built or -packaged by this branch. +The two lifecycle implementations used the same API and tests. The resulting +[backend decision](phase1-native-ring-decision.md) selected the statically +linked, vendored liburing route. Subsequent Phase 1 work added the native NOP +request lifecycle and promoted this implementation to the canonical +`_uringcore` module name. Full sanitizer coverage and the remaining request +state-machine work are still required. The raw spike is preserved separately +on `feature/raw-syscall-ring-spike`; it is not built or packaged. On the initial CPython 3.12 x86-64 development build, including debug information, the module sizes are: diff --git a/scripts/verify_wheel.py b/scripts/verify_wheel.py index 6f31fa3..7400d45 100644 --- a/scripts/verify_wheel.py +++ b/scripts/verify_wheel.py @@ -8,7 +8,7 @@ EXTENSION_MODULES = ( "uringloop._liburing", - "uringloop._uringcore_liburing", + "uringloop._uringcore", ) diff --git a/setup.py b/setup.py index b1f891a..c519683 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ LIBURING_SOURCE = ROOT / "libs" LIBURING_EXTENSION_NAMES = { "uringloop._liburing", - "uringloop._uringcore_liburing", + "uringloop._uringcore", } @@ -83,7 +83,7 @@ def _build_vendored_liburing(self): cmdclass={"build_ext": VendoredLiburingBuildExt}, ext_modules=[ Extension( - "uringloop._uringcore_liburing", + "uringloop._uringcore", sources=["src/uringcore_liburing.c"], ), ], diff --git a/src/uringcore_liburing.c b/src/uringcore_liburing.c index 0146732..42f2efb 100644 --- a/src/uringcore_liburing.c +++ b/src/uringcore_liburing.c @@ -230,7 +230,7 @@ PyDoc_STRVAR( static PyTypeObject UringCoreLiburingRequestType = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "uringloop._uringcore_liburing.Request", + .tp_name = "uringloop._uringcore.Request", .tp_basicsize = sizeof(UringCoreLiburingRequest), .tp_dealloc = (destructor)uringcore_liburing_request_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, @@ -657,7 +657,7 @@ PyDoc_STRVAR( static PyTypeObject UringCoreLiburingRingType = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "uringloop._uringcore_liburing.Ring", + .tp_name = "uringloop._uringcore.Ring", .tp_basicsize = sizeof(UringCoreLiburingRing), .tp_dealloc = (destructor)uringcore_liburing_ring_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, @@ -669,15 +669,15 @@ static PyTypeObject UringCoreLiburingRingType = { .tp_new = uringcore_liburing_ring_new, }; -static PyModuleDef uringcore_liburing_module = { +static PyModuleDef uringcore_module = { PyModuleDef_HEAD_INIT, - .m_name = "_uringcore_liburing", - .m_doc = "Statically linked liburing ring primitives.", + .m_name = "_uringcore", + .m_doc = "Native ring primitives backed by statically linked liburing.", .m_size = -1, }; PyMODINIT_FUNC -PyInit__uringcore_liburing(void) +PyInit__uringcore(void) { PyObject *module; @@ -688,7 +688,7 @@ PyInit__uringcore_liburing(void) return NULL; } - module = PyModule_Create(&uringcore_liburing_module); + module = PyModule_Create(&uringcore_module); if (module == NULL) { return NULL; } diff --git a/tests/unit/test_uringcore_liburing.py b/tests/unit/test_uringcore.py similarity index 71% rename from tests/unit/test_uringcore_liburing.py rename to tests/unit/test_uringcore.py index c01e3cf..5fb8197 100644 --- a/tests/unit/test_uringcore_liburing.py +++ b/tests/unit/test_uringcore.py @@ -3,11 +3,13 @@ import pytest -from uringloop import _uringcore_liburing +from uringloop import _uringcore -def test_static_liburing_core_has_versioned_abi(): - assert _uringcore_liburing.ABI_VERSION == 2 +def test_native_core_has_versioned_abi(): + assert _uringcore.ABI_VERSION == 2 + assert _uringcore.Ring.__module__ == "uringloop._uringcore" + assert _uringcore.Request.__module__ == "uringloop._uringcore" def reap_requests(ring, count): @@ -22,13 +24,13 @@ def reap_requests(ring, count): @pytest.mark.parametrize("entries", [-(2**32), -1, 0, 2**32, 2**64]) -def test_static_liburing_ring_rejects_out_of_range_queue_size(entries): +def test_native_ring_rejects_out_of_range_queue_size(entries): with pytest.raises(ValueError, match="entries must be between"): - _uringcore_liburing.Ring(entries) + _uringcore.Ring(entries) -def test_static_liburing_ring_owns_and_releases_kernel_resources(): - ring = _uringcore_liburing.Ring(8) +def test_native_ring_owns_and_releases_kernel_resources(): + ring = _uringcore.Ring(8) assert ring.sq_entries >= 8 assert ring.cq_entries >= ring.sq_entries @@ -41,8 +43,8 @@ def test_static_liburing_ring_owns_and_releases_kernel_resources(): assert ring.closed is True -def test_static_liburing_ring_context_manager_closes_resources(): - with _uringcore_liburing.Ring(entries=8) as ring: +def test_native_ring_context_manager_closes_resources(): + with _uringcore.Ring(entries=8) as ring: assert ring.closed is False assert ring.closed is True @@ -50,8 +52,8 @@ def test_static_liburing_ring_context_manager_closes_resources(): ring.__enter__() -def test_static_liburing_ring_submits_and_reaps_native_nop_requests(): - ring = _uringcore_liburing.Ring(8) +def test_native_ring_submits_and_reaps_native_nop_requests(): + ring = _uringcore.Ring(8) requests = [ring.prepare_nop(), ring.prepare_nop()] assert ring.pending == 2 @@ -71,8 +73,8 @@ def test_static_liburing_ring_submits_and_reaps_native_nop_requests(): assert [request.flags for request in requests] == [0, 0] -def test_static_liburing_ring_keeps_request_alive_until_completion(): - ring = _uringcore_liburing.Ring(8) +def test_native_ring_keeps_request_alive_until_completion(): + ring = _uringcore.Ring(8) ring.prepare_nop() assert ring.submit() == 1 @@ -82,8 +84,8 @@ def test_static_liburing_ring_keeps_request_alive_until_completion(): assert request.result == 0 -def test_static_liburing_ring_close_releases_prepared_request_ownership(): - ring = _uringcore_liburing.Ring(8) +def test_native_ring_close_releases_prepared_request_ownership(): + ring = _uringcore.Ring(8) request = ring.prepare_nop() ring.close() @@ -96,10 +98,10 @@ def test_static_liburing_ring_close_releases_prepared_request_ownership(): @pytest.mark.parametrize("max_completions", [-1, 0, 2**32, 2**64]) -def test_static_liburing_ring_reap_rejects_out_of_range_batch_size( +def test_native_ring_reap_rejects_out_of_range_batch_size( max_completions, ): - ring = _uringcore_liburing.Ring(8) + ring = _uringcore.Ring(8) with pytest.raises(ValueError, match="max_completions must be between"): ring.reap(max_completions) diff --git a/uringloop/_uringcore_liburing.pyi b/uringloop/_uringcore.pyi similarity index 100% rename from uringloop/_uringcore_liburing.pyi rename to uringloop/_uringcore.pyi