Skip to content

Fix resource leaks, Perl interpreter lifecycle, and silent failures - #18

Open
quinnjr wants to merge 2 commits into
FIUBioRG:masterfrom
quinnjr:bugfix/perl-plugin-fixes
Open

Fix resource leaks, Perl interpreter lifecycle, and silent failures#18
quinnjr wants to merge 2 commits into
FIUBioRG:masterfrom
quinnjr:bugfix/perl-plugin-fixes

Conversation

@quinnjr

@quinnjr quinnjr commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Applies findings from a combined /simplify + /optimize + /audit pass, followed by a full multi-dimension code review of the resulting diff, then adds test coverage for the gaps that review surfaced.

  • Perl (src/languages/Perl.cxx, Perl.h): the interpreter is now constructed once and reused across plugin calls instead of a full alloc/parse/destruct per invocation (large perf win on pipelines with multiple Perl steps). Load failures now throw and are caught by main.cxx's existing fail-fast path instead of crashing the process or silently reusing the previous plugin's input/run/output subs.
  • PerlIO.pm: fixed a two-argument open() (shell-injection vector via a crafted filename), a scalar-context arg-parsing bug, a wrong-sigil return that made readParameters() always return nothing, and a missing trailing 1; (Perl modules must return true). Also flagged separately (not fixed here, needs a maintainer call): the module's name collides with Perl's own built-in PerlIO I/O-layer pragma, so use PerlIO; in a plugin silently resolves to the wrong module — will file as a separate issue.
  • src/languages/Compiled.cxx: fixed a leaked Plugin*/dlopen handle on every plugin invocation; mutex-guarded the new dlopen handle cache against concurrent LitterLaunch pipelines.
  • src/languages/Py.cxx: mutex-guarded the new sys.path dedup cache for the same concurrency reason; fixed a new[]/delete mismatch.
  • src/main.cxx: a plugin exception or "no suitable language for plugin" now aborts the pipeline (previously both were silent no-ops that let the run continue and exit 0). exit(1) no longer fires directly from a LitterLaunch worker thread — sibling threads get to finish first via a shared atomic flag checked after joins.
  • src/PluginManager.h: create() no longer null-derefs on an unregistered plugin name; supportedLanguages() is now properly gated by HAVE_PYTHON/HAVE_PERL/HAVE_R so --without-* builds actually link.
  • SConstruct: --without-{python,perl,r} now also gates compilation of the corresponding language source and linked libraries (previously only affected some targets, causing link failures); removed a hardcoded, machine-specific JVM library path left over from local debugging.
  • src/languages/R.cxx: fixed a dangling-pointer trap between unload()/load().
  • Assorted: PluGen generator bugs (undeclared Java field reference, OOB vector access, substring-vs-exact plugin-name matching), testPluMA.py's check() returning True after only checking the first line of a diff, Dockerfile layer ordering for build caching, dead-code removal, deduplicated the checkPool.py/getPool.py/getPlugins.py plugin-pool scraping logic into a shared pool_utils.py.

Test coverage added

  • plugins/PerlChainStepA + plugins/PerlChainStepB: two minimal Perl plugins chained in one config.txt, the first example anywhere in the repo that runs more than one Perl plugin per process — exercises the interpreter-reuse code above. Verified locally: both plugins execute in one process, each produces its own correct output (not stale/leaked from the other), matching .expected files exactly.
  • tests/test_negative_paths.sh (wired into CI): verifies ./pluma exits non-zero when a config references an unregistered plugin name, instead of the previous silent success. Verified locally against a live build.

Test plan

  • Full project build (scons, and scons --without-r) with no new compiler errors in any touched file
  • All touched Python files pass py_compile; PerlIO.pm and both new plugin scripts pass perl -c
  • Manually built ./pluma and ran both new test fixtures against it directly — both pass
  • CI (testPluMA.py + the new negative-path test) — will run automatically on this PR

Applies findings from a combined /simplify + /optimize + /audit pass
followed by a full /code-review of the resulting diff:

- Perl: interpreter is now constructed once and reused across plugin
  calls instead of a full alloc/parse/destruct per invocation, with
  proper error propagation (do FILE load failures now throw and are
  caught by main.cxx's existing fail-fast path instead of crashing or
  silently reusing the previous plugin's subs).
- PerlIO.pm: fixed two-argument open() (was a shell-injection vector),
  scalar-context arg bug, and a wrong-sigil return that made
  readParameters() always return nothing.
- Compiled.cxx: fixed a leaked Plugin*/dlopen handle per invocation and
  mutex-guarded the new dlopen handle cache against concurrent
  LitterLaunch pipelines.
- Py.cxx: mutex-guarded the new sys.path dedup cache for the same
  reason; fixed a new[]/delete mismatch.
- main.cxx: plugin exceptions and "no suitable language" now abort the
  pipeline (previously silent no-ops that let the run continue and
  exit 0); exit(1) no longer fires directly from a LitterLaunch worker
  thread, so sibling threads get to finish first.
- PluginManager.h: create() no longer null-derefs on an unregistered
  plugin name; --without-{python,perl,r} now actually gates
  supportedLanguages() so those builds link.
- SConstruct: --without-{python,perl,r} now also gates compilation of
  the corresponding language source and linked libraries; removed a
  hardcoded machine-specific JVM library path.
- R.cxx: fixed a dangling-pointer trap between unload()/load().
- Assorted: PluGen generator bugs (undeclared Java field, OOB vector
  access, substring-vs-exact plugin-name match), testPluMA.py's
  check() always returning True after the first line, Dockerfile
  layer ordering, dead code removal, deduplicated the
  checkPool.py/getPool.py/getPlugins.py scraping logic into
  pool_utils.py.

Adds test coverage for previously-untested paths: a two-Perl-plugin
chain (plugins/PerlChainStepA + PerlChainStepB) exercising the
interpreter-reuse code above, and tests/test_negative_paths.sh
verifying pluma exits non-zero on an unregistered plugin name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n-fixes

# Conflicts:
#	SConstruct
#	build_config.py
#	build_support.py
#	src/PluGen/main.cxx
#	src/languages/Compiled.cxx
#	src/main.cxx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant