mirror of
https://github.com/morluto/rea.git
synced 2026-10-22 05:42:31 -05:00
* fix(windows): preserve caller affinity only within one processor group `spawnProcess` read the caller's affinity mask and applied it to the suspended child unconditionally. A mask names processors relative to one processor group, and Windows assigns each new process to a group of its own choosing, so on hosts with more than 64 logical processors the child is often placed in another group. `SetProcessAffinityMask` then rejects the mask with ERROR_INVALID_PARAMETER (87) and the spawn fails, making every owned provider process start intermittently on such hosts. Affinity is a nicety, not an authority or isolation control, so it is now applied only when the caller and the suspended child both occupy the same single processor group. A child placed in another group keeps that group's default affinity and the spawn proceeds. Job Object membership, kill-on-owner-close policy, owner normalization and priority preservation are unchanged, and the mask failure remains fatal within a shared group, where it is not expected. Single-group hosts, including every host with 64 or fewer logical processors, are unaffected: `GetProcessGroupAffinity` reports one group for both processes and the same mask is applied as before. The optional process-boundary fixture gains an `affinity` observation, and the native verification lane asserts that an owned spawn succeeds and that the child's mask matches the caller's whenever both share a single group. Fixes #2181 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(windows): refuse to widen a pinned caller's affinity across groups Addresses review feedback on the first commit. An unconstrained caller loses nothing when Windows places an owned child in another processor group, so that spawn proceeds with the group's default. A caller deliberately pinned to fewer processors than its group holds is a different case: silently accepting the default would hand the provider tree the whole group, which `docs/installation.md` tells operators to rely on affinity to prevent. Such a spawn now fails naming the constraint instead. `GetProcessAffinityMask` already reports the group's active set, which distinguishes the two without another query. Selecting the caller's group at creation through PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY was tried first and rejected: `CreateProcessW` fails with ERROR_INVALID_PARAMETER on a host with a single processor group, both alongside PROC_THREAD_ATTRIBUTE_JOB_LIST and on its own, so it cannot carry the ordinary path. The boundary fixture's affinity observation now reports a process id's own mask rather than its own, because a separate probe process is assigned its own group and cannot stand in for the process that spawns the child. Masks and the group's active set cross as fixed-width hexadecimal strings: a mask can set bits above the safe-integer range, where `JSON.parse` would round two different masks to one value. The verification lane asserts the mask is inherited exactly when caller and child share a group, and otherwise that the caller was unconstrained. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(windows): record what owned-process affinity does not preserve A mask covering its group's whole active set does not distinguish a caller the system placed in that group from one that selected the group deliberately, so an owned child can still be placed elsewhere with its processor count bounded and its group placement lost. Treating that case as constrained instead is not available: on a multi-group host an ordinary process is assigned a group by the system and its mask is that group's active set, so refusing it would fail every owned spawn there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>