sharedbox 0.4.1__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sharedbox-0.5.0/.github/workflows/contention.yaml +45 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/CHANGELOG.md +41 -1
- {sharedbox-0.4.1 → sharedbox-0.5.0}/CLAUDE.md +13 -7
- {sharedbox-0.4.1 → sharedbox-0.5.0}/PKG-INFO +37 -11
- {sharedbox-0.4.1 → sharedbox-0.5.0}/README.md +36 -10
- {sharedbox-0.4.1 → sharedbox-0.5.0}/benchmarks/test_bench_box.py +41 -0
- sharedbox-0.5.0/docs/diagrams/style.d2 +61 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/store_arrays.py +12 -0
- sharedbox-0.5.0/docs/explanation/checking-a-process-is-alive.md +89 -0
- sharedbox-0.5.0/docs/explanation/closing-and-lifetime.md +242 -0
- sharedbox-0.5.0/docs/explanation/field-types.md +141 -0
- sharedbox-0.5.0/docs/explanation/glossary.md +130 -0
- sharedbox-0.5.0/docs/explanation/how-a-box-is-stored.md +214 -0
- sharedbox-0.5.0/docs/explanation/how-the-module-is-built.md +96 -0
- sharedbox-0.5.0/docs/explanation/index.md +44 -0
- sharedbox-0.5.0/docs/explanation/limits.md +74 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/explanation/performance.md +14 -10
- sharedbox-0.5.0/docs/explanation/reading-and-writing.md +272 -0
- sharedbox-0.5.0/docs/explanation/references.md +154 -0
- sharedbox-0.5.0/docs/explanation/waiting-for-changes.md +268 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/explanation/when-to-use-sharedbox.md +11 -6
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/accept-a-box-in-c.md +30 -18
- sharedbox-0.5.0/docs/how-to/accept-a-box-in-cpp.md +192 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/build-docs.md +3 -0
- sharedbox-0.5.0/docs/how-to/change-several-fields-at-once.md +60 -0
- sharedbox-0.5.0/docs/how-to/clean-up-segments.md +93 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/commits-and-prs.md +4 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/contribute.md +3 -2
- sharedbox-0.5.0/docs/how-to/follow-a-whole-reference-graph.md +73 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/index.md +6 -5
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/install-sharedbox.md +9 -8
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/make-a-release.md +3 -2
- sharedbox-0.5.0/docs/how-to/name-a-box.md +83 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/open-a-box-from-a-program.md +13 -11
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/run-benchmarks.md +34 -2
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/run-commit-checks.md +5 -4
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/run-tests.md +7 -2
- sharedbox-0.5.0/docs/how-to/send-a-box-to-another-process.md +127 -0
- sharedbox-0.5.0/docs/how-to/set-defaults-and-check-values.md +84 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/set-up-development.md +3 -1
- sharedbox-0.5.0/docs/how-to/store-arrays.md +175 -0
- sharedbox-0.5.0/docs/how-to/store-collections.md +71 -0
- sharedbox-0.5.0/docs/how-to/store-records.md +79 -0
- sharedbox-0.5.0/docs/how-to/store-text-and-bytes.md +68 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/write-docs.md +68 -6
- sharedbox-0.5.0/docs/index.md +185 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/cpp-and-c-api.md +9 -6
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/index.md +4 -2
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/segment-layout.md +11 -7
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/tutorials/index.md +6 -4
- sharedbox-0.5.0/docs/tutorials/react-to-changes.md +149 -0
- sharedbox-0.5.0/docs/tutorials/refer-to-another-box.md +104 -0
- sharedbox-0.5.0/docs/tutorials/share-a-record.md +131 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/include/sharedbox/sharedbox.hpp +34 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/pyproject.toml +1 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_box.py +93 -10
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_layout.py +6 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/module.cpp +173 -7
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/segment.cpp +44 -1
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/segment.hpp +31 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/types.cpp +130 -21
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/types.hpp +17 -2
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native.pyi +56 -2
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_types.py +34 -1
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_version.py +3 -3
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/_app.py +37 -1
- sharedbox-0.5.0/src/sharedbox/benchmarks/contention.py +324 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/ops.py +9 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/stubtest-allowlist.txt +5 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/CMakeLists.txt +1 -1
- sharedbox-0.5.0/tests/cpp/test_write_in_place.cpp +83 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/forged_open.py +6 -2
- sharedbox-0.5.0/tests/stress/test_stress_read_into.py +61 -0
- sharedbox-0.5.0/tests/test_benchbox_contention.py +15 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_box.py +5 -4
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_fork.py +34 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_properties_stateful.py +12 -1
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_properties_types.py +5 -2
- sharedbox-0.5.0/tests/test_read_into.py +197 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_arrays_bfloat16.py +21 -0
- sharedbox-0.5.0/tests/test_types_records_attrs.py +83 -0
- sharedbox-0.5.0/tests/test_types_records_msgspec.py +92 -0
- sharedbox-0.5.0/tests/test_value_reuse.py +446 -0
- sharedbox-0.5.0/tests/test_writing.py +171 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/type_checks/box_types.py +21 -1
- {sharedbox-0.4.1 → sharedbox-0.5.0}/uv.lock +32 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/zensical.toml +1 -1
- sharedbox-0.4.1/docs/explanation/checking-a-process-is-alive.md +0 -63
- sharedbox-0.4.1/docs/explanation/closing-and-lifetime.md +0 -136
- sharedbox-0.4.1/docs/explanation/field-types.md +0 -91
- sharedbox-0.4.1/docs/explanation/glossary.md +0 -114
- sharedbox-0.4.1/docs/explanation/how-a-box-is-stored.md +0 -145
- sharedbox-0.4.1/docs/explanation/how-the-module-is-built.md +0 -68
- sharedbox-0.4.1/docs/explanation/index.md +0 -44
- sharedbox-0.4.1/docs/explanation/limits.md +0 -63
- sharedbox-0.4.1/docs/explanation/reading-and-writing.md +0 -121
- sharedbox-0.4.1/docs/explanation/references.md +0 -79
- sharedbox-0.4.1/docs/explanation/waiting-for-changes.md +0 -184
- sharedbox-0.4.1/docs/how-to/accept-a-box-in-cpp.md +0 -174
- sharedbox-0.4.1/docs/how-to/change-several-fields-at-once.md +0 -59
- sharedbox-0.4.1/docs/how-to/clean-up-segments.md +0 -82
- sharedbox-0.4.1/docs/how-to/follow-a-whole-reference-graph.md +0 -70
- sharedbox-0.4.1/docs/how-to/name-a-box.md +0 -76
- sharedbox-0.4.1/docs/how-to/send-a-box-to-another-process.md +0 -69
- sharedbox-0.4.1/docs/how-to/set-defaults-and-check-values.md +0 -75
- sharedbox-0.4.1/docs/how-to/store-arrays.md +0 -58
- sharedbox-0.4.1/docs/how-to/store-collections.md +0 -59
- sharedbox-0.4.1/docs/how-to/store-records.md +0 -68
- sharedbox-0.4.1/docs/how-to/store-text-and-bytes.md +0 -66
- sharedbox-0.4.1/docs/index.md +0 -63
- sharedbox-0.4.1/docs/tutorials/react-to-changes.md +0 -130
- sharedbox-0.4.1/docs/tutorials/refer-to-another-box.md +0 -92
- sharedbox-0.4.1/docs/tutorials/share-a-record.md +0 -115
- sharedbox-0.4.1/tests/test_types_records_attrs.py +0 -21
- sharedbox-0.4.1/tests/test_types_records_msgspec.py +0 -35
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.clang-format +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.claude/settings.json +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.github/dependabot.yml +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.github/workflows/check-docs.yaml +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.github/workflows/ci.yaml +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.github/workflows/codspeed.yml +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.github/workflows/publish-docs.yaml +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/.gitignore +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/CMakeLists.txt +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/LICENSE +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/cmake/sharedbox-config.cmake +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/cmake/sharedbox-require-cxx.cmake +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/assets/benchmarks/ops-dark.svg +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/assets/benchmarks/ops-light.svg +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/assets/benchmarks/roundtrip-dark.svg +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/assets/benchmarks/roundtrip-light.svg +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/change_several_fields_at_once.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/clean_up_segments.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/follow_a_whole_reference_graph.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/name_a_box.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/send_a_box_to_another_process.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/set_defaults_and_check_values.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/store_collections.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/store_records.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/examples/store_text_and_bytes.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/how-to/ai-contribution-policy.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/arrays.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/box.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/errors.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/events.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/index.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/library-authors.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/api/references.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/reference/changelog.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/docs/tutorials/motor.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/include/sharedbox/sharedbox_c.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/include/sharedbox/sharedbox_c.h +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/includes/abbreviations.md +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/prek.toml +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/scripts/check_xrefs.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/scripts/vscode_setup.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/__init__.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_arrays.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_events.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_follow.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/codec.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/codec.hpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/scalars.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_native/scalars.hpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/_refs.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/__init__.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/__main__.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/cli.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/plot.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/roundtrip.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/size.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/benchmarks/size_diff.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/src/sharedbox/py.typed +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/conftest.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/compile_fail.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/consumer/CMakeLists.txt +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/consumer/consumer.c +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/table_builder.hpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_atomics.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_c_smoke.c +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_c_smoke_box.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_fork.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_layout2.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_lock.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_mapping.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_result.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_slots.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_small_shm.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_types.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_values.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_wait.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/test_windows_h.cpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/cpp/unique.hpp +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/crossproc.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/forged_types.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/refs_future.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/stress_helpers.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_contention.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_crashes.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_follow.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_lock_timeout.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_memory.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_waiters.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/stress/test_stress_watchers.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_benchbox_plot.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_capsule.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_doc_examples.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_doc_tutorials.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_events_async.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_events_bytes.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_events_signals.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_events_sync.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_exit.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_fields.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_follow.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_layout.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_layout_v1.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_native_codec.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_native_liveness.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_native_segment.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_native_types.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_native_wait.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_native_waiters.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_properties_codec.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_properties_forged.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_properties_layout.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_refs.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_refs_lazy.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_arrays.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_arrays_torch.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_choices.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_collections.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_records.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_scalars.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/test_types_unions.py +0 -0
- {sharedbox-0.4.1 → sharedbox-0.5.0}/tests/type_checks/events_types.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Contention
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
paths:
|
|
7
|
+
- include/sharedbox/**
|
|
8
|
+
- src/sharedbox/_native/**
|
|
9
|
+
- src/sharedbox/benchmarks/contention.py
|
|
10
|
+
- .github/workflows/contention.yaml
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
contention:
|
|
18
|
+
name: Contention on ${{ matrix.os }}
|
|
19
|
+
runs-on: ${{ matrix.os }}
|
|
20
|
+
timeout-minutes: 30
|
|
21
|
+
strategy:
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
os: [ ubuntu-latest, windows-latest ]
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v7
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
|
|
30
|
+
- uses: astral-sh/setup-uv@v7
|
|
31
|
+
|
|
32
|
+
- name: Install the project
|
|
33
|
+
run: uv sync --frozen --python 3.12
|
|
34
|
+
|
|
35
|
+
- name: Run benchbox contention
|
|
36
|
+
shell: bash
|
|
37
|
+
run: |
|
|
38
|
+
cores=$(uv run --no-sync python -c "import os; print(os.cpu_count())")
|
|
39
|
+
echo "### Contention on ${{ matrix.os }}, $cores logical cores" >> "$GITHUB_STEP_SUMMARY"
|
|
40
|
+
uv run --no-sync benchbox contention --json contention.json --markdown >> "$GITHUB_STEP_SUMMARY"
|
|
41
|
+
|
|
42
|
+
- uses: actions/upload-artifact@v7
|
|
43
|
+
with:
|
|
44
|
+
name: contention-${{ matrix.os }}
|
|
45
|
+
path: contention.json
|
|
@@ -9,6 +9,44 @@ Dates are marked as `DD-MM-YYYY`
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.5.0] - 09-10-2026
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `SharedBox.read_into`: copies an array field into an array the caller
|
|
17
|
+
passes and returns it, without allocating a new array.
|
|
18
|
+
- `SharedBox.writing`: a context manager that holds the box's write lock
|
|
19
|
+
and yields an array field as an array to fill in place.
|
|
20
|
+
- `benchbox contention`: times writes and reads of an `int` while several
|
|
21
|
+
writer and reader processes share it, for a box, `mp.Value` and
|
|
22
|
+
`SharedMemory` with a `Lock`.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- `SharedBox`: a field named `read_into` raises `TypeError`.
|
|
27
|
+
- `SharedBox`: a field named `writing` raises `TypeError`.
|
|
28
|
+
- `SharedBox`: reading a collection, tuple or record field whose members
|
|
29
|
+
are `bool`, `int`, `float` or `str` takes less time: about 90 ns less
|
|
30
|
+
for a list of 16 `float` on Windows.
|
|
31
|
+
- `SharedBox`: reading a field whose value cannot be changed (a frozen
|
|
32
|
+
dataclass, attrs class or msgspec Struct, a NamedTuple, a tuple, a
|
|
33
|
+
frozenset, an enum, flag or literal, `complex`, `date`, `time`,
|
|
34
|
+
`datetime`, `timedelta`, `UUID` or `Decimal`) returns the value the
|
|
35
|
+
previous read built until the field is written: 40 ns instead of 540 ns
|
|
36
|
+
for a frozen dataclass of four fields on Windows. A tuple, frozenset or
|
|
37
|
+
record qualifies only when its members are of these kinds or `bool`,
|
|
38
|
+
`int`, `float`, `str` or `bytes`.
|
|
39
|
+
- `SharedBox`: a frozen record's `__post_init__`, attrs converters and
|
|
40
|
+
validators run on the first read after each write instead of on every
|
|
41
|
+
read.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- `SharedBox.attach`: a box whose header gives a field count other than the
|
|
46
|
+
class's raises `SchemaMismatchError` instead of `ValueError`.
|
|
47
|
+
|
|
48
|
+
## [0.4.1] - 07-10-2026
|
|
49
|
+
|
|
12
50
|
### Changed
|
|
13
51
|
|
|
14
52
|
- `SharedBox.update`: takes about 7 to 9 ns less per call on Windows.
|
|
@@ -292,7 +330,9 @@ stage.events.nested.connect(lambda path, new, old: print(path, new))
|
|
|
292
330
|
|
|
293
331
|
- Initial release
|
|
294
332
|
|
|
295
|
-
[Unreleased]: https://github.com/jacopoabramo/sharedbox/compare/v0.
|
|
333
|
+
[Unreleased]: https://github.com/jacopoabramo/sharedbox/compare/v0.5.0...HEAD
|
|
334
|
+
[0.5.0]: https://github.com/jacopoabramo/sharedbox/compare/v0.4.1...v0.5.0
|
|
335
|
+
[0.4.1]: https://github.com/jacopoabramo/sharedbox/compare/v0.4.0...v0.4.1
|
|
296
336
|
[0.4.0]: https://github.com/jacopoabramo/sharedbox/compare/v0.3.1...v0.4.0
|
|
297
337
|
[0.3.1]: https://github.com/jacopoabramo/sharedbox/compare/v0.3.0...v0.3.1
|
|
298
338
|
[0.3.0]: https://github.com/jacopoabramo/sharedbox/compare/0.2.4...v0.3.0
|
|
@@ -33,15 +33,17 @@ sharedbox/
|
|
|
33
33
|
| |-- py.typed
|
|
34
34
|
| |-- benchmarks/ the benchbox command (extra: benchmarks)
|
|
35
35
|
| | |-- cli.py entry point; exits with an install hint without Typer
|
|
36
|
-
| | |-- _app.py Typer commands: ops, roundtrip, size, plot, all
|
|
36
|
+
| | |-- _app.py Typer commands: ops, roundtrip, contention, size, plot, all
|
|
37
37
|
| | |-- ops.py pyperf timings of single operations, against the stdlib
|
|
38
38
|
| | |-- roundtrip.py cross-process round trip percentiles
|
|
39
|
+
| | |-- contention.py throughput and percentiles with several writer and reader processes
|
|
39
40
|
| | |-- plot.py SVG charts of the ops and roundtrip results (matplotlib)
|
|
40
41
|
| | |-- size.py wheel and extension size (standard library only)
|
|
41
42
|
| | `-- size_diff.py wheel size table against main, for CI (standard library only)
|
|
42
43
|
| `-- _native/
|
|
43
44
|
| |-- module.cpp nanobind module: Segment, the Field descriptor, the BoxMethod type
|
|
44
|
-
| | (native update and snapshot) and the
|
|
45
|
+
| | (native update and snapshot), ValueCache (values kept for reuse) and the
|
|
46
|
+
| | error classes
|
|
45
47
|
| |-- codec.{hpp,cpp} converts field values to and from their stored bytes; encode_all
|
|
46
48
|
| | checks every value of an update before any is written,
|
|
47
49
|
| | with_record reads the whole record at once
|
|
@@ -51,6 +53,7 @@ sharedbox/
|
|
|
51
53
|
|-- tests/ pytest; many tests spawn processes
|
|
52
54
|
| |-- crossproc.py helpers that run a box in another process
|
|
53
55
|
| |-- forged_types.py builds segments with forged description tables
|
|
56
|
+
| |-- test_benchbox_contention.py benchbox contention, a short run
|
|
54
57
|
| |-- test_benchbox_plot.py benchbox plot; skipped without matplotlib and pyperf
|
|
55
58
|
| |-- test_capsule.py __sharedbox_box__, and the C consumer in tests/cpp/consumer/
|
|
56
59
|
| |-- test_doc_examples.py runs each docs/examples/*.py script
|
|
@@ -78,6 +81,8 @@ sharedbox/
|
|
|
78
81
|
|-- .github/workflows/check-docs.yaml builds the site and runs check_xrefs.py
|
|
79
82
|
|-- .github/workflows/publish-docs.yaml deploys the checked site to GitHub Pages
|
|
80
83
|
|-- .github/workflows/codspeed.yml benchmarks on CodSpeed
|
|
84
|
+
|-- .github/workflows/contention.yaml benchbox contention on Linux and Windows, for pull requests
|
|
85
|
+
| that touch the native code, and by hand
|
|
81
86
|
|-- CMakeLists.txt sharedbox::headers, sharedbox::c, extension build
|
|
82
87
|
|-- stubtest-allowlist.txt stubtest exceptions for nanobind types
|
|
83
88
|
|-- .clang-format clang-format style for the C and C++ sources
|
|
@@ -280,11 +285,12 @@ if __name__ == "__main__":
|
|
|
280
285
|
Motor.unlink()
|
|
281
286
|
```
|
|
282
287
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
+
A read decodes the stored bytes; a value that cannot be changed (a
|
|
289
|
+
`datetime`, or a frozen record, tuple or frozenset holding only such values)
|
|
290
|
+
is reused until its field is written. `update(**values)` writes several
|
|
291
|
+
fields at once; `watch(field)` and `events` report changes from any process.
|
|
292
|
+
C++ and C code take a box through `__sharedbox_box__` or open it by name; see
|
|
293
|
+
`docs/how-to/accept-a-box-in-cpp.md`, `docs/how-to/accept-a-box-in-c.md` and
|
|
288
294
|
`docs/how-to/open-a-box-from-a-program.md`.
|
|
289
295
|
|
|
290
296
|
## Docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharedbox
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Python containers using shared memory.
|
|
5
5
|
Author-Email: Jacopo Abramo <jacopo.abramo@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -34,16 +34,24 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
> [!WARNING]
|
|
35
35
|
> This project is a work in progress; be patient or feel free to contribute.
|
|
36
36
|
|
|
37
|
-
`sharedbox`
|
|
37
|
+
`sharedbox` lets several Python processes share one record, as if they all
|
|
38
|
+
held the same dataclass. You declare the fields once, and every process that
|
|
39
|
+
opens the record reads and writes the same values in shared memory, so no
|
|
40
|
+
process has to send them to another.
|
|
38
41
|
|
|
39
42
|
## Installation
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
`sharedbox` needs CPython 3.11 or newer on Windows x64 or Linux x86_64. PyPI
|
|
45
|
+
has compiled wheels, so you don't need a compiler:
|
|
42
46
|
|
|
43
47
|
```sh
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
pip install sharedbox
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
or, in a project that [`uv`](https://docs.astral.sh/uv/) manages:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
uv add sharedbox
|
|
47
55
|
```
|
|
48
56
|
|
|
49
57
|
## Quick start
|
|
@@ -76,14 +84,32 @@ if __name__ == "__main__":
|
|
|
76
84
|
Motor.unlink()
|
|
77
85
|
```
|
|
78
86
|
|
|
87
|
+
The child finds the box by its class alone, and the parent prints the change
|
|
88
|
+
as soon as the child makes it.
|
|
89
|
+
|
|
79
90
|
## Documentation
|
|
80
91
|
|
|
81
|
-
The [documentation site](https://jacopoabramo.github.io/sharedbox)
|
|
82
|
-
|
|
83
|
-
reference.
|
|
92
|
+
The [documentation site](https://jacopoabramo.github.io/sharedbox) starts
|
|
93
|
+
with three tutorials, then has how-to guides, explanations of how a
|
|
94
|
+
box works, and the API reference. C and C++ code can use a box too, either
|
|
95
|
+
handed over from Python or opened by name; the
|
|
96
|
+
[C and C++ guides](https://jacopoabramo.github.io/sharedbox/how-to/accept-a-box-in-cpp/)
|
|
97
|
+
show how.
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
To change `sharedbox` itself you need `git`, `uv`, CMake 3.30 or newer and a
|
|
102
|
+
C++20 compiler (MSVC or GCC):
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
git clone https://github.com/jacopoabramo/sharedbox.git
|
|
106
|
+
cd sharedbox
|
|
107
|
+
uv sync --dev
|
|
108
|
+
uv run pytest
|
|
109
|
+
```
|
|
84
110
|
|
|
85
|
-
|
|
86
|
-
|
|
111
|
+
[How to set up a development environment](https://jacopoabramo.github.io/sharedbox/how-to/set-up-development/)
|
|
112
|
+
has the details.
|
|
87
113
|
|
|
88
114
|
## License
|
|
89
115
|
|
|
@@ -10,16 +10,24 @@
|
|
|
10
10
|
> [!WARNING]
|
|
11
11
|
> This project is a work in progress; be patient or feel free to contribute.
|
|
12
12
|
|
|
13
|
-
`sharedbox`
|
|
13
|
+
`sharedbox` lets several Python processes share one record, as if they all
|
|
14
|
+
held the same dataclass. You declare the fields once, and every process that
|
|
15
|
+
opens the record reads and writes the same values in shared memory, so no
|
|
16
|
+
process has to send them to another.
|
|
14
17
|
|
|
15
18
|
## Installation
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
`sharedbox` needs CPython 3.11 or newer on Windows x64 or Linux x86_64. PyPI
|
|
21
|
+
has compiled wheels, so you don't need a compiler:
|
|
18
22
|
|
|
19
23
|
```sh
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
pip install sharedbox
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
or, in a project that [`uv`](https://docs.astral.sh/uv/) manages:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
uv add sharedbox
|
|
23
31
|
```
|
|
24
32
|
|
|
25
33
|
## Quick start
|
|
@@ -52,14 +60,32 @@ if __name__ == "__main__":
|
|
|
52
60
|
Motor.unlink()
|
|
53
61
|
```
|
|
54
62
|
|
|
63
|
+
The child finds the box by its class alone, and the parent prints the change
|
|
64
|
+
as soon as the child makes it.
|
|
65
|
+
|
|
55
66
|
## Documentation
|
|
56
67
|
|
|
57
|
-
The [documentation site](https://jacopoabramo.github.io/sharedbox)
|
|
58
|
-
|
|
59
|
-
reference.
|
|
68
|
+
The [documentation site](https://jacopoabramo.github.io/sharedbox) starts
|
|
69
|
+
with three tutorials, then has how-to guides, explanations of how a
|
|
70
|
+
box works, and the API reference. C and C++ code can use a box too, either
|
|
71
|
+
handed over from Python or opened by name; the
|
|
72
|
+
[C and C++ guides](https://jacopoabramo.github.io/sharedbox/how-to/accept-a-box-in-cpp/)
|
|
73
|
+
show how.
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
To change `sharedbox` itself you need `git`, `uv`, CMake 3.30 or newer and a
|
|
78
|
+
C++20 compiler (MSVC or GCC):
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
git clone https://github.com/jacopoabramo/sharedbox.git
|
|
82
|
+
cd sharedbox
|
|
83
|
+
uv sync --dev
|
|
84
|
+
uv run pytest
|
|
85
|
+
```
|
|
60
86
|
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
[How to set up a development environment](https://jacopoabramo.github.io/sharedbox/how-to/set-up-development/)
|
|
88
|
+
has the details.
|
|
63
89
|
|
|
64
90
|
## License
|
|
65
91
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from collections.abc import Iterator
|
|
3
|
+
from dataclasses import dataclass
|
|
3
4
|
from itertools import count
|
|
4
5
|
from typing import Annotated
|
|
5
6
|
|
|
@@ -17,6 +18,22 @@ class Record(SharedBox):
|
|
|
17
18
|
s: Annotated[str, Capacity(32)]
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
class Samples(SharedBox):
|
|
22
|
+
floats: Annotated[list[float], Capacity(16)]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class Quad:
|
|
27
|
+
a: int
|
|
28
|
+
b: float
|
|
29
|
+
c: bool
|
|
30
|
+
d: int
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Quads(SharedBox):
|
|
34
|
+
quad: Quad
|
|
35
|
+
|
|
36
|
+
|
|
20
37
|
@pytest.fixture
|
|
21
38
|
def box() -> Iterator[Record]:
|
|
22
39
|
name = f"bench-codspeed-{os.getpid()}-{next(NAMES)}"
|
|
@@ -55,3 +72,27 @@ def test_native_set_int(benchmark: BenchmarkFixture, box: Record) -> None:
|
|
|
55
72
|
|
|
56
73
|
def test_native_get_int(benchmark: BenchmarkFixture, box: Record) -> None:
|
|
57
74
|
benchmark(box._segment.get, 0)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.fixture
|
|
78
|
+
def samples() -> Iterator[Samples]:
|
|
79
|
+
name = f"bench-codspeed-{os.getpid()}-{next(NAMES)}"
|
|
80
|
+
with Samples.create(name, [0.5] * 16) as box:
|
|
81
|
+
yield box
|
|
82
|
+
Samples.unlink(name)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_read_float_list(benchmark: BenchmarkFixture, samples: Samples) -> None:
|
|
86
|
+
benchmark(getattr, samples, "floats")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.fixture
|
|
90
|
+
def quads() -> Iterator[Quads]:
|
|
91
|
+
name = f"bench-codspeed-{os.getpid()}-{next(NAMES)}"
|
|
92
|
+
with Quads.create(name, Quad(1, 1.5, True, 2)) as box:
|
|
93
|
+
yield box
|
|
94
|
+
Quads.unlink(name)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_read_record(benchmark: BenchmarkFixture, quads: Quads) -> None:
|
|
98
|
+
benchmark(getattr, quads, "quad")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Shared look of every diagram in the docs. A diagram pulls it in with
|
|
2
|
+
# `...@diagrams/style` on its first line and gives shapes these classes.
|
|
3
|
+
classes: {
|
|
4
|
+
layer: {
|
|
5
|
+
style: {
|
|
6
|
+
border-radius: 10
|
|
7
|
+
font-size: 18
|
|
8
|
+
bold: true
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
step: {
|
|
12
|
+
style.border-radius: 6
|
|
13
|
+
}
|
|
14
|
+
current: {
|
|
15
|
+
style: {
|
|
16
|
+
border-radius: 6
|
|
17
|
+
stroke-width: 4
|
|
18
|
+
bold: true
|
|
19
|
+
shadow: true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
done: {
|
|
23
|
+
style: {
|
|
24
|
+
border-radius: 6
|
|
25
|
+
opacity: 0.4
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
failed: {
|
|
29
|
+
style: {
|
|
30
|
+
border-radius: 6
|
|
31
|
+
stroke: "#c62828"
|
|
32
|
+
stroke-dash: 4
|
|
33
|
+
font-color: "#c62828"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
process: {
|
|
37
|
+
shape: hexagon
|
|
38
|
+
}
|
|
39
|
+
hardware: {
|
|
40
|
+
shape: cylinder
|
|
41
|
+
}
|
|
42
|
+
file: {
|
|
43
|
+
shape: page
|
|
44
|
+
}
|
|
45
|
+
note: {
|
|
46
|
+
shape: text
|
|
47
|
+
style: {
|
|
48
|
+
italic: true
|
|
49
|
+
font-size: 14
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
gap: {
|
|
53
|
+
label: ""
|
|
54
|
+
style.opacity: 0
|
|
55
|
+
}
|
|
56
|
+
# a shape a later step brings in: declared on the first board so that every
|
|
57
|
+
# board keeps the same layout, and shown by a step that changes its class
|
|
58
|
+
hidden: {
|
|
59
|
+
style.opacity: 0
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -23,6 +23,18 @@ sensor.frame = np.full((4, 6), 7, np.uint8)
|
|
|
23
23
|
print(int(sensor.frame.sum())) # 168
|
|
24
24
|
# --8<-- [end:write]
|
|
25
25
|
|
|
26
|
+
# --8<-- [start:read-into]
|
|
27
|
+
frame = np.empty((4, 6), np.uint8)
|
|
28
|
+
sensor.read_into("frame", frame)
|
|
29
|
+
print(int(frame.sum())) # 168
|
|
30
|
+
# --8<-- [end:read-into]
|
|
31
|
+
|
|
32
|
+
# --8<-- [start:writing]
|
|
33
|
+
with sensor.writing("frame") as frame:
|
|
34
|
+
frame[0, :] = 255
|
|
35
|
+
print(int(sensor.frame[0].sum())) # 1530
|
|
36
|
+
# --8<-- [end:writing]
|
|
37
|
+
|
|
26
38
|
# --8<-- [start:any-library]
|
|
27
39
|
raw = sensor.raw
|
|
28
40
|
print(np.from_dlpack(raw)) # [0. 0. 0.]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: lucide/lightbulb
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Checking a process is alive
|
|
6
|
+
|
|
7
|
+
Sometimes `sharedbox` has to answer a simple-sounding question: is the
|
|
8
|
+
process recorded in a [segment](glossary.md#segment) still running? The
|
|
9
|
+
answer decides whether a crashed waiter's slot can be freed, and what an
|
|
10
|
+
error message tells you about who holds a name. This page explains how it
|
|
11
|
+
answers, and why the obvious check isn't enough.
|
|
12
|
+
|
|
13
|
+
## A pid and a start time
|
|
14
|
+
|
|
15
|
+
The obvious check would be "is there a process with this process id
|
|
16
|
+
(pid)?", but that isn't enough: once a process exits, the operating system
|
|
17
|
+
can give its pid to a new one. So `sharedbox.hpp` names a process by its
|
|
18
|
+
pid and its start time, the way the `psutil` library tells a process from a
|
|
19
|
+
later one with the same pid.[^psutil] On Windows the start
|
|
20
|
+
time is the creation time from `GetProcessTimes`. On Linux it is field 22
|
|
21
|
+
of `/proc/<pid>/stat`.[^proc-stat] `process_alive()` reads the start time
|
|
22
|
+
of whatever process has the pid now and decides like this:
|
|
23
|
+
|
|
24
|
+
```d2 title="Is the recorded process still running?"
|
|
25
|
+
...@diagrams/style
|
|
26
|
+
direction: down
|
|
27
|
+
pid: "a process has the pid?" {class: step}
|
|
28
|
+
zombie: "on Linux: a zombie?" {
|
|
29
|
+
class: step
|
|
30
|
+
tooltip: A zombie, state Z or X in /proc/PID/stat, has exited and keeps its entry only until its parent collects it.
|
|
31
|
+
}
|
|
32
|
+
readable: "start time readable?" {
|
|
33
|
+
class: step
|
|
34
|
+
tooltip: It is not when the process belongs to another user or /proc is mounted with hidepid.
|
|
35
|
+
}
|
|
36
|
+
same: "same start time?" {class: step}
|
|
37
|
+
dead: "dead" {class: failed}
|
|
38
|
+
alive: "alive" {class: current}
|
|
39
|
+
pid -> dead: "no"
|
|
40
|
+
pid -> zombie: "yes"
|
|
41
|
+
zombie -> dead: "yes"
|
|
42
|
+
zombie -> readable: "no"
|
|
43
|
+
readable -> alive: "no" {
|
|
44
|
+
tooltip: The process exists and nothing shows it is a different one, so it counts as alive.
|
|
45
|
+
}
|
|
46
|
+
readable -> same: "yes"
|
|
47
|
+
same -> alive: "yes"
|
|
48
|
+
same -> dead: "no, the pid was reused"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A recorded start time that couldn't be read is treated the same way: the
|
|
52
|
+
process counts as alive.[^proc-stat]
|
|
53
|
+
|
|
54
|
+
On Linux there's one more catch: containers give their processes their own
|
|
55
|
+
set of pids, called a pid namespace, so the same pid can mean different
|
|
56
|
+
processes in different containers. That's why
|
|
57
|
+
[waiter slots](glossary.md#waiter-slot) and the header's creator fields also
|
|
58
|
+
record the namespace, and a process in another namespace, or one whose
|
|
59
|
+
namespace is unknown, is never judged dead.
|
|
60
|
+
|
|
61
|
+
## Where the check is used
|
|
62
|
+
|
|
63
|
+
- Freeing the waiter slots of dead processes, as [Waiting for
|
|
64
|
+
changes](waiting-for-changes.md#waking-a-process) describes.
|
|
65
|
+
- Naming the creator in
|
|
66
|
+
[`SegmentExistsError`][sharedbox.SegmentExistsError]. When a create finds
|
|
67
|
+
the name taken, the extension reads the existing header with
|
|
68
|
+
`sharedbox::inspect()` and checks the creator it records. The creator
|
|
69
|
+
writes the header before it publishes the [box](glossary.md#box), so a
|
|
70
|
+
name that holds no published box is checked the same way. The docstring
|
|
71
|
+
of `SegmentExistsError` lists the messages that result.
|
|
72
|
+
|
|
73
|
+
Even when the creator has exited, nothing is removed automatically, because
|
|
74
|
+
other processes may still be using a segment whose creator died. Removing
|
|
75
|
+
it is your call; [How to clean up segments](../how-to/clean-up-segments.md)
|
|
76
|
+
shows how.
|
|
77
|
+
|
|
78
|
+
## Sources
|
|
79
|
+
|
|
80
|
+
[^psutil]:
|
|
81
|
+
psutil (BSD-3-Clause), `Process`: a process is identified by its pid
|
|
82
|
+
and its creation time, so a reused pid is not mistaken for the same
|
|
83
|
+
process.
|
|
84
|
+
<https://github.com/giampaolo/psutil>
|
|
85
|
+
|
|
86
|
+
[^proc-stat]:
|
|
87
|
+
Linux manual page `proc_pid_stat(5)`: the process state (field 3) and
|
|
88
|
+
`starttime` (field 22).
|
|
89
|
+
<https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html>
|