Skip to content

fix(Foundation, Util, Prometheus, Zip): fixes found while building poco-osp - #5620

Merged
matejk merged 10 commits into
mainfrom
5616-foundation-util-prometheus-fixes
Nov 4, 2026
Merged

matejk merged 10 commits into
mainfrom
5616-foundation-util-prometheus-fixes

Conversation

@matejk

@matejk matejk commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Closes #5616.

Fixes found while building pocoproject/poco-osp; the issue lists the problems. One commit per library, the breaking
changes in their own; every fix has a test that fails without it. Please use a rebase merge.

  • Foundation: Glob::match() backtracks only to the last *; MemoryPool observers lock the mutex; on Windows,
    Process::launch() adds the given variables to the parent environment as on POSIX (PROCESS_CLEAR_ENVIRONMENT
    passes only them), and wait() works after kill().
  • Util: BasicEvent<const std::string> is instantiated where it is used.
  • Prometheus: a destroyed Collector unregisters itself.
  • Zip: copied entries keep their attributes, extra fields and data descriptor format.
  • Tests: four Foundation tests no longer depend on undefined behaviour or timing.

Breaking changes, in the CHANGELOG: the Windows environment default; classes derived from Collector call
unregister() first in their destructor.

Tests: Foundation, Util, Prometheus and Zip on macOS, Linux (ASan, UBSan) and Windows MSVC.

@matejk matejk added this to the Release 2.0 milestone Nov 2, 2026
@matejk matejk added bug breaking A breaking change labels Nov 2, 2026
@matejk
matejk requested a review from aleks-f November 2, 2026 13:33
Comment thread Prometheus/src/Registry.cpp Dismissed
Comment thread Foundation/include/Poco/MemoryPool.h Dismissed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Collector teardown can access a destroyed registry and race with export’s virtual dispatch, while Windows process termination still mishandles exited processes with code 259.

3 open issues
What changed in this PR

Addresses issues found while building poco-osp across Foundation, Util, Prometheus, and Zip, with regression tests and compatibility documentation.

Changes:

  • Bounds glob backtracking, synchronizes memory-pool counters, and corrects Windows process environment and termination behavior.
  • Adjusts configuration-event instantiation, adds collector unregistration, and preserves metadata when copying ZIP entries.
  • Adds regression coverage and removes overflow, unchecked-operation, and timing problems from existing tests.
File Description
Zip/​testsuite/​src/​CompressTest.h Declares ZIP-copy regression tests.
Zip/​testsuite/​src/​CompressTest.cpp Tests extra fields, attributes, and ZIP64 descriptors.
Zip/​src/​ZipManipulator.cpp Passes directory metadata to copy operations.
Zip/​src/​ZipFileInfo.cpp Implements attribute copying.
Zip/​src/​Rename.cpp Forwards metadata when renaming entries.
Zip/​src/​Keep.cpp Forwards metadata when retaining entries.
Zip/​src/​Compress.cpp Preserves attributes and corrects descriptor serialization.
Zip/​include/​Poco/​Zip/​ZipFileInfo.h Declares attribute-copying support.
Zip/​include/​Poco/​Zip/​ZipArchive.h Adds central-directory entry lookup.
Zip/​include/​Poco/​Zip/​Rename.h Stores optional source metadata.
Zip/​include/​Poco/​Zip/​Keep.h Stores optional source metadata.
Zip/​include/​Poco/​Zip/​Compress.h Extends raw-copy API with metadata.
Util/​testsuite/​src/​ConfigurationTestSuite.cpp Registers configuration-event tests.
Util/​testsuite/​src/​ConfigurationEventTest.h Declares event-instantiation regression coverage.
Util/​testsuite/​src/​ConfigurationEventTest.cpp Tests include order and configuration events.
Util/​testsuite/​Makefile Builds the new event tests.
Util/​src/​AbstractConfiguration.cpp Removes explicit string-event instantiation.
Util/​include/​Poco/​Util/​AbstractConfiguration.h Allows string-event instantiation at use sites.
Prometheus/​testsuite/​src/​RegistryTest.h Declares registry-lifetime tests.
Prometheus/​testsuite/​src/​RegistryTest.cpp Tests registration, teardown, and concurrent export.
Prometheus/​testsuite/​src/​PrometheusTestSuite.cpp Registers registry tests.
Prometheus/​testsuite/​Makefile Builds registry tests.
Prometheus/​src/​ThreadPoolCollector.cpp Unregisters during destruction.
Prometheus/​src/​Registry.cpp Tracks registrations and clears collector back-references.
Prometheus/​src/​ProcessCollector.cpp Unregisters during destruction.
Prometheus/​src/​IntGauge.cpp Unregisters during destruction.
Prometheus/​src/​IntCounter.cpp Unregisters during destruction.
Prometheus/​src/​Histogram.cpp Unregisters during destruction.
Prometheus/​src/​Gauge.cpp Unregisters during destruction.
Prometheus/​src/​Counter.cpp Unregisters during destruction.
Prometheus/​src/​Collector.cpp Implements automatic unregistration.
Prometheus/​include/​Poco/​Prometheus/​Registry.h Documents registry-lifetime behavior.
Prometheus/​include/​Poco/​Prometheus/​Collector.h Adds registry tracking and destructor guidance.
Prometheus/​include/​Poco/​Prometheus/​CallbackMetric.h Unregisters during destruction.
Foundation/​testsuite/​src/​StringTest.cpp Uses unsigned benchmark accumulators.
Foundation/​testsuite/​src/​SPSCQueueTest.cpp Uses a 64-bit sum and meaningful assertion.
Foundation/​testsuite/​src/​ProcessTest.h Declares environment and termination tests.
Foundation/​testsuite/​src/​ProcessTest.cpp Tests environment inheritance and post-kill behavior.
Foundation/​testsuite/​src/​ProcessRunnerTest.cpp Waits for PID content and checks directory changes.
Foundation/​testsuite/​src/​MemoryPoolTest.h Declares concurrent observer tests.
Foundation/​testsuite/​src/​MemoryPoolTest.cpp Exercises counters during concurrent allocation.
Foundation/​testsuite/​src/​GlobTest.h Declares backtracking regression tests.
Foundation/​testsuite/​src/​GlobTest.cpp Tests matching behavior and bounded execution time.
Foundation/​testsuite/​src/​FileTest.cpp Checks directory-change and symlink results.
Foundation/​src/​Process_WIN32U.cpp Merges environments and retains killed-process handles.
Foundation/​src/​Glob.cpp Backtracks only to the latest asterisk.
Foundation/​include/​Poco/​ProcessOptions.h Updates environment-option documentation.
Foundation/​include/​Poco/​Process.h Documents inheritance and waiting after kill.
Foundation/​include/​Poco/​MemoryPool.h Synchronizes counters and clarifies their units.
CHANGELOG Records environment and collector compatibility changes.
AI/​MCP/​include/​Poco/​AI/​MCP/​ProcessClient.h Updates inherited-environment documentation.

Review effort: Balanced

Comment on lines +43 to +46
/// A class derived from Collector must call unregister() at the
/// start of its destructor, before any member that exportTo()
/// uses is destroyed; otherwise a Registry::exportTo() running
/// in another thread can use a partially destroyed object.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could not reproduce this for the classes of this PR, with clang and GCC ThreadSanitizer, high contention and an exporter that forces the overlap: the vptr store at the entry of the most-derived destructor writes the value the object already has, and every store that changes the vptr comes after unregister() has taken the registry mutex. The checks found two related problems, which are fixed now: a collector registers at the end of the constructor of its concrete class (before, an export could reach an object under construction), and the documentation states the rules for every class in the hierarchy, also for a class derived from Counter or Gauge. testConcurrentExportAndDestruction now forces the overlap.

Comment on lines +37 to +38
Registry* pRegistry = _pRegistry.load();
if (pRegistry != nullptr) pRegistry->unregisterCollector(this);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed with ThreadSanitizer: it happens when a Registry is destroyed while another thread destroys one of its collectors. That is the general rule that an object must not be destroyed while another thread still uses it, so the Registry and Collector documentation now states it as a precondition instead of adding reference-counted registration state.

Comment thread Foundation/src/Process_WIN32U.cpp Outdated
Comment on lines +490 to +491
DWORD exitCode = 0;
if (TerminateProcess(handle.process(), 0) == 0 && (GetExitCodeProcess(handle.process(), &exitCode) == 0 || exitCode == STILL_ACTIVE))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. kill() now treats a process whose handle is signalled as ended, so an exit code of 259 no longer makes it throw; ProcessTest::testKillAfterExitWithCodeStillActive covers it on Windows. tryWait() and isRunning() have the same STILL_ACTIVE ambiguity on main and are left for a separate change.

matejk added 10 commits November 3, 2026 20:23
Glob::match() backtracks only to the last '*'
MemoryPool and FastMemoryPool observers lock the mutex
FastMemoryPool observers are documented as block counts
StringTest benchmarks sum into an unsigned sink
SPSCQueueTest sums the throughput values in Int64
FileTest checks the chdir and symlink results
ProcessRunnerTest waits until the child PID is written
ProcessRunnerTest checks the result of changeDir()
ProcessRunnerTest comment uses ASCII only
ProcessRunnerTest builds without MSVC warning C4456
launch() adds env to the parent environment on Windows
ProcessHandle::wait() works after kill() on Windows
AI/MCP ProcessClient documents env as added on every platform
Compress::addFileRaw() keeps extra fields of copied entries
ZipManipulator keeps the attributes of copied entries
Copied entries get the data descriptor format of their header
@matejk
matejk force-pushed the 5616-foundation-util-prometheus-fixes branch from 6028297 to a3f9731 Compare November 3, 2026 19:38
@matejk
matejk merged commit d1ce197 into main Nov 4, 2026
65 checks passed
@matejk
matejk deleted the 5616-foundation-util-prometheus-fixes branch November 4, 2026 06:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking A breaking change bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Foundation, Util, Prometheus and Zip: fixes found while building poco-osp

3 participants