paulk-asert commented on PR #2986:
URL: https://github.com/apache/groovy/pull/2986#issuecomment-6455666379

   Thanks for the rework. It addresses the earlier review: `fixed(n)` and 
`cpu()` are back to plain capped fork-join pools, the stall is documented 
rather than hidden behind compensation, `Pool.virtual()`/`io()` no longer fall 
back to the common pool, and nested calls stay on the current pool. CI is green 
and the branch merges cleanly with master. I read through 
`ParallelExecutorTasks` and found no correctness problems. A few things before 
merge:
   
   **1. `grepParallel`: fix now, or split it off.** There are two problems:
   
   - **Regression:** `[1, null, 2].grepParallel(null)` now throws NPE because 
`require(self, filter)` null-checks the filter. On master it returns `[null]`, 
the same as sequential `grep(null)`. Null-checking only `self` fixes this.
   - **Present before this PR:** it keeps an element unless `isCase` returns 
exactly `Boolean.FALSE`. Sequential `grep` uses Groovy truth (`castToBoolean`). 
With a filter whose `isCase` returns `x % 2`, `grep` gives `[1, 3]` but 
`grepParallel` gives `[1, 2, 3, 4]`. With one whose `isCase` returns `null`, 
`grep` gives `[]` but `grepParallel` returns everything. Closure, Pattern and 
Class filters are unaffected because they return real booleans.
   
   Both are one-line changes to the same method, so I'd fix them here. If you'd 
rather keep this PR focused, split them into a follow-up PR together with item 
2, but that follow-up needs to land before the next release so the regression 
doesn't ship.
   
   **2. `maxParallel` and nulls.** The shared min/max helper passes every 
element to the comparator. Sequential `max(Comparator)` skips nulls, so `[null, 
1].maxParallel(Comparator.naturalOrder())` throws where `[null, 
1].max(Comparator.naturalOrder())` returns `1`. On `[null, null]`, sequential 
returns `null` without calling the comparator, while the PR calls 
`compare(null, null)`. Sequential `min(Comparator)` does pass nulls through, so 
`minParallel` already matches. Sequential is asymmetric here, so either match 
each sequential method exactly or keep the symmetric rule and document how it 
differs from `max`. Either is fine; it just shouldn't diverge silently. This 
goes with item 1 if you split.
   
   **3. Per-element `Pool.withCurrent` rebinding.** Every callback inside a 
`withPool`/`fixed`/`cpu` scope is now wrapped so that nested calls see the 
pool. I emulated the wrapper on master: `Pool.fixed(12)`, 2M elements, a 
trivial `map(i + 1)`. It goes from 2 ms to 31–32 ms on JDK 17/21 and to 14 ms 
on JDK 25. A real `collectParallel { it + 1 }` on master takes 3–4 ms, so for 
light callbacks the wrapper would cost more than the work. A cheaper option 
would be a worker thread factory whose threads carry their owning `Pool`, with 
`Pool.current()` falling back to the current worker's pool. That keeps nested 
calls on the pool without rebinding per element. If you'd rather keep the 
current approach, I'd like to see a JMH number for a light workload first.
   
   **4. Interrupt semantics on the executor path.** When `find`/`any`/`every` 
short-circuits, or a sibling task fails, `ParallelExecutorTasks` interrupts 
callbacks that are still running. The fork-join path never does this. 
Interrupting a thread blocked on an `InterruptibleChannel` (for example a 
`FileChannel`) closes the channel, which users won't expect. Only internal 
Javadoc mentions it; could you add a sentence to 
`core-parallel-collections.adoc`?
   
   **5. Small nit:** `groupInOrder` already inserts in encounter order. If it 
used a `LinkedHashMap` instead of a `HashMap`, the keys would come out in 
first-seen order, like sequential `groupBy`.
   
   **Housekeeping:** the JIRA description still describes the dropped 
compensation design and should be updated to match what the PR does now. We 
also need to decide whether this goes to 6.0.x. If it does, the null-handling 
changes (null matches in `find*`, null elements in `min`/`max`/`sum`, null 
`groupBy` keys) need a release-notes entry.
   
   The rebinding numbers come from emulating the wrapper on master, not from 
running the PR, and the reply says so. If you'd rather not ask for the JMH 
number in item 3, delete that sentence.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to