Skip to content

Document the secondary pool's task contract - #3610

Open
MattBDev wants to merge 2 commits into
mainfrom
docs/secondary-pool-contract
Open

Document the secondary pool's task contract#3610
MattBDev wants to merge 2 commits into
mainfrom
docs/secondary-pool-contract

Conversation

@MattBDev

@MattBDev MattBDev commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@dordsor21 — this is the documentation work you asked for, split out as promised in #3609. It stands alone and doesn't depend on that PR landing.

What this adds

QueueHandler#async's javadoc said what the secondary pool is for ("cleanup" tasks, possibly IO-bound) but never said what a task submitted to it must not do. Two rules were load-bearing and undocumented:

1. A task must not wait on anything this pool completes.

ForkJoinPool only compensates for blocking it can observe through managedBlock. It cannot see monitorenter or Future#get, so a blocked worker is still counted as running and no replacement is started. Every worker can end up parked waiting for work only this pool can perform.

The javadoc now states the rule and the remedy you described — if a task does have a dependency, don't block inside it; submit the dependent half separately and chain the futures.

2. Actor and command work doesn't belong here.

Player-facing actions go through Actor#runAction (or the queueAction / runAsyncIfFree wrappers), which serialises per actor without holding a worker on this pool.

Plus a note that downstream plugins should use their own threads rather than this pool, which is sized by parallel-threads for FAWE's own cleanup work.

Shape of the change

The full contract lives on the forkJoinPoolSecondary field. The three async(...) overloads get a two-line summary pointing at it, rather than repeating it three times.

Documentation only — no behavioural change, no API change.

The javadoc on QueueHandler#async described what the secondary pool is
for, but not what a task submitted to it is required to avoid. Two rules
were relied on implicitly and are now stated:

- A task must not block waiting on a future that this pool completes.
  ForkJoinPool only compensates for blocking it observes through
  managedBlock, so monitorenter and Future#get leave a worker counted as
  running and no replacement is started. Callers with such a dependency
  should submit the dependent half separately and chain the futures
  rather than blocking inside one task.
- Actor and command work does not belong here; Actor#runAction
  serialises per actor without holding a worker.

Also notes that downstream plugins should use their own threads rather
than this pool, which is sized for FAWE's own cleanup work.

Documentation only; no behavioural change.
@MattBDev
MattBDev requested a review from a team as a code owner August 6, 2026 17:23
Copilot AI lite review requested due to automatic review settings August 6, 2026 17:23

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds clearer documentation for the secondary ForkJoinPool to prevent deadlocks and misuse (especially blocking waits and actor/command work) when submitting “cleanup” tasks.

Changes:

  • Expanded Javadoc on forkJoinPoolSecondary to document a strict submission contract and rationale.
  • Added the same contract summary to the three async(...) overloads that submit to the secondary pool.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +219 to +222
* <p>
* The submitted task must not wait on a {@link Future} completed by this pool, and must not be actor or command work.
* See the {@code forkJoinPoolSecondary} field for the full contract and the reasoning behind it.
* </p>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mm this is probably a good point

Comment on lines +219 to +222
* <p>
* The submitted task must not wait on a {@link Future} completed by this pool, and must not be actor or command work.
* See the {@code forkJoinPoolSecondary} field for the full contract and the reasoning behind it.
* </p>
The full contract lived on the private forkJoinPoolSecondary field,
which most Javadoc builds exclude, so the async(...) overloads'
"See the field" pointer led nowhere for readers of the generated docs.

Move the contract to the public getForkJoinPoolSecondary() accessor
and have the field and the three async(...) overloads link to it with
{@link}/@see instead of an unresolvable textual reference.
Copilot AI review requested due to automatic review settings August 10, 2026 02:22

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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (3)

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java:216

  • Same as the other overloads: this method submits work to the secondary pool and returns a Future immediately, so "Complete a task" is misleading in the Javadoc.
     * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue should be used for "cleanup" tasks that are
     * likely to be shorter in life than those submitted to the primary queue. They may be IO-bound tasks.

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java:232

  • Same wording nit as the other async(...) overloads: this API submits work and returns a Future, it doesn’t "complete" the task synchronously.
     * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue should be used for "cleanup" tasks that are
     * likely to be shorter in life than those submitted to the primary queue. They may be IO-bound tasks.

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java:198

  • The Javadoc says "Complete a task" but these async(...) overloads are submitting work to the secondary pool (returning a Future immediately). Using "Submit"/"Run" here would be more accurate and avoids implying the task is already completed when the method returns.

This issue also appears in the following locations of the same file:

  • line 215
  • line 231
     * Complete a task in the {@code forkJoinPoolSecondary} queue. Secondary queue should be used for "cleanup" tasks that are
     * likely to be shorter in life than those submitted to the primary queue. They may be IO-bound tasks.

@MattBDev
MattBDev enabled auto-merge (squash) August 11, 2026 21:09
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.

3 participants