From 764f2659b7e91e8d0ef37294a226c8d3bc13db9c Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Tue, 29 Sep 2026 13:38:06 +0200 Subject: [PATCH] rugnux: clear the handled allocation error before the GPU merge retries The wait-and-retry in RotationScaleMergeGPU::Impl::Alloc (c644c4e38) caught the failed allocation but left its CUDA error as the thread's last error - CudaDevicePtr leaves it there by contract (CudaDevicePtr_FailedAllocation- LeavesErrorUntilCleared). The retry then succeeded and the next launch check, cudaGetLastError() after ReduceGroupMeansKernel or MergeAccum, reported that stale error over work that went fine, ending the run. Before the retry existed the failure always ended the run, so the leftover never mattered. Reproduced deterministically on a set of 8.2M partials by forcing one merge allocation to fail (first, early or later buffer of the layout) and by making the concurrent probe pass hold extra device memory: every such run failed at the next launch check before, and passes with identical output now. The battery's "resource already mapped" is the same leftover with another code from the failed cudaMalloc. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01D1G8gJVAy6gp1K5Dz3NE5C --- image_analysis/scale_merge/RotationScaleMergeGPU.cu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/image_analysis/scale_merge/RotationScaleMergeGPU.cu b/image_analysis/scale_merge/RotationScaleMergeGPU.cu index 15e3ebf7d..8ed922149 100644 --- a/image_analysis/scale_merge/RotationScaleMergeGPU.cu +++ b/image_analysis/scale_merge/RotationScaleMergeGPU.cu @@ -713,11 +713,15 @@ struct RotationScaleMergeGPU::Impl { // end and is asked for again - the same buffer, so what the merge computes does not depend on the // wait. It is asked for again also when nothing is running beside it any more: work that ended // between the failure and the check has just given its memory back. + // + // The failed allocation is handled here, so its error must not stay behind as this thread's last + // error: the next kernel launch's cudaGetLastError() would report it over work that went fine. template CudaDevicePtr Alloc(size_t n) const { try { return CudaDevicePtr(n, ALLOC); } catch (const JFJochException &) { + cuda_clear_error(); } if (!wait_for_gpu_work_beside(GPU_BUSY_TIMEOUT)) ThrowGPUBusy(n_obs);