From 2c6dc3decd2095ec14ef8cbb38c5c3fcc1580492 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Wed, 3 Jun 2026 23:48:55 +0200 Subject: [PATCH] fix: serialize pretty-mode build progress output under OpenMP Each target's pretty-mode (TTY) status update is a compound of two console writes, and console_update_line steps the cursor relative to the tracked line count. Previously the two writes sat in separate unnamed critical regions, so a concurrent worker thread could interleave its own output between them and invalidate the relative cursor math, corrupting the rendered progress. Wrap each target's compound update in a named critical(fpm_progress) so it is atomic against other threads. Plain mode (used off a TTY, in CI) is unchanged. Also make the n_complete counter coherent: increment with atomic capture and read with atomic read, instead of an unnamed critical write paired with an unsynchronized read. --- src/fpm_backend_output.f90 | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/fpm_backend_output.f90 b/src/fpm_backend_output.f90 index 5bad652e66..3611dfa293 100644 --- a/src/fpm_backend_output.f90 +++ b/src/fpm_backend_output.f90 @@ -95,6 +95,7 @@ subroutine output_status_compiling(progress, queue_index) character(:), allocatable :: target_name character(100) :: output_string character(7) :: overall_progress + integer :: n_done associate(target=>progress%target_queue(queue_index)%ptr) @@ -104,7 +105,9 @@ subroutine output_status_compiling(progress, queue_index) target_name = basename(target%output_file) end if - write(overall_progress,'(A,I3,A)') '[',100*progress%n_complete/progress%n_target,'%] ' + !$omp atomic read + n_done = progress%n_complete + write(overall_progress,'(A,I3,A)') '[',100*n_done/progress%n_target,'%] ' if (progress%plain_mode) then ! Plain output @@ -116,9 +119,12 @@ subroutine output_status_compiling(progress, queue_index) write(output_string,'(A,T40,A,A)') target_name, COLOR_YELLOW//'compiling...'//COLOR_RESET + ! Keep a target's two-line update atomic so concurrent threads do + ! not interleave it and corrupt the console's relative cursor math. + !$omp critical(fpm_progress) call progress%console%write_line(trim(output_string),progress%output_lines(queue_index)) - call progress%console%write_line(overall_progress//'Compiling...',advance=.false.) + !$omp end critical(fpm_progress) end if @@ -138,10 +144,12 @@ subroutine output_status_complete(progress, queue_index, build_stat) character(:), allocatable :: target_name character(100) :: output_string character(7) :: overall_progress + integer :: n_done - !$omp critical + !$omp atomic capture progress%n_complete = progress%n_complete + 1 - !$omp end critical + n_done = progress%n_complete + !$omp end atomic associate(target=>progress%target_queue(queue_index)%ptr) @@ -157,7 +165,7 @@ subroutine output_status_complete(progress, queue_index, build_stat) write(output_string,'(A,T40,A,A)') target_name,COLOR_RED//'failed.'//COLOR_RESET end if - write(overall_progress,'(A,I3,A)') '[',100*progress%n_complete/progress%n_target,'%] ' + write(overall_progress,'(A,I3,A)') '[',100*n_done/progress%n_target,'%] ' if (progress%plain_mode) then ! Plain output @@ -167,9 +175,10 @@ subroutine output_status_complete(progress, queue_index, build_stat) else ! Pretty output + !$omp critical(fpm_progress) call progress%console%update_line(progress%output_lines(queue_index),trim(output_string)) - call progress%console%write_line(overall_progress//'Compiling...',advance=.false.) + !$omp end critical(fpm_progress) end if