Skip to content

An analysis can be given a time limit (4.2.41) - #800

Merged
ddehilster merged 2 commits into
VisualText:masterfrom
ddehilster:feat/an-analysis-can-be-given-a-time-limit
Oct 26, 2026
Merged

ddehilster merged 2 commits into
VisualText:masterfrom
ddehilster:feat/an-analysis-can-be-given-a-time-limit

Conversation

@ddehilster

Copy link
Copy Markdown
Member

NLP++ runs as a C++ extension, so a grammar whose loop never ends can't be stopped from outside. A Python caller has no thread to interrupt, only a process to kill. And a loop that makes a new string every turn keeps growing until the process is killed for its memory. One such loop took a document extractor down five times in half an hour on one document, along with every analysis queued behind it. The loop itself came from the string-position bug fixed in #799; this PR covers the next loop, whatever causes it.

API

  • NLP_ENGINE::setRunLimit(ms): a limit on each text's analysis. 0 (the default) means no limit.
  • NLP_ENGINE::lastRunStopped(): true after an analyze() / analyzeStream() call that stopped a run at the limit.
  • nlp -TIMELIMIT <seconds>: a run it stops exits 2. That's distinct from success (0) and from the missing analyzer or input that exits 1.

How it stops

  • Each Parse takes the limit when it's created. Parse::overBudget() is checked on every while turn (it reads the clock every 256th time) and before every pass.
  • Past the limit, the loop fails its region and no further passes run. The stop is reported once, in err.log and on stderr, naming the pass and its file:
    [Run stopped: past its time limit of 2000 ms in pass 3 (loop); the rest of the analysis was not run.]
  • The run's string table is freed afterwards as usual, so the memory comes back.
  • Compiled analyzers get the same check: the generator writes Arun::overbudget(nlppp) into every while condition.

Tests

  • New fixture run-limit, run on Windows, Linux and macOS. It loops forever in pass 3, and checks that pass 2 ran, that neither the line after the loop nor pass 4 ran, that the message is on stderr, and that the exit status is 2.
  • The production analyzer that hung, run on 4.2.39's string functions so it still loops: -TIMELIMIT 5 stops it at 5 s with exit 2.

No limit means no change

  • 881 fixture inputs of 17 production analyzers give byte-identical output.json to 4.2.39.
  • 3 million while turns take the same time (about 3.0–3.2 s) with no limit and with a 600 s limit.
  • 52 of the 54 Windows CI steps pass locally. The two that don't need CI's environment: compile libraries for compiled-bridge, and the analyzers checkout for test-nlp.

This is rebased on #799 and builds as 4.2.41. The str-positions and run-limit steps both pass on the combined build.

The NLPPlus side (analyze(..., timeout=)) follows once this is in a release.

🤖 Generated with Claude Code

ddehilster and others added 2 commits October 25, 2026 23:27
NLP++ is a C++ extension. A grammar whose loop never ends cannot be
stopped from outside -- a Python caller has no thread to interrupt, only
a process to kill -- and a loop that makes a string a turn grows until
the process is killed for its memory. One such loop took a document
extractor down five times in half an hour on one document, and with it
every analysis queued behind that document.

- NLP_ENGINE::setRunLimit(ms) puts a limit on each text's analysis; 0,
  the default, is none. lastRunStopped() says whether the last analyze()
  or analyzeStream() call stopped a run at it.
- Each Parse takes the limit when it is made. Parse::overBudget is asked
  on every while turn (reading the clock every 256th time) and before
  every pass. Past the limit, the loop fails its region, no more passes
  run, and the stop is reported once in err.log and on stderr, naming
  the pass and its file. The run's string table is freed with it as
  usual, so the memory comes back.
- Compiled analyzers get the same check: the generator writes
  Arun::overbudget(nlppp) into every while condition.
- nlp -TIMELIMIT <seconds>. A run it stops exits 2: neither success nor
  the missing analyzer or input that exits 1.

Fixture run-limit loops forever in pass 3 and checks that pass 2 ran,
the line after the loop and pass 4 did not, the message is on stderr,
and nlp exits 2. On the analyzer that hung in production (with the old
string functions, so it still loops) -TIMELIMIT 5 stops it at 5 seconds.

No limit, no change: 881 fixture inputs of 17 production analyzers give
byte-identical output.json to 4.2.39, and 3 million while turns take the
same time with or without a limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
my_tchar.h maps each <tchar.h> name nlp uses to its char function on
platforms without one; _tstof was not among them, so main.cpp did not
compile there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ddehilster
ddehilster merged commit ff03cb3 into VisualText:master Oct 26, 2026
7 checks passed
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