See attached patch 3 of 3.
Regression tested on x86_64.
OK for mainline?
Regards,
Jerry
---
libgfortran: [PR127347] caf_shmem: fix STAT= for stopped
images
Image control statements and collective subroutines did not follow
F2023 when an image of the team had stopped.
Collective subroutines blocked forever. They now report
STAT_STOPPED_IMAGE, or STAT_FAILED_IMAGE, when the current team
contains a terminated image (F2023 16.6), and a collective already
running gives up with an error instead of blocking.
STAT= was classified with the program-wide counts of stopped and
failed images, so an image that stopped in one team made SYNC ALL in a
sibling team report STAT_STOPPED_IMAGE. It is now classified over the
images involved in the statement, and SYNC TEAM reports them at all.
SYNC ALL and SYNC TEAM with a stopped image still synchronized the
remaining images, although F2023 11.7.11 gives them the effect of SYNC
MEMORY, so a conforming program could deadlock. This needs the team
barrier to decide consistently which images synchronized: its rounds
are now numbered, and a round with an arrival from SYNC ALL, SYNC TEAM
or a collective subroutine is aborted rather than completed once a
stopped image exists. Images waiting in END TEAM, CHANGE TEAM or at
program end take part in the next round instead.
Not addressed: an image that crashes while others wait in the barrier
of a team other than the initial team can still block them, and END
TEAM does not set STAT=.
Assisted-by: Claude Opus 5
PR libfortran/127347
libgfortran/ChangeLog:
* caf/shmem.c (_gfortran_caf_sync_all, _gfortran_caf_sync_team):
Have the effect of SYNC MEMORY with a stopped image. Check the
images of the team synchronized.
(_gfortran_caf_sync_images): Check the images synchronized. Fix
the format of the duplicate image error.
(mark_stopped): Wake the images in a collective subroutine.
(_gfortran_caf_co_broadcast, _gfortran_caf_co_sum)
(_gfortran_caf_co_min, _gfortran_caf_co_max)
(_gfortran_caf_co_reduce): Report terminated images of the team.
* caf/shmem/collective_subroutine.c (collsub_images_terminated): New
function.
(collsub_sync): Give up when an image of the team terminated.
* caf/shmem/counter_barrier.c (counter_barrier_init): Initialize the
new fields.
(next_round): New function.
(counter_barrier_wait_recheck): Add abortable argument. Number the
rounds and abort a round with an abortable arrival when the recheck
reports so.
(counter_barrier_wait): Adjust.
* caf/shmem/counter_barrier.h (counter_barrier): Make
curr_wait_group a round number. Add aborted_round and
abortable_arrivals.
(counter_barrier_wait_recheck): Add abortable argument.
* caf/shmem/supervisor.c (ensure_shmem_initialization): Record the
initial team's collective barrier.
(wake_barrier): New function.
(supervisor_main_loop): Use it for the initial team's barriers.
* caf/shmem/supervisor.h (supervisor): Add
initial_team_collsub_barrier.
* caf/shmem/sync.c (sync_all): Return whether the images were
synchronized.
(sync_team): Take part in the next round when a round is aborted.
(sync_team_unless_stopped): New function.
* caf/shmem/sync.h (sync_all): Adjust.
(sync_team_unless_stopped): Declare.
* caf/shmem/teams_mgmt.c (count_images, team_terminated_images)
(update_and_check_stopped): New functions.
(update_teams_images_locked): Take the team.
(update_teams_images): Wake the images waiting in the barrier.
(check_health): Classify the given images. Return the stat value.
* caf/shmem/teams_mgmt.h (team_terminated_images)
(update_and_check_stopped): Declare.
(update_teams_images_locked, check_health): Adjust.
(TEAM_HEALTH_CHECK): New macro.
(HEALTH_CHECK): Use it.
gcc/testsuite/ChangeLog:
* gfortran.dg/coarray/stop_sync_1.f90: Check STAT=.
* gfortran.dg/coarray/collective_stopped_1.f90: New test.
* gfortran.dg/coarray/stop_end_team_1.f90: New test.
* gfortran.dg/coarray/stop_sync_team_1.f90: New test.
* gfortran.dg/coarray/sync_memory_stopped_1.f90: New test.
---From 676adab87ad813b86d487b00c868bb8924852c4e Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Fri, 28 Aug 2026 17:05:15 -0700
Subject: [PATCH 3/3] libgfortran: [PR127347] caf_shmem: fix STAT= for stopped
images
Image control statements and collective subroutines did not follow
F2023 when an image of the team had stopped.
Collective subroutines blocked forever. They now report
STAT_STOPPED_IMAGE, or STAT_FAILED_IMAGE, when the current team
contains a terminated image (F2023 16.6), and a collective already
running gives up with an error instead of blocking.
STAT= was classified with the program-wide counts of stopped and
failed images, so an image that stopped in one team made SYNC ALL in a
sibling team report STAT_STOPPED_IMAGE. It is now classified over the
images involved in the statement, and SYNC TEAM reports them at all.
SYNC ALL and SYNC TEAM with a stopped image still synchronized the
remaining images, although F2023 11.7.11 gives them the effect of SYNC
MEMORY, so a conforming program could deadlock. This needs the team
barrier to decide consistently which images synchronized: its rounds
are now numbered, and a round with an arrival from SYNC ALL, SYNC TEAM
or a collective subroutine is aborted rather than completed once a
stopped image exists. Images waiting in END TEAM, CHANGE TEAM or at
program end take part in the next round instead.
Not addressed: an image that crashes while others wait in the barrier
of a team other than the initial team can still block them, and END
TEAM does not set STAT=.
Assisted-by: Claude Opus 5
PR libfortran/127347
libgfortran/ChangeLog:
* caf/shmem.c (_gfortran_caf_sync_all, _gfortran_caf_sync_team):
Have the effect of SYNC MEMORY with a stopped image. Check the
images of the team synchronized.
(_gfortran_caf_sync_images): Check the images synchronized. Fix
the format of the duplicate image error.
(mark_stopped): Wake the images in a collective subroutine.
(_gfortran_caf_co_broadcast, _gfortran_caf_co_sum)
(_gfortran_caf_co_min, _gfortran_caf_co_max)
(_gfortran_caf_co_reduce): Report terminated images of the team.
* caf/shmem/collective_subroutine.c (collsub_images_terminated): New
function.
(collsub_sync): Give up when an image of the team terminated.
* caf/shmem/counter_barrier.c (counter_barrier_init): Initialize the
new fields.
(next_round): New function.
(counter_barrier_wait_recheck): Add abortable argument. Number the
rounds and abort a round with an abortable arrival when the recheck
reports so.
(counter_barrier_wait): Adjust.
* caf/shmem/counter_barrier.h (counter_barrier): Make
curr_wait_group a round number. Add aborted_round and
abortable_arrivals.
(counter_barrier_wait_recheck): Add abortable argument.
* caf/shmem/supervisor.c (ensure_shmem_initialization): Record the
initial team's collective barrier.
(wake_barrier): New function.
(supervisor_main_loop): Use it for the initial team's barriers.
* caf/shmem/supervisor.h (supervisor): Add
initial_team_collsub_barrier.
* caf/shmem/sync.c (sync_all): Return whether the images were
synchronized.
(sync_team): Take part in the next round when a round is aborted.
(sync_team_unless_stopped): New function.
* caf/shmem/sync.h (sync_all): Adjust.
(sync_team_unless_stopped): Declare.
* caf/shmem/teams_mgmt.c (count_images, team_terminated_images)
(update_and_check_stopped): New functions.
(update_teams_images_locked): Take the team.
(update_teams_images): Wake the images waiting in the barrier.
(check_health): Classify the given images. Return the stat value.
* caf/shmem/teams_mgmt.h (team_terminated_images)
(update_and_check_stopped): Declare.
(update_teams_images_locked, check_health): Adjust.
(TEAM_HEALTH_CHECK): New macro.
(HEALTH_CHECK): Use it.
gcc/testsuite/ChangeLog:
* gfortran.dg/coarray/stop_sync_1.f90: Check STAT=.
* gfortran.dg/coarray/collective_stopped_1.f90: New test.
* gfortran.dg/coarray/stop_end_team_1.f90: New test.
* gfortran.dg/coarray/stop_sync_team_1.f90: New test.
* gfortran.dg/coarray/sync_memory_stopped_1.f90: New test.
---
.../coarray/collective_stopped_1.f90 | 33 ++++++
.../gfortran.dg/coarray/stop_end_team_1.f90 | 28 +++++
.../gfortran.dg/coarray/stop_sync_1.f90 | 4 +-
.../gfortran.dg/coarray/stop_sync_team_1.f90 | 32 ++++++
.../coarray/sync_memory_stopped_1.f90 | 25 +++++
libgfortran/caf/shmem.c | 39 ++++++-
libgfortran/caf/shmem/collective_subroutine.c | 22 +++-
libgfortran/caf/shmem/counter_barrier.c | 71 ++++++++----
libgfortran/caf/shmem/counter_barrier.h | 29 +++--
libgfortran/caf/shmem/supervisor.c | 39 +++++--
libgfortran/caf/shmem/supervisor.h | 5 +-
libgfortran/caf/shmem/sync.c | 19 +++-
libgfortran/caf/shmem/sync.h | 9 +-
libgfortran/caf/shmem/teams_mgmt.c | 104 ++++++++++++------
libgfortran/caf/shmem/teams_mgmt.h | 30 ++++-
15 files changed, 392 insertions(+), 97 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/coarray/collective_stopped_1.f90
create mode 100644 gcc/testsuite/gfortran.dg/coarray/stop_end_team_1.f90
create mode 100644 gcc/testsuite/gfortran.dg/coarray/stop_sync_team_1.f90
create mode 100644 gcc/testsuite/gfortran.dg/coarray/sync_memory_stopped_1.f90
diff --git a/gcc/testsuite/gfortran.dg/coarray/collective_stopped_1.f90 b/gcc/testsuite/gfortran.dg/coarray/collective_stopped_1.f90
new file mode 100644
index 00000000000..b1eae226279
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/coarray/collective_stopped_1.f90
@@ -0,0 +1,33 @@
+! { dg-do run }
+!
+! A collective subroutine has to report an image that terminated normally
+! through stat= instead of blocking forever.
+
+program collective_stopped_1
+ use iso_fortran_env, only : stat_stopped_image
+ implicit none
+ integer :: st, val
+
+ sync all
+ if (num_images () > 1 .and. this_image () == num_images ()) stop
+
+ st = 0
+ sync all (stat=st)
+
+ val = this_image ()
+ st = 0
+ call co_sum (val, stat=st)
+ if (num_images () > 1) then
+ if (st /= stat_stopped_image) error stop "co_sum missed the stopped image"
+ else
+ if (st /= 0) error stop "co_sum failed"
+ end if
+
+ st = 0
+ call co_broadcast (val, 1, stat=st)
+ if (num_images () > 1) then
+ if (st /= stat_stopped_image) error stop "co_broadcast missed the stopped image"
+ else
+ if (st /= 0) error stop "co_broadcast failed"
+ end if
+end program collective_stopped_1
diff --git a/gcc/testsuite/gfortran.dg/coarray/stop_end_team_1.f90 b/gcc/testsuite/gfortran.dg/coarray/stop_end_team_1.f90
new file mode 100644
index 00000000000..79e7d246f57
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/coarray/stop_end_team_1.f90
@@ -0,0 +1,28 @@
+! { dg-do run }
+! { dg-skip-if "CHANGE TEAM needs a coarray library" { *-*-* } { "-fcoarray=single" } { "" } }
+!
+! In a team with a stopped image, SYNC ALL (STAT=) on some images must not
+! pair with END TEAM on the others.
+
+program stop_end_team_1
+ use iso_fortran_env, only : team_type, stat_stopped_image
+ implicit none
+ type(team_type) :: t
+ integer :: me, st, k
+
+ me = 2 - mod (this_image (), 2)
+ form team (me, t)
+ change team (t)
+ sync all
+ if (me == 1 .and. num_images () > 1 .and. this_image () == num_images ()) stop
+ if (me == 1 .and. mod (this_image (), 2) == 0) then
+ do k = 1, 3
+ sync all (stat=st)
+ if (st /= 0 .and. st /= stat_stopped_image) error stop 1
+ end do
+ end if
+ end team
+ st = -1
+ sync all (stat=st)
+ if (num_images () > 2 .and. st /= stat_stopped_image) error stop 2
+end program stop_end_team_1
diff --git a/gcc/testsuite/gfortran.dg/coarray/stop_sync_1.f90 b/gcc/testsuite/gfortran.dg/coarray/stop_sync_1.f90
index cb41610aaf9..ee2d8426ba4 100644
--- a/gcc/testsuite/gfortran.dg/coarray/stop_sync_1.f90
+++ b/gcc/testsuite/gfortran.dg/coarray/stop_sync_1.f90
@@ -1,9 +1,10 @@
! { dg-do run }
!
! A normal STOP on one image must not block the surviving images in the
-! SYNC ALL statements that follow.
+! SYNC ALL statements that follow, which report STAT_STOPPED_IMAGE.
program stop_sync_1
+ use iso_fortran_env, only : stat_stopped_image
implicit none
integer :: i, st
@@ -14,4 +15,5 @@ program stop_sync_1
st = 0
sync all (stat=st)
end do
+ if (num_images () > 1 .and. st /= stat_stopped_image) error stop 1
end program stop_sync_1
diff --git a/gcc/testsuite/gfortran.dg/coarray/stop_sync_team_1.f90 b/gcc/testsuite/gfortran.dg/coarray/stop_sync_team_1.f90
new file mode 100644
index 00000000000..9eba00e7b10
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/coarray/stop_sync_team_1.f90
@@ -0,0 +1,32 @@
+! { dg-do run }
+! { dg-skip-if "CHANGE TEAM needs a coarray library" { *-*-* } { "-fcoarray=single" } { "" } }
+!
+! An image stopping in one team is a stopped image for SYNC ALL and SYNC
+! TEAM in that team only, not in its sibling team.
+
+program stop_sync_team_1
+ use iso_fortran_env, only : team_type, stat_stopped_image
+ implicit none
+ type(team_type) :: t
+ integer :: i, st, me
+
+ if (num_images () < 4) stop
+ me = 2 - mod (this_image (), 2)
+ form team (me, t)
+ change team (t)
+ sync all
+ if (me == 1 .and. this_image () == num_images ()) stop
+
+ do i = 1, 5
+ st = -1
+ sync all (stat=st)
+ if (me == 2 .and. st /= 0) error stop 1
+ end do
+ if (me == 1 .and. st /= stat_stopped_image) error stop 2
+
+ st = -1
+ sync team (get_team (), stat=st)
+ if (me == 2 .and. st /= 0) error stop 3
+ if (me == 1 .and. st /= stat_stopped_image) error stop 4
+ end team
+end program stop_sync_team_1
diff --git a/gcc/testsuite/gfortran.dg/coarray/sync_memory_stopped_1.f90 b/gcc/testsuite/gfortran.dg/coarray/sync_memory_stopped_1.f90
new file mode 100644
index 00000000000..dd400fd5911
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/coarray/sync_memory_stopped_1.f90
@@ -0,0 +1,25 @@
+! { dg-do run }
+!
+! SYNC ALL (STAT=) with a stopped image in the team has the effect of
+! SYNC MEMORY: it must not wait for the other images (F2023 11.7.11).
+
+program sync_memory_stopped_1
+ use iso_fortran_env, only : stat_stopped_image
+ implicit none
+ integer :: s
+
+ if (num_images () < 3) stop
+ sync all
+ if (this_image () == 3) stop
+ do while (image_status (3) /= stat_stopped_image)
+ end do
+ s = -1
+ if (this_image () == 1) then
+ sync all (stat=s)
+ sync images (2)
+ else if (this_image () == 2) then
+ sync images (1)
+ sync all (stat=s)
+ end if
+ if (this_image () <= 2 .and. s /= stat_stopped_image) error stop 1
+end program sync_memory_stopped_1
diff --git a/libgfortran/caf/shmem.c b/libgfortran/caf/shmem.c
index 5c92c13d328..dbb96f4a2c3 100644
--- a/libgfortran/caf/shmem.c
+++ b/libgfortran/caf/shmem.c
@@ -483,7 +483,9 @@ _gfortran_caf_sync_all (int *stat, char *errmsg, size_t errmsg_len)
__asm__ __volatile__ ("":::"memory");
HEALTH_CHECK (stat, errmsg, errmsg_len);
CHECK_TEAM_INTEGRITY (caf_current_team);
- sync_all ();
+ /* With a stopped image, SYNC ALL only has the effect of SYNC MEMORY. */
+ if (!sync_all ())
+ HEALTH_CHECK (stat, errmsg, errmsg_len);
}
@@ -543,7 +545,7 @@ _gfortran_caf_sync_images (int count, int images[], int *stat, char *errmsg,
if (mapped_images[c] == mapped_images[i])
{
caf_internal_error ("SYNC IMAGES: Duplicate image %d in "
- "images at position %d and &d.",
+ "images at position %d and %d.",
stat, errmsg, errmsg_len, images[c],
i + 1, c + 1);
/* There is no official error code for this, but 3 is what
@@ -565,7 +567,10 @@ _gfortran_caf_sync_images (int count, int images[], int *stat, char *errmsg,
__asm__ __volatile__ ("" ::: "memory");
sync_table (&local->si, mapped_images, count);
- HEALTH_CHECK (stat, errmsg, errmsg_len);
+ if (count > 0)
+ check_health (mapped_images, count, stat, errmsg, errmsg_len);
+ else
+ HEALTH_CHECK (stat, errmsg, errmsg_len);
}
extern void _gfortran_report_exception (void);
@@ -587,9 +592,13 @@ mark_stopped (void)
atomic_fetch_add (&this_image.supervisor->finished_images, 1);
}
- /* Drop this image from the barriers of the teams it is a member of. */
+ /* Drop this image from the barriers of the teams it is a member of and wake
+ the images waiting in a collective subroutine. */
for (caf_shmem_team_t t = caf_current_team; t; t = t->parent)
- update_teams_images (t);
+ {
+ update_teams_images (t);
+ caf_shmem_cond_broadcast (&t->u.image_info->collsub.barrier.cond);
+ }
}
/* Tell the supervisor that this image error stopped, so that it can terminate
@@ -850,6 +859,9 @@ _gfortran_caf_co_broadcast (gfc_descriptor_t *desc, int source_image, int *stat,
if (stat)
*stat = 0;
+ if (HEALTH_CHECK (stat, errmsg, errmsg_len))
+ return;
+
if (!check_map_team (&mapped_index, &this_image_index, source_image, NULL,
NULL, stat))
return;
@@ -945,6 +957,9 @@ _gfortran_caf_co_sum (gfc_descriptor_t *desc, int result_image, int *stat,
if (stat)
*stat = 0;
+ if (HEALTH_CHECK (stat, errmsg, errmsg_len))
+ return;
+
/* If result_image == 0 then allreduce is wanted, i.e. mapped_index = -1. */
if (result_image
&& !check_map_team (&mapped_index, &this_image_index, result_image, NULL,
@@ -967,6 +982,9 @@ _gfortran_caf_co_min (gfc_descriptor_t *desc, int result_image, int *stat,
if (stat)
*stat = 0;
+
+ if (HEALTH_CHECK (stat, errmsg, errmsg_len))
+ return;
/* If result_image == 0 then allreduce is wanted, i.e. mapped_index = -1. */
if (result_image
&& !check_map_team (&mapped_index, &this_image_index, result_image, NULL,
@@ -989,6 +1007,9 @@ _gfortran_caf_co_max (gfc_descriptor_t *desc, int result_image, int *stat,
if (stat)
*stat = 0;
+
+ if (HEALTH_CHECK (stat, errmsg, errmsg_len))
+ return;
/* If result_image == 0 then allreduce is wanted, i.e. mapped_index = -1. */
if (result_image
&& !check_map_team (&mapped_index, &this_image_index, result_image, NULL,
@@ -1011,6 +1032,9 @@ _gfortran_caf_co_reduce (gfc_descriptor_t *desc, void *(*opr) (void *, void *),
if (stat)
*stat = 0;
+ if (HEALTH_CHECK (stat, errmsg, errmsg_len))
+ return;
+
/* If result_image == 0 then allreduce is wanted, i.e. mapped_index = -1. */
if (result_image
&& !check_map_team (&mapped_index, &this_image_index, result_image, NULL,
@@ -1939,7 +1963,10 @@ _gfortran_caf_sync_team (caf_team_t team, int *stat, char *errmsg,
return;
}
- sync_team (team_to_sync);
+ TEAM_HEALTH_CHECK (team_to_sync, stat, errmsg, errmsg_len);
+ /* With a stopped image, SYNC TEAM only has the effect of SYNC MEMORY. */
+ if (!sync_team_unless_stopped (team_to_sync))
+ TEAM_HEALTH_CHECK (team_to_sync, stat, errmsg, errmsg_len);
}
int
diff --git a/libgfortran/caf/shmem/collective_subroutine.c b/libgfortran/caf/shmem/collective_subroutine.c
index 01389b18b0a..412bcfa5dbc 100644
--- a/libgfortran/caf/shmem/collective_subroutine.c
+++ b/libgfortran/caf/shmem/collective_subroutine.c
@@ -22,6 +22,7 @@ a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
+#include "../caf_error.h"
#include "collective_subroutine.h"
#include "supervisor.h"
#include "teams_mgmt.h"
@@ -218,13 +219,32 @@ get_collsub_buf (size_t size)
return ret;
}
+/* Give up on the collective's barrier as soon as an image of the team has
+ terminated. The reduction is laid out over a fixed set of images, so it
+ cannot be completed with fewer of them. */
+
+static int
+collsub_images_terminated (void *team)
+{
+ if (!this_image.supervisor->finished_images
+ && !this_image.supervisor->failed_images)
+ return 0;
+
+ return team_terminated_images ((caf_shmem_team_t) team) != 0;
+}
+
/* This function syncs all images with one another. It will only return once
all images have called it. */
static void
collsub_sync (void)
{
- counter_barrier_wait (&caf_current_team->u.image_info->collsub.barrier);
+ counter_barrier *barrier = &caf_current_team->u.image_info->collsub.barrier;
+
+ if (!counter_barrier_wait_recheck (barrier, collsub_images_terminated,
+ caf_current_team, true))
+ caf_runtime_error ("Image terminated while executing a collective "
+ "subroutine");
}
typedef void *(*red_op) (void *, void *);
diff --git a/libgfortran/caf/shmem/counter_barrier.c b/libgfortran/caf/shmem/counter_barrier.c
index 0e583eecb98..23935d7344a 100644
--- a/libgfortran/caf/shmem/counter_barrier.c
+++ b/libgfortran/caf/shmem/counter_barrier.c
@@ -48,50 +48,73 @@ unlock_counter_barrier (counter_barrier *b)
void
counter_barrier_init (counter_barrier *b, int val)
{
- *b = (counter_barrier) {CAF_SHMEM_MUTEX_INITIALIZER,
- CAF_SHMEM_COND_INITIALIZER, val, 0, val};
+ *b = (counter_barrier) {.mutex = CAF_SHMEM_MUTEX_INITIALIZER,
+ .cond = CAF_SHMEM_COND_INITIALIZER,
+ .wait_count = val,
+ .curr_wait_group = 1,
+ .aborted_round = 0,
+ .abortable_arrivals = 0,
+ .count = val};
initialize_shared_condition (&b->cond, val);
initialize_shared_mutex (&b->mutex);
}
-void
+/* Start the next round of the barrier and wake the images waiting in the
+ current one. */
+
+static void
+next_round (counter_barrier *b, bool abort)
+{
+ if (abort)
+ b->aborted_round = b->curr_wait_group;
+ ++b->curr_wait_group;
+ b->wait_count = b->count;
+ b->abortable_arrivals = 0;
+ caf_shmem_cond_broadcast (&b->cond);
+}
+
+bool
counter_barrier_wait_recheck (counter_barrier *b,
- counter_barrier_recheck recheck, void *data)
+ counter_barrier_recheck recheck, void *data,
+ bool abortable)
{
- int wait_group_beginning;
+ uint64_t round;
+ bool completed;
lock_counter_barrier (b);
- /* Re-check before blocking, so that a terminating image that adjusted the
- count before this image took the lock is not waited for. */
- if (recheck)
- recheck (data);
- wait_group_beginning = b->curr_wait_group;
-
- if ((--b->wait_count) <= 0)
- caf_shmem_cond_broadcast (&b->cond);
- else
+ round = b->curr_wait_group;
+ if (recheck && recheck (data) && abortable)
{
- while (b->wait_count > 0 && b->curr_wait_group == wait_group_beginning)
- {
- caf_shmem_cond_wait (&b->cond, &b->mutex);
- if (recheck)
- recheck (data);
- }
+ if (b->abortable_arrivals)
+ next_round (b, true);
+ unlock_counter_barrier (b);
+ return false;
}
- if (b->wait_count <= 0)
+ if (abortable)
+ ++b->abortable_arrivals;
+ --b->wait_count;
+ while (b->wait_count > 0 && b->curr_wait_group == round)
{
- b->curr_wait_group = !wait_group_beginning;
- b->wait_count = b->count;
+ caf_shmem_cond_wait (&b->cond, &b->mutex);
+ if (b->curr_wait_group == round && b->abortable_arrivals && recheck
+ && recheck (data))
+ next_round (b, true);
}
+ /* The last image to arrive ends the round. */
+ if (b->curr_wait_group == round)
+ next_round (b, b->abortable_arrivals && recheck && recheck (data));
+
+ completed = b->aborted_round != round;
unlock_counter_barrier (b);
+ return completed;
}
void
counter_barrier_wait (counter_barrier *b)
{
- counter_barrier_wait_recheck (b, NULL, NULL);
+ counter_barrier_wait_recheck (b, NULL, NULL, false);
}
static inline void
diff --git a/libgfortran/caf/shmem/counter_barrier.h b/libgfortran/caf/shmem/counter_barrier.h
index 75cf3e2c41c..ac83c9de4fc 100644
--- a/libgfortran/caf/shmem/counter_barrier.h
+++ b/libgfortran/caf/shmem/counter_barrier.h
@@ -27,6 +27,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#include "thread_support.h"
+#include <stdbool.h>
+#include <stdint.h>
+
/* Usable as counter barrier and as waitable counter.
This "class" allows to sync all images acting as a barrier. For this the
counter_barrier is to be initialized by the number of images and then later
@@ -44,7 +47,13 @@ typedef struct
caf_shmem_mutex mutex;
caf_shmem_condvar cond;
volatile int wait_count;
- volatile int curr_wait_group;
+ /* The number of the current round of the barrier, and of the last round
+ that was aborted. */
+ volatile uint64_t curr_wait_group;
+ volatile uint64_t aborted_round;
+ /* The number of images in the current round that asked for it to be
+ aborted when RECHECK reports so. */
+ volatile int abortable_arrivals;
volatile int count;
} counter_barrier;
@@ -73,19 +82,23 @@ void counter_barrier_init_add (counter_barrier *, int);
int counter_barrier_get_count (counter_barrier *);
-/* Called with the barrier's mutex held every time a waiting image wakes up.
- It allows the caller to adjust the barrier's count, e.g. when images have
- terminated in the meantime. */
+/* Called with the barrier's mutex held before an image blocks, every time a
+ waiting image wakes up and before a round completes. It allows the caller
+ to adjust the barrier's count, e.g. when images have terminated in the
+ meantime. Returns non-zero when a round with an abortable arrival is to be
+ aborted instead of completed. */
-typedef void (*counter_barrier_recheck) (void *);
+typedef int (*counter_barrier_recheck) (void *);
/* Wait for the count in the barrier drop to or below 0. */
void counter_barrier_wait (counter_barrier *);
-/* Like counter_barrier_wait, but call RECHECK with DATA on every wake up. */
+/* Like counter_barrier_wait, but call RECHECK with DATA. An ABORTABLE
+ arrival does not wait when RECHECK reports so, and the round it is in is
+ aborted for all its images. Returns false, when the round was aborted. */
-void counter_barrier_wait_recheck (counter_barrier *, counter_barrier_recheck,
- void *);
+bool counter_barrier_wait_recheck (counter_barrier *, counter_barrier_recheck,
+ void *, bool abortable);
#endif
diff --git a/libgfortran/caf/shmem/supervisor.c b/libgfortran/caf/shmem/supervisor.c
index c5b6209b800..c5022f3380a 100644
--- a/libgfortran/caf/shmem/supervisor.c
+++ b/libgfortran/caf/shmem/supervisor.c
@@ -236,6 +236,9 @@ ensure_shmem_initialization (void)
collsub_init_supervisor (&caf_initial_team->u.image_info->collsub,
alloc_get_allocator (&local->ai),
local->total_num_images);
+ this_image.supervisor->initial_team_collsub_barrier = AS_SHMPTR (
+ (void *) &caf_initial_team->u.image_info->collsub.barrier,
+ local->sm);
caf_initial_team->u.image_info->team_parent_id = 0;
caf_initial_team->u.image_info->team_id = -1;
caf_initial_team->u.image_info->image_map_size
@@ -256,6 +259,7 @@ ensure_shmem_initialization (void)
counter_barrier_init (&this_image.supervisor->num_active_images,
local->total_num_images);
this_image.supervisor->initial_team_barrier = SHMPTR_NULL;
+ this_image.supervisor->initial_team_collsub_barrier = SHMPTR_NULL;
alloc_init_supervisor (&local->ai, &local->sm);
sync_init_supervisor (&local->si, &local->ai);
}
@@ -303,6 +307,24 @@ startWorker (image *im __attribute__ ((unused)),
return true;
}
+/* Wake the images waiting in the given barrier. Taking the lock ensures that
+ an image about to block sees the status of the terminated image instead of
+ the wake up. */
+
+static void
+wake_barrier (shared_mem_ptr barrier)
+{
+ counter_barrier *b;
+
+ if (SHMPTR_IS_NULL (barrier))
+ return;
+
+ b = SHMPTR_AS (counter_barrier *, barrier, &local->sm);
+ caf_shmem_mutex_lock (&b->mutex);
+ caf_shmem_cond_broadcast (&b->cond);
+ caf_shmem_mutex_unlock (&b->mutex);
+}
+
#ifdef HAVE_FORK
static void
kill_all_images (supervisor *m)
@@ -494,18 +516,11 @@ supervisor_main_loop (int *argc __attribute__ ((unused)),
caf_shmem_cond_signal (&SHMPTR_AS (caf_shmem_condvar *,
m->sync_shared.sync_images_cond_vars,
&local->sm)[j]);
- /* Trigger images waiting in the initial team's barrier, e.g. in
- SYNC ALL, to re-evaluate the number of participating images. */
- if (!SHMPTR_IS_NULL (m->initial_team_barrier))
- {
- counter_barrier *b = SHMPTR_AS (counter_barrier *,
- m->initial_team_barrier, &local->sm);
- /* Under the lock, so that an image about to block sees the status
- of the terminated image instead of the wake up. */
- caf_shmem_mutex_lock (&b->mutex);
- caf_shmem_cond_broadcast (&b->cond);
- caf_shmem_mutex_unlock (&b->mutex);
- }
+ /* Trigger images waiting in the initial team's barriers, e.g. in
+ SYNC ALL or in a collective subroutine, to re-evaluate the images
+ still participating. */
+ wake_barrier (m->initial_team_barrier);
+ wake_barrier (m->initial_team_collsub_barrier);
counter_barrier_add (&m->num_active_images, -1);
#elif defined(WIN32)
DWORD res = WaitForMultipleObjects (count_waiting, waiting_handles, FALSE,
diff --git a/libgfortran/caf/shmem/supervisor.h b/libgfortran/caf/shmem/supervisor.h
index f859bd41256..82c9204f3c3 100644
--- a/libgfortran/caf/shmem/supervisor.h
+++ b/libgfortran/caf/shmem/supervisor.h
@@ -62,9 +62,10 @@ typedef struct supervisor
atomic_int failed_images;
atomic_int finished_images;
counter_barrier num_active_images;
- /* The initial team's image_count barrier, used by the supervisor to wake
- images waiting in it when an image has terminated. */
+ /* The initial team's barriers, used by the supervisor to wake images waiting
+ in them when an image has terminated. */
shared_mem_ptr initial_team_barrier;
+ shared_mem_ptr initial_team_collsub_barrier;
caf_shmem_mutex image_tracker_lock;
#ifdef WIN32
size_t global_used_handles;
diff --git a/libgfortran/caf/shmem/sync.c b/libgfortran/caf/shmem/sync.c
index 3d03cd67152..ef63de528e8 100644
--- a/libgfortran/caf/shmem/sync.c
+++ b/libgfortran/caf/shmem/sync.c
@@ -147,19 +147,28 @@ sync_table (sync_t *si, int *images, int size)
unlock_table (si);
}
-void
+bool
sync_all (void)
{
- sync_team (caf_current_team);
+ return sync_team_unless_stopped (caf_current_team);
}
void
sync_team (caf_shmem_team_t team)
{
/* Re-evaluate the number of participating images on every wake up, so that
- images terminating while this image waits do not block it forever. */
- counter_barrier_wait_recheck (&team->u.image_info->image_count,
- update_teams_images_locked, team);
+ images terminating while this image waits do not block it forever. Take
+ part in the next round when another image aborted this one. */
+ while (!counter_barrier_wait_recheck (&team->u.image_info->image_count,
+ update_and_check_stopped, team, false))
+ ;
+}
+
+bool
+sync_team_unless_stopped (caf_shmem_team_t team)
+{
+ return counter_barrier_wait_recheck (&team->u.image_info->image_count,
+ update_and_check_stopped, team, true);
}
void
diff --git a/libgfortran/caf/shmem/sync.h b/libgfortran/caf/shmem/sync.h
index 6f8b28d378a..0aa596c9eff 100644
--- a/libgfortran/caf/shmem/sync.h
+++ b/libgfortran/caf/shmem/sync.h
@@ -51,7 +51,10 @@ void sync_init (sync_t *, shared_memory);
void sync_init_supervisor (sync_t *, alloc *);
-void sync_all (void);
+/* Synchronize the images of the current team. Returns false without
+ synchronizing when a stopped image is a member of the team. */
+
+bool sync_all (void);
/* Prototype for circular dependency break. */
@@ -60,6 +63,10 @@ typedef struct caf_shmem_team *caf_shmem_team_t;
void sync_team (caf_shmem_team_t team);
+/* Like sync_all for TEAM. */
+
+bool sync_team_unless_stopped (caf_shmem_team_t team);
+
void sync_table (sync_t *, int *, int);
void lock_alloc_lock (sync_t *);
diff --git a/libgfortran/caf/shmem/teams_mgmt.c b/libgfortran/caf/shmem/teams_mgmt.c
index 0e67a7532b5..9593e3cdfa0 100644
--- a/libgfortran/caf/shmem/teams_mgmt.c
+++ b/libgfortran/caf/shmem/teams_mgmt.c
@@ -28,26 +28,44 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
caf_shmem_team_t caf_current_team = NULL, caf_initial_team;
caf_shmem_team_t caf_teams_formed = NULL;
-void
-update_teams_images_locked (void *data)
+/* Count the images among the COUNT images in MAP that have status STATUS. */
+
+static int
+count_images (const int *map, int count, image_status status)
{
- caf_shmem_team_t team = (caf_shmem_team_t) data;
+ int i, n = 0;
+
+ for (i = 0; i < count; ++i)
+ if (this_image.supervisor->images[map[i]].status == status)
+ ++n;
+
+ return n;
+}
+
+int
+team_terminated_images (caf_shmem_team_t team)
+{
+ const int sz = team->u.image_info->image_map_size;
+ int i, term = 0;
+
+ for (i = 0; i < sz; ++i)
+ if (this_image.supervisor->images[team->u.image_info->image_map[i]].status
+ != IMAGE_OK)
+ ++term;
+
+ return term;
+}
+void
+update_teams_images_locked (caf_shmem_team_t team)
+{
if (team->u.image_info->num_term_images
!= this_image.supervisor->finished_images
+ this_image.supervisor->failed_images)
{
const int old_num = team->u.image_info->num_term_images;
- const int sz = team->u.image_info->image_map_size;
- int i, good = 0;
-
- for (i = 0; i < sz; ++i)
- if (this_image.supervisor->images[team->u.image_info->image_map[i]]
- .status
- == IMAGE_OK)
- ++good;
- team->u.image_info->num_term_images = sz - good;
+ team->u.image_info->num_term_images = team_terminated_images (team);
counter_barrier_add_locked (&team->u.image_info->image_count,
old_num
@@ -55,37 +73,57 @@ update_teams_images_locked (void *data)
}
}
+int
+update_and_check_stopped (void *data)
+{
+ caf_shmem_team_t team = (caf_shmem_team_t) data;
+
+ update_teams_images_locked (team);
+ return this_image.supervisor->finished_images
+ && count_images (team->u.image_info->image_map,
+ team->u.image_info->image_map_size, IMAGE_SUCCESS);
+}
+
void
update_teams_images (caf_shmem_team_t team)
{
caf_shmem_mutex_lock (&team->u.image_info->image_count.mutex);
update_teams_images_locked (team);
+ /* Wake the images waiting in the team's barrier to re-check it. */
+ caf_shmem_cond_broadcast (&team->u.image_info->image_count.cond);
caf_shmem_mutex_unlock (&team->u.image_info->image_count.mutex);
}
-void
-check_health (int *stat, char *errmsg, size_t errmsg_len)
+int
+check_health (const int *map, int count, int *stat, char *errmsg,
+ size_t errmsg_len)
{
- if (this_image.supervisor->finished_images
- || this_image.supervisor->failed_images)
+ int stopped = 0, failed = 0;
+
+ if (this_image.supervisor->finished_images)
+ stopped = count_images (map, count, IMAGE_SUCCESS);
+ if (this_image.supervisor->failed_images)
+ failed = count_images (map, count, IMAGE_FAILED);
+
+ if (stopped)
+ {
+ caf_internal_error ("Stopped images present (currently %d)", stat,
+ errmsg, errmsg_len, stopped);
+ if (stat)
+ *stat = CAF_STAT_STOPPED_IMAGE;
+ return CAF_STAT_STOPPED_IMAGE;
+ }
+
+ if (failed)
{
- if (this_image.supervisor->finished_images)
- {
- caf_internal_error ("Stopped images present (currently %d)", stat,
- errmsg, errmsg_len,
- this_image.supervisor->finished_images);
- if (stat)
- *stat = CAF_STAT_STOPPED_IMAGE;
- }
- else if (this_image.supervisor->failed_images)
- {
- caf_internal_error ("Failed images present (currently %d)", stat,
- errmsg, errmsg_len,
- this_image.supervisor->failed_images);
- if (stat)
- *stat = CAF_STAT_FAILED_IMAGE;
- }
+ caf_internal_error ("Failed images present (currently %d)", stat,
+ errmsg, errmsg_len, failed);
+ if (stat)
+ *stat = CAF_STAT_FAILED_IMAGE;
+ return CAF_STAT_FAILED_IMAGE;
}
- else if (stat)
+
+ if (stat)
*stat = 0;
+ return 0;
}
diff --git a/libgfortran/caf/shmem/teams_mgmt.h b/libgfortran/caf/shmem/teams_mgmt.h
index 415ecda2cb8..9f84e420838 100644
--- a/libgfortran/caf/shmem/teams_mgmt.h
+++ b/libgfortran/caf/shmem/teams_mgmt.h
@@ -87,12 +87,34 @@ extern caf_shmem_team_t caf_teams_formed;
void update_teams_images (caf_shmem_team_t);
/* Same as update_teams_images, but with the team's image_count lock already
- taken. Shaped as a counter_barrier_recheck callback. */
+ taken. */
-void update_teams_images_locked (void *);
+void update_teams_images_locked (caf_shmem_team_t);
-void check_health (int *, char *, size_t);
+/* A counter_barrier_recheck: update_teams_images_locked, returning non-zero
+ when a stopped image is a member of the team. */
-#define HEALTH_CHECK(stat, errmsg, errlen) check_health (stat, errmsg, errlen)
+int update_and_check_stopped (void *);
+
+/* Get the number of images of the team that have terminated. */
+
+int team_terminated_images (caf_shmem_team_t);
+
+/* Set STAT for the stopped or failed images among the COUNT images in MAP.
+ Returns the stat value. */
+
+int check_health (const int *map, int count, int *stat, char *errmsg,
+ size_t errmsg_len);
+
+/* Perform the health check on the specified team. */
+
+#define TEAM_HEALTH_CHECK(team, stat, errmsg, errlen) \
+ check_health ((team)->u.image_info->image_map, \
+ (team)->u.image_info->image_map_size, stat, errmsg, errlen)
+
+/* Perform the health check on the current team. */
+
+#define HEALTH_CHECK(stat, errmsg, errlen) \
+ TEAM_HEALTH_CHECK (caf_current_team, stat, errmsg, errlen)
#endif
--
2.55.0