Coarray Teams, Events, Collectives and Atomics

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Fortran 2023 (ISO/IEC 1539-1:2023, J3/23-007r1) and was checked on 2026-10-19. It continues Coarrays, Images and Synchronization, which covers images, codimensions, coindexed access, segments and sync all, sync images, sync memory, lock and critical. Here are the features built on that base: teams (the subsets of images, 11.1.5 and 11.7.6 to 11.7.9), events (one-sided signalling, 11.7.7 and 11.7.8), the Fortran 2023 put with notify (11.6 and 9.6), the collective subroutines (16.6 and 16.9.54 to 16.9.58), the atomic subroutines (16.5 and 16.9.28 to 16.9.38) and the support for failed and stopped images. Teams, events, collectives, atomics and failed-image support entered the language in Fortran 2018 from the technical specification ISO/IEC TS 18508:2015 (Annex C.1 of J3/23-007r1). Put with notify and the permission for an array or allocatable object to have a coarray component are Fortran 2023 (Introduction of J3/23-007r1). The running example is still heatflow, the 2D heat-diffusion program: each image owns a slab of the plate, as in heatflow/parallel/coarray_halo.f90, and the examples here are the coordination around that slab: global reductions, a split of the plate into two teams, a hand-off between two images and a ticket counter.

What was compiled, with what, and what works

The multi-image features are the part of Fortran that compilers support least, so this page is explicit about it. Every example was compiled with GNU Fortran 16.2 as gfortran -fcoarray=lib -std=f2023 -Wall -Wextra -pedantic -fcheck=all file.f90 -lcaf_shmem (the library built with GCC 16, shared-memory images in separate processes; the number of images is set by the environment variable GFORTRAN_NUM_IMAGES, here 4) with no warning and run for real, except where an example is labelled as rejected. The same sources were also tried with OpenCoarrays 2.10.3 (caf and cafrun, built on MPICH) and with LLVM Flang 23. The result, per feature:

Feature gfortran 16.2, -fcoarray=lib with libcaf_shmem OpenCoarrays 2.10.3 LLVM Flang 23

co_sum, co_max, co_min, co_broadcast, co_reduce

works (with result_image, stat, errmsg)

works

co_reduce: "not yet implemented"; with -fcoarray only

form team, change team, sync team, team_number, get_team

works, collectives inside change team act on the team

the link fails: undefined reference to _gfortran_caf_get_team (and change team crashed in a smaller test)

"not yet implemented" or link errors

Image selectors team= and team_number= of the current or an ancestor team

works

not tested (the teams do not link)

not available

Image selector team_number= naming a sibling team

rejected at run time: "requested team not found"

not tested

not available

event post, event wait, event_query, event_type

works

works

"not yet implemented: EVENT POST runtime"

atomic_define, atomic_ref, atomic_add, atomic_cas, atomic_fetch_add

works

works, but the ticket check of the atomics example printed F (see below)

"not yet implemented: intrinsic atomic_define"

fail image, failed_images, stopped_images, image_status, stat= on sync all

works, with caveats (see Failed and stopped images)

stop and fail image terminate the whole MPI job

not available

Put with notify (notify_type, notify=, notify wait)

rejected at compile time

not tested

not available

Array or allocatable object with a coarray component (Fortran 2023)

rejected at compile time

not tested

not available

team= and completion= arguments of the collectives (Fortran 2028 draft)

rejected at compile time

not tested

not available

Flang says, by default, "Multi-image features are experimental and are disabled by default, use '-fcoarray' to enable". With -fcoarray it prints a warning that multi-image support "is still experimental and in development" and then stops at the first construct it cannot lower. The diagnostics are quoted where each feature is discussed. Without a -fcoarray option gfortran refuses coarray syntax (Fatal Error: Coarrays disabled at (1), use '-fcoarray=' to enable), and with -fcoarray=single it accepts the syntax but there is only one image: the event handshake below stops at its own error stop 'needs two images'. Multi-image runs need -fcoarray=lib and a library, here libcaf_shmem.

Collective subroutines

A collective subroutine (16.6) computes one value from the contributions of all the images of the current team, without the program having to synchronize them by hand. Every image of the team calls it, in unordered segments (a collective is not an image control statement), and the calls correspond: the same collective, in the same order, with arguments of the same type, kind and shape. The five standard collectives are:

Subroutine Effect Clause

co_sum(a [, result_image, stat, errmsg])

a becomes the processor-dependent approximation to the sum over the images; numeric a

16.9.58

co_max(a [, ...]) and co_min(a [, ...])

maximum and minimum; integer, real or character a

16.9.55, 16.9.56

co_broadcast(a, source_image [, stat, errmsg])

every image receives the value of a on source_image; no result_image

16.9.54

co_reduce(a, operation [, result_image, stat, errmsg])

generalized reduction with a pure function of two arguments that must be mathematically associative

16.9.57

The common rules:

  • a is intent(inout), is not coindexed, and has the same shape, type and type parameters on every image. It may be an array, in which case the reduction is element by element.

  • Without result_image every image gets the result. With result_image=k only image k gets it and a becomes undefined on the others, because the processor may use it for intermediate values. This is a common source of stale reads: after call co_min(t, result_image=1) read t on image 1 only.

  • The operation argument of co_reduce is a pure function with exactly two scalar, non-optional, non-allocatable arguments and a result, all of the type and type parameters of a; it shall be the same function on every image. The standard says it shall implement a mathematically associative operation and that the order of evaluation is otherwise not specified, so a non-commutative operation can give an order-dependent result (note in 16.9.57). co_reduce replaced the name operator by operation in Fortran 2018 and no longer requires commutativity (Annex C.1).

  • With stat present, an error is reported instead of terminating: stat is zero on success, stat_stopped_image if an image of the team has stopped, otherwise stat_failed_image if one has failed, otherwise another positive, processor-dependent value; errmsg receives an explanatory message. Without stat, an error initiates error termination (16.6). The standard also allows a collective to succeed on one image while it fails on another (16.6), so stat must be tested on every image.

  • A collective inside a change team construct acts on the current team, which is what makes teams useful (see the next section).

  • Floating-point sums are approximations: the order of the additions is not specified, so the last digits can change from run to run and with the number of images.

The program below does the collective work of one heatflow time step: the hottest and coldest temperatures, the total heat, the value of largest magnitude by a user-defined reduction, and the configuration read on image 1 and broadcast to the others. The reduction operation is in a module, which keeps the example free of the nested-procedure trampoline that an internal procedure passed as an actual argument can require.

module slab_ops
  use, intrinsic :: iso_fortran_env, only: real64
  implicit none
  private
  public :: dp, max_abs
  integer, parameter :: dp = real64
contains
  ! the operation handed to co_reduce: pure, two scalar arguments, same type as the result
  pure function max_abs(a, b) result(c)
    real(dp), intent(in) :: a, b
    real(dp) :: c
    c = merge(a, b, abs(a) >= abs(b))
  end function max_abs
end module slab_ops

program collectives
  use slab_ops, only: dp, max_abs
  implicit none
  integer :: me, np, stat
  real(dp) :: t_max, t_min, heat, worst, dt
  real(dp) :: cfg(3)
  character(len=64) :: msg

  me = this_image()
  np = num_images()

  ! pretend each image owns a slab of the plate with these extreme temperatures
  t_max = 10.0_dp*me
  t_min = -1.5_dp*me
  heat = 0.25_dp*me

  call co_max(t_max)                       ! the result is on every image
  call co_min(t_min, result_image=1)       ! only image 1 holds the result afterwards
  call co_sum(heat)                        ! total heat, on every image
  worst = (-1.0_dp)**me*me                 ! +/- sign pattern: -1, 2, -3, 4
  call co_reduce(worst, max_abs)           ! the value of largest magnitude: 4.0

  cfg = 0.0_dp
  if (me == 1) cfg = [1.0e-4_dp, 0.5_dp, 2.0_dp]   ! only image 1 read the input
  call co_broadcast(cfg, source_image=1)

  dt = cfg(1)
  stat = -1
  msg = ''
  call co_sum(dt, stat=stat, errmsg=msg)   ! with stat= an error is reported instead of terminating

  if (me == 1) then
    print '(a,f7.2)', 'co_max            : ', t_max
    print '(a,f7.2)', 'co_min (image 1)  : ', t_min
    print '(a,f7.2)', 'co_sum            : ', heat
    print '(a,f7.2)', 'co_reduce max_abs : ', worst
    print '(a,3f8.4)', 'co_broadcast      : ', cfg
    print '(a,es10.2,a,i0)', 'sum of dt         : ', dt, '  stat=', stat
  end if
  sync all
  if (me == np) print '(a,3f8.4)', 'last image also has: ', cfg
end program collectives

Output (4 images):

co_max            :   40.00
co_min (image 1)  :   -6.00
co_sum            :    2.50
co_reduce max_abs :    4.00
co_broadcast      :   0.0001  0.5000  2.0000
sum of dt         :   4.00E-04  stat=0
last image also has:   0.0001  0.5000  2.0000

The same program, built with caf and run with cafrun -n 4, printed the same lines. Flang 23 stops on co_reduce: with -fcoarray it reports not yet implemented: coarray: intrinsic co_reduce, and without it the first coarray-related statement is refused as above.

Teams

A team is a subset of the images of the program that acts, for a stretch of the execution, as if it were the whole program (Annex C.1 describes it as letting a subset of the images act together as if it were the set of all images). The images at the start form the initial team. A team formed from the current team by form team is a child of it; the current team is its parent; teams with the same parent are siblings. Each team has a team number: positive for a child team, and -1 for the initial team (3.142.5 and 16.9.207). Inside a team, the images are numbered 1 to the number of images in the team.

flowchart TD I["initial team: team_number() = -1
images 1 2 3 4"] -->|"form team (1, half)"| T1["team 1: images 1 2
this_image() = 1 2"] I -->|"form team (2, half)"| T2["team 2: images 3 4
this_image() = 1 2"] T1 -->|"end team"| I T2 -->|"end team"| I

The pieces:

Construct or procedure What it does Clause

type(team_type)

opaque type in iso_fortran_env that identifies a team; it cannot be a coarray (C824 in 8.5.6, and Annex C.1)

16.10.2.34

form team (team_number, team_variable [, new_index=n] [, sync-stat])

collective over the current team: every image gives the number of the team it joins; a new team is created for each distinct number; the variable identifies it. new_index= sets this image’s index in the new team (positive, at most the team size, distinct); without it the numbering is processor dependent

11.7.9

change team (team_value [, coarray-association-list] [, sync-stat]) …​ end team

the construct that makes the team the current team. It synchronizes all images of the new team on entry and again on end team; return is not allowed inside it, and a branch may not leave it

11.1.5

sync team (team_value [, sync-stat])

synchronizes the images of the team named: the current team, an ancestor, or a child of the current team

11.7.6

get_team([level])

returns the team value for current_team, parent_team or initial_team (named constants of iso_fortran_env)

16.9.95

team_number([team])

the team number of the team (the current team if absent)

16.9.207

this_image([team]), num_images([team]), image_index(coarray, sub, team)

the same inquiries, relative to a given team

16.9.208, 16.9.156, 16.9.107

failed_images([team]), stopped_images([team]), image_status(image [, team])

see Failed and stopped images

16.9.87, 16.9.199, 16.9.108

form team and the change team statement must be executed by all active images of the team concerned, the same number of times. A coarray established before change team stays accessible inside it; its cosubscripts then count images of the current team. A named construct entity can re-declare the codimensions with coarray-association-list (codimension-decl => selector), which works as in associate. A child team can allocate and deallocate allocatable coarrays collectively among its own images. An allocatable coarray that was unallocated when the construct started and is allocated at end team is deallocated by it (11.1.5.2).

The example splits four images into a left and a right half, which is how a plate would be handed to two groups, and then makes each half compute its own total with a plain co_sum:

program teams
  use, intrinsic :: iso_fortran_env, only: team_type
  implicit none
  type(team_type) :: half, where_am_i
  integer :: me, color, my_total[*], in_team, team_id, n_in_team
  integer :: total_seen[*]

  me = this_image()
  color = merge(1, 2, me <= num_images()/2)          ! left half = team 1, right half = team 2
  form team (color, half, new_index=me - (color - 1)*(num_images()/2))   ! all images, collectively
  where_am_i = get_team()                             ! the current (here: initial) team

  if (me == 1) print '(a,i0,a,i0)', 'initial team: team_number()=', team_number(), ' num_images()=', num_images()

  change team (half)                                   ! coarrays declared outside stay accessible
    in_team = this_image()
    team_id = team_number()
    n_in_team = num_images()
    total_seen = me                                    ! 'me' is the image index in the initial team
    call co_sum(total_seen)                            ! a collective acts on the current team only
    sync team (get_team())                             ! synchronize the current team (sync all would too)
    my_total = total_seen
  end team                                             ! back in the initial team; implicit synchronization

  sync all
  if (me == 1) then
    print '(a,i0,a,i0,a,i0)', 'after end team, image 1 sees the initial team again: ', this_image(), ' of ', num_images()
    print '(a,i0)', 'sum over team 1: ', my_total[1]
    print '(a,i0)', 'sum over team 2: ', my_total[num_images()]
    print '(a,l1)', 'where_am_i has team_number -1: ', team_number(where_am_i) == -1
    print '(a,i0,a,i0,a,i0)', 'last local values: team_id=', team_id, ' in_team=', in_team, ' n_in_team=', n_in_team
  end if
end program teams

Output (4 images):

initial team: team_number()=-1 num_images()=4
after end team, image 1 sees the initial team again: 1 of 4
sum over team 1: 3
sum over team 2: 7
where_am_i has team_number -1: T
last local values: team_id=1 in_team=1 n_in_team=2

The two sums are for images 1 and 2 (1 + 2 = 3) and images 3 and 4 (3 + 4 = 7): the co_sum inside change team never saw the other half. The statement end team restored the initial team, in which team_number() is -1 again. Three details:

  • The image numbering of a new team is processor dependent unless new_index= is given. Without it, a probe run with four images (the same program without new_index=) made global image 3 image 2 of team 2 and global image 4 image 1, and a six-image run made global image 2 image 3 of team 1 and global image 3 image 2. Code that indexes a coarray inside the team by this_image() should say what it wants with new_index=.

  • sync all inside the construct synchronizes the current team only; the images of the other half are not involved. After end team the initial team is the current team again, so sync all there involves all four images.

  • Execution of change team and end team each include a synchronization of the new team. The standard’s note (11.1.5.2) is that a sync team of the parent team immediately after change team, or a sync all after end team, is how to wait for the whole of the original team.

Team-aware image selectors

A cosubscript list selects an image; an image selector may also carry team= or team_number= (9.6, C934: not both):

  • With no specifier, the team of the selector is the current team: the cosubscript is an image index of the current team.

  • team=team_value: the team shall be the current team or an ancestor; the object shall be an established coarray in that team.

  • team_number=n: when the current team is not the initial team, n shall be the team number of a sibling team of the current team, and the object shall be a coarray established in the parent of the current team (or an associating entity of the change team construct). In the initial team, n shall be the team number of the initial team, -1.

program team_selectors
  use, intrinsic :: iso_fortran_env, only: team_type, initial_team, parent_team, current_team
  implicit none
  type(team_type) :: half
  integer :: me, color, x[*]

  me = this_image()
  color = merge(1, 2, me <= num_images()/2)
  form team (color, half, new_index=me - (color - 1)*(num_images()/2))
  x = 100*me                         ! x is established in the initial team
  sync all

  change team (half)
    sync all
    if (team_number() == 2 .and. this_image() == 1) then
      print '(a,i0,a,i0)', 'global image ', me, ' is image 1 of team ', team_number()
      print '(a,i0)', 'x[1]                         = ', x[1]                              ! image 1 of MY team
      print '(a,i0)', 'x[1, team=get_team(initial_team)] = ', x[1, team=get_team(initial_team)]   ! image 1 of the initial team
      print '(a,i0)', 'x[2, team_number=2]          = ', x[2, team_number=2]                ! my own team, by number
      print '(a,i0)', 'num_images(get_team(parent_team)) = ', num_images(get_team(parent_team))
      print '(a,i0)', 'image_index(x, [1], get_team(initial_team)) = ', image_index(x, [1], get_team(initial_team))
    end if
  end team
end program team_selectors

Output (4 images):

global image 3 is image 1 of team 2
x[1]                         = 300
x[1, team=get_team(initial_team)] = 100
x[2, team_number=2]          = 400
num_images(get_team(parent_team)) = 4
image_index(x, [1], get_team(initial_team)) = 1

Global image 3 is image 1 of team 2: x[1] is the first image of its own team, global image 3, which holds 300; the selector with team=get_team(initial_team) reaches global image 1 (100); and x[2, team_number=2] is the second image of its own team, global image 4 (400). The standard also allows a sibling’s number. That is the case the same compilers do not run (a deliberately standard-conforming program, rejected by the library at run time):

program sibling_selector
  use, intrinsic :: iso_fortran_env, only: team_type
  implicit none
  type(team_type) :: half
  integer :: me, color, x[*]
  me = this_image()
  color = merge(1, 2, me <= num_images()/2)
  form team (color, half, new_index=me - (color - 1)*(num_images()/2))
  x = 100*me
  sync all
  change team (half)
    sync all
    if (team_number() == 1 .and. this_image() == 1) print '(a,i0)', 'x in sibling team 2, image 1: ', x[1, team_number=2]
  end team
end program sibling_selector

GNU Fortran 16.2 compiles it without a warning. At run time on 4 images the library prints:

Fortran runtime error: requested team not found
ERROR: Image 1(pid: 15) failed with signal 0, exitstatus 1.

and the remaining images then do not finish on their own: the run had to be killed by the 60-second timeout. Under C.7.7 of the standard ("Accessing coarrays in sibling teams") this access is valid, so this is a limitation of the shared-memory library in this release. While it stands, reach a sibling’s data by publishing it in a coarray of the parent team and selecting with team= (as in the previous example), or by an x[...] outside the construct. The selector with the team number of the current team, x[2, team_number=2] above, works.

OpenCoarrays 2.10.3 could not be used for the teams examples: building 02_teams.f90 and 06_team_selectors.f90 with caf fails to link with undefined reference to _gfortran_caf_get_team, because that library predates the teams support in GCC 16. A simpler program with form team and change team only crashed with a segmentation fault at run time. Flang 23 refuses teams: by default with the "Multi-image features are experimental" error; with -fcoarray, it prints for example not yet implemented: coarray: lowering a reference to a coarray object; for the form-team, change-team and team-number statements the objects it links against are prif_* runtime entry points (the Parallel Runtime Interface for Fortran, which the Caffeine library implements), and the build ended with undefined references to _QMprifPprif_form_team and _QMprifPprif_change_team.

Events

An event is a counter on one image that other images increment, with the waiting image able to block until the count is high enough. It is the Fortran way to say "this data is ready" without a global barrier, and it orders segments in the same way as sync images (11.7.8: the segments before k posts precede the segments after the wait that satisfies them).

Item Meaning Clause

type(event_type)

from iso_fortran_env; an event variable, normally a coarray, with an integer count starting at zero. It cannot be copied (source= of an allocate may not be of that type)

16.10.2.10

event post (ev[k] [, sync-stat])

atomically adds one to the count on image k. It does not wait for the receiver

11.7.7

event wait (ev [, until_count=n] [, sync-stat])

the event variable shall not be coindexed: an image waits only on its own. Waits until the count is at least n (default and minimum one), then atomically decrements it by n

11.7.8

call event_query(ev, count [, stat])

returns the current count (atomically with respect to posts; -1 if an error occurs). The event variable is not coindexed

16.9.82

Because event wait subtracts the threshold, the count is the number of posts not yet consumed: ten posts and two plain waits leave a count of eight (the example in 16.9.82). A count is only a snapshot when other images may still post.

Segments are ordered by an event: an image that wrote data, then posted, precedes the receiver’s segment that follows the wait. This is the guarantee that makes the typical hand-off safe, with no sync memory or barrier needed. The sequence below is the handshake the next program performs; it also shows an acknowledgement in the opposite direction, which the producer needs before it may reuse the buffer.

sequenceDiagram participant P as Image 1 (producer) participant C as Image 2 (consumer) P->>C: halo(:)[2] = data (one-sided put) P->>C: event post (data_ready[2]) Note over C: event wait (data_ready) returns C->>C: read halo C->>P: event post (consumed[1]) Note over P: event wait (consumed) returns P->>P: may overwrite halo again
program event_handshake
  use, intrinsic :: iso_fortran_env, only: event_type
  implicit none
  integer, parameter :: n = 4
  real :: halo(n)[*]                  ! the data window on every image
  type(event_type) :: data_ready[*]   ! posted on the consumer when the data has arrived
  type(event_type) :: consumed[*]     ! posted on the producer when the consumer is done
  integer :: count_left

  if (num_images() < 2) error stop 'needs two images'

  if (this_image() == 1) then
    halo(:)[2] = [1.0, 2.0, 3.0, 4.0]    ! one-sided put into image 2
    event post (data_ready[2])           ! orders the put before the consumer's read
    event wait (consumed)                ! wait for the acknowledgement
    call event_query(consumed, count_left)
    print '(a,i0)', 'producer: acknowledged, event count after wait = ', count_left
  else if (this_image() == 2) then
    event wait (data_ready)
    print '(a,4f5.1)', 'consumer: halo = ', halo
    event post (consumed[1])
  end if
end program event_handshake

Output (4 images, images 3 and 4 do nothing):

consumer: halo =   1.0  2.0  3.0  4.0
producer: acknowledged, event count after wait = 0

The data is written before the post, and the consumer reads it after its wait, so it sees 1.0 2.0 3.0 4.0. The count is back to zero after the wait. OpenCoarrays 2.10.3 printed the same two lines; Flang 23 with -fcoarray stopped with not yet implemented: coarray: lowering a reference to a coarray object.

until_count gathers several posts with one wait, which is how a leader learns that a whole group is ready:

program event_count
  use, intrinsic :: iso_fortran_env, only: event_type
  implicit none
  type(event_type) :: arrived[*]
  integer :: n

  if (this_image() /= 1) then
    event post (arrived[1])                         ! every other image reports once
  else
    event wait (arrived, until_count=num_images() - 1)   ! returns when the count reaches num_images()-1
    call event_query(arrived, n)
    print '(a,i0,a,i0)', 'image 1 saw ', num_images() - 1, ' posts; count left = ', n
  end if
end program event_count

Output (4 images):

image 1 saw 3 posts; count left = 0

Notes:

  • An event post is a one-way, non-blocking release: it never waits for the receiver. Always wait on the receiver’s own variable.

  • With stat= on event post an error is reported as for any image control statement (11.7.11), including stat_failed_image when the target image has failed; event wait reports another positive value on error.

  • A named variable of type event_type shall be a coarray (C1604). The count is updated as if by atomic_add, so an event coarray may be referenced in segments unordered with respect to each other, like an atomic (16.10.2.10); notify_type is specified the same way (16.10.2.22).

Put with notify (Fortran 2023)

A put followed by an event post is two operations: the data transfer and the signal. Fortran 2023 fuses them: an assignment to a coindexed variable can carry notify= in its image selector, which atomically increments the notify count of a notify_type variable on the target image, and the receiver blocks with notify wait. Introduced in Fortran 2023 (Introduction of J3/23-007r1), described in 11.6, in 9.6 (C933: the notify= specifier may appear only in the designator of the variable of an intrinsic assignment statement) and in 16.10.2.22 (notify_type, whose count behaves like an atomic_int_kind integer updated as if by atomic_add).

The intended form of the handshake of the previous section, for the data direction only, is:

program notify_demo
  use, intrinsic :: iso_fortran_env, only: notify_type
  implicit none
  type(notify_type) :: done[*]
  real :: halo(4)[*]
  if (this_image() == 1) then
    halo(:)[2, notify=done] = [1.0, 2.0, 3.0, 4.0]   ! Fortran 2023
  else if (this_image() == 2) then
    notify wait (done)
    print *, halo
  end if
end program notify_demo

Output of gfortran -fcoarray=lib -std=f2023 (GNU Fortran 16.2): rejected, because neither the type nor the syntax exists in this release:

11_notify_rejected.f90:2:42:

    2 |   use, intrinsic :: iso_fortran_env, only: notify_type
      |                                          1
Error: Symbol 'notify_type' referenced at (1) not found in intrinsic module ISO_FORTRAN_ENV
11_notify_rejected.f90:4:20:

    4 |   type(notify_type) :: done[*]
      |                    1
Error: Derived type 'notify_type' at (1) is being used before it is defined
11_notify_rejected.f90:7:22:

    7 |     halo(:)[2, notify=done] = [1.0, 2.0, 3.0, 4.0]   ! Fortran 2023
      |                      1
Error: Invalid form of coarray reference at (1)

This example was not run: the text is checked against the standard (11.6, 9.6 and 16.10.2.22), not against a compiler. None of the compilers available for this page accepts it. Intel ifx, NAG and the other compilers were not available, so nothing is claimed about them. Until your compiler supports it, use the event handshake above: event post after the put, which orders the same two segments.

Atomic subroutines

An atomic subroutine (16.5) performs an action on its atom argument indivisibly. Any two atomic actions on the same object in unordered segments behave as if one finished before the other began, in a processor-dependent order, without needing image control statements. They are the building block for custom synchronization (counters, flags, tickets). The atom is a scalar coarray or coindexed object of type integer of kind atomic_int_kind or logical of kind atomic_logical_kind (both from iso_fortran_env, 16.10.2.2 and 16.10.2.3). Only atom is accessed atomically; the other arguments are ordinary.

Subroutine Effect Clause

atomic_define(atom, value [, stat])

sets atom to value

16.9.31

atomic_ref(value, atom [, stat])

copies atom into value (note the argument order)

16.9.37

atomic_add(atom, value [, stat]), atomic_and, atomic_or, atomic_xor

atom becomes atom combined with value (integer only)

16.9.28, 16.9.29, 16.9.36, 16.9.38

atomic_fetch_add(atom, value, old [, stat]), atomic_fetch_and, atomic_fetch_or, atomic_fetch_xor

the same, and old receives the value atom had before the operation

16.9.32 to 16.9.35

atomic_cas(atom, old, compare, new [, stat])

if atom equals compare it becomes new; old always receives the previous value; integer and logical

16.9.30

atomic_define and atomic_ref are older (their stat argument is among the Fortran 2018 additions from TS 18508, Annex C.1); atomic_add and the rest of the family arrived in Fortran 2018 (Annex C.1). stat is zero on success; stat_failed_image if atom is on a failed image; otherwise a processor-dependent positive value; without it an error terminates the program (16.5).

Atomics synchronize nothing by themselves: an atomic_define of the initial value on one image and an atomic_add on another, in unordered segments, are in an undetermined order. The rules of the standard (16.5) are the ones that matter: atomic operations shall make asynchronous progress (a loop that polls with atomic_ref will eventually see the value), and the sequence of changes seen by another image can appear in a different order from the one the defining image performed. The program below does what the named plain coarray accesses cannot: a shared counter, a ticket dispenser and an election, all by atomic_* on image 1, followed by a flag polled by the last image.

program atomics
  use, intrinsic :: iso_fortran_env, only: atomic_int_kind, atomic_logical_kind
  implicit none
  integer(atomic_int_kind) :: counter[*], ticket[*], leader[*]
  logical(atomic_logical_kind) :: done[*]
  integer(atomic_int_kind) :: mine, old, seen
  logical :: flag
  integer :: stat, me, k
  integer(atomic_int_kind), allocatable :: tickets(:)[:]

  me = this_image()
  allocate (tickets(num_images())[*])
  call atomic_define(counter, 0_atomic_int_kind)     ! initialise your own copy ...
  call atomic_define(ticket, 0_atomic_int_kind)
  call atomic_define(leader, 0_atomic_int_kind)
  call atomic_define(done, .false.)
  sync all                                           ! ... then make it visible before anyone uses it

  call atomic_add(counter[1], 10_atomic_int_kind)     ! everybody adds 10 to image 1's counter
  call atomic_fetch_add(ticket[1], 1_atomic_int_kind, mine)   ! take a unique ticket number
  tickets(me)[1] = mine                                ! publish it (one distinct element per image)
  call atomic_cas(leader[1], old, 0_atomic_int_kind, int(me, atomic_int_kind))   ! first one wins

  sync all
  if (me == 1) then
    call atomic_ref(seen, counter, stat)
    print '(a,i0,a,i0)', 'counter = ', seen, '  stat = ', stat
    print '(a,l1)', 'every ticket 0..3 handed out exactly once: ', &
      all([(count(tickets == k) == 1, k=0, num_images() - 1)])
    call atomic_ref(seen, leader)
    print '(a,l1)', 'one leader elected, index in range: ', seen >= 1 .and. seen <= num_images()
    call atomic_define(done[num_images()], .true.)
  end if
  if (me == num_images()) then
    do                                                 ! spin until image 1 signals
      call atomic_ref(flag, done)
      if (flag) exit
    end do
    print '(a)', 'last image saw the flag set by image 1'
  end if
end program atomics

Output (4 images):

counter = 40  stat = 0
every ticket 0..3 handed out exactly once: T
one leader elected, index in range: T
last image saw the flag set by image 1

Four images each added 10 (40), took a different ticket (the check prints T when every number from 0 to 3 was handed out exactly once; which image got which number differs from run to run), and one won the election. The tickets are published with ordinary assignments to distinct elements of a coarray, and sync all orders them before they are read. With OpenCoarrays 2.10.3 the counter and the election were right, but the ticket check printed F (the published element values were garbage); that was not investigated, so the example is portable across the standard, not shown to be portable across these libraries. Flang 23 stops with not yet implemented: coarray: intrinsic atomic_define.

Failed and stopped images

An image stops when it begins normal termination (stop, or the end of the program), and fails when it terminates abnormally in a way the program may survive, which a program can simulate with the fail image statement (11.5). The rest of the program can then learn what happened and continue with the remaining images. Detecting this relies on stat= in image control statements and collectives:

Item Meaning Clause

sync all (stat=s, errmsg=m), sync images, sync team, event post, form team, change team, end team

if an involved image has stopped, s is stat_stopped_image; otherwise if one failed and nothing else is wrong, the action is performed on the active images and s is stat_failed_image; otherwise another positive value. The effect of a sync that reports stat_stopped_image is that of sync memory

11.7.11

failed_images([team, kind])

increasing indices of the images known by the invoking image to have failed (a zero-sized array if none)

16.9.87

stopped_images([team, kind])

the same for stopped images

16.9.199

image_status(image [, team])

stat_failed_image, stat_stopped_image or zero

16.9.108

stat_failed_image, stat_stopped_image (and the lock-related stat_locked, stat_unlocked, stat_locked_other_image, stat_unlocked_failed_image)

named constants of iso_fortran_env; the first two are distinct positive values

16.10.2.28 to 16.10.2.33

Image failure is a model: the standard defines what a program can observe, and the implementation decides what can fail. The two programs below follow it with the libcaf_shmem library.

program failed_stopped
  use, intrinsic :: iso_fortran_env, only: int64, stat_failed_image, stat_stopped_image
  implicit none
  integer :: stat, i

  if (this_image() == 2) stop 0          ! image 2 ends normally and early: it is "stopped"
  call pause_seconds(2)                  ! give image 2 time to finish (see the text for why)
  sync all (stat=stat)                   ! must not wait for a stopped image forever

  if (this_image() == 1) then
    print '(a,l1)', 'stat == stat_stopped_image: ', stat == stat_stopped_image
    print '(a,*(i0,1x))', 'stopped_images():   ', stopped_images()
    print '(a,*(i0,1x))', 'failed_images():    ', failed_images()
    do i = 1, num_images()
      print '(a,i0,a,i0)', 'image_status(', i, ') = ', image_status(i)
    end do
    print '(a,i0,a,i0)', 'stat_stopped_image = ', stat_stopped_image, ', stat_failed_image = ', stat_failed_image
  end if
contains
  subroutine pause_seconds(s)
    integer, intent(in) :: s
    integer(int64) :: t0, t, rate
    call system_clock(t0, rate)
    do
      call system_clock(t)
      if (t - t0 >= s*rate) exit
    end do
  end subroutine pause_seconds
end program failed_stopped

Output (4 images; the line STOP 0 comes from image 2 as it stops, and the position of that line may differ):

STOP 0
stat == stat_stopped_image: T
stopped_images():   2
failed_images():
image_status(1) = 0
image_status(2) = 6000
image_status(3) = 0
stat_stopped_image = 6000, stat_failed_image = 6001

The statement fail image is the failure case:

program fail_image_demo
  use, intrinsic :: iso_fortran_env, only: int64, stat_failed_image
  implicit none
  integer :: stat
  if (this_image() == 2) fail image      ! simulate a crash of image 2
  call pause_seconds(2)
  sync all (stat=stat)
  if (this_image() == 1) then
    print '(a,l1)', 'stat == stat_failed_image: ', stat == stat_failed_image
    print '(a,*(i0,1x))', 'failed_images(): ', failed_images()
    print '(a,i0)', 'image_status(2) = ', image_status(2)
  end if
contains
  subroutine pause_seconds(s)
    integer, intent(in) :: s
    integer(int64) :: t0, t, rate
    call system_clock(t0, rate)
    do
      call system_clock(t)
      if (t - t0 >= s*rate) exit
    end do
  end subroutine pause_seconds
end program fail_image_demo

Output (4 images):

IMAGE FAILED!
stat == stat_failed_image: T
failed_images(): 2
image_status(2) = 6001

Findings that apply to GNU Fortran 16.2 with libcaf_shmem, observed here:

  • The pause_seconds wait before sync all (stat=stat) is not decoration. A sync all reaching an image that has not yet stopped is a barrier, and in the runs made here a version of the program with no pause usually did not finish (it had to be killed by the timeout) instead of reporting stat_stopped_image. A program that needs to survive a failure should not rely on timing: let the survivors learn about a failure through a mechanism that is not a barrier the failed image was supposed to join, for example stat= on event post, or polling image_status in a loop.

  • stat_stopped_image is 6000 and stat_failed_image is 6001 in this implementation; other compilers use other values, so compare with the named constants.

  • Assigning the result of stopped_images() or failed_images() to an allocatable array (gone = stopped_images()) gave an array of size 2, 2 0, when one image had stopped, while using the call directly in print or size() gave the right size 1. The file 10_stopped_images_assignment.f90 in the sources reproduces it. Use the result as an actual argument or index expression if you can, and check the size when you cannot.

  • fail image in OpenCoarrays 2.10.3 terminates the MPI job (BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES), and stop 0 on one image reached MPI_Abort: the run-through-failure model needs the fault-tolerance support that this build lacks.

  • The values of the other stat_ constants and the errmsg text are processor dependent; the page does not copy them.

Coarray components in arrays and allocatables (Fortran 2023)

Up to Fortran 2018 a variable whose type has a coarray component had to be a scalar, neither allocatable nor a pointer nor a coarray (the GNU Fortran message below still says so). The Introduction of J3/23-007r1 lists the relaxation: "A data object with a coarray component can be an array or allocatable." What remains in Fortran 2023 is that the variable shall not be a pointer, a coarray or a function result (C825), shall be an associate name, a dummy argument or have the allocatable or save attribute (C826), and that a coarray component shall be allocatable with a deferred coshape (C751). A type that holds the slab of the plate can then be used in an array of slabs. The Fortran 2023 form:

program coarray_components
  implicit none
  type :: slab_t
    real, allocatable :: u(:)[:]
  end type slab_t
  type(slab_t) :: slabs(2)  ! array of a type with a coarray component
  integer :: k
  do k = 1, 2
    allocate (slabs(k)%u(3)[*])
    slabs(k)%u = real(this_image()*10 + k)
  end do
  sync all
end program coarray_components

Output of gfortran -fcoarray=lib -std=f2023 (GNU Fortran 16.2): rejected:

12_coarray_component_array_rejected.f90:6:19:

    6 |   type(slab_t) :: slabs(2)  ! array of a type with a coarray component
      |                   1~~~~~~~
Error: Variable 'slabs' at (1) with coarray component shall be a nonpointer, nonallocatable scalar, which is not a coarray

The message states the older restriction, so this release does not implement the relaxation. Not run; the example is checked against the standard (7.5.4.3, 8.5.6 and the Introduction), not against a compiler. Until a compiler catches up, declare one scalar variable of the type per slab, or keep an allocatable coarray of the data itself next to an ordinary array of descriptors.

Fortran 2028 preview: team-scoped and non-blocking collectives

Fortran 2028 is at the committee-draft stage; ISO/IEC 1539-1 for it is not published, and this section is based on the J3 working draft J3/26-007r1 of 3 March 2026 (the WG5 committee draft N2258 is not a public document). Anything below can still change. The relevant changes in the draft’s Introduction and 17.6 are:

  • The collective subroutines gain an optional team argument. Its value shall be the current team, an ancestor, or a team whose parent is the current team, and identify the same team on all images taking part, so a collective can be run on a team without a change team.

  • They gain an optional completion argument of the new type completion_type, which turns the call into an asynchronous collective: the call returns after initiating the operation, the new subroutine complete(completion [, finished]) waits for it, and meanwhile the data argument (and stat and errmsg) must not be touched, like the buffer of an asynchronous input operation (draft 17.6, 17.9.64).

  • New collective prefix reductions (co_sum_prefix_inclusive, co_sum_prefix_exclusive, co_reduce_prefix_inclusive, co_reduce_prefix_exclusive) and the corresponding array functions.

A program that uses team= today is rejected, which I checked for completeness:

program team_argument
  use, intrinsic :: iso_fortran_env, only: team_type
  implicit none
  type(team_type) :: t
  real :: x
  form team (1, t)
  x = 1.0
  call co_sum(x, team=t)    ! TEAM= is a Fortran 2028 draft feature
end program team_argument

Output of gfortran -fcoarray=lib -std=f2023 (GNU Fortran 16.2):

13_team_argument_rejected.f90:8:67:

    8 |   call co_sum(x, team=t)    ! TEAM= is a Fortran 2028 draft feature
      |                                                                   1
Error: Cannot find keyword named 'team' in call to 'co_sum' at (1)

Not run, not available: the interface is shown only as "upcoming". Do not write production code against it; where you need a team-scoped collective now, put it in a change team construct as shown above.

Where the books are outdated

Topic What changed

Teams, events, collectives, atomics, failed images

books written against Fortran 2008 describe coarrays with sync all only; these features arrived in Fortran 2018 from ISO/IEC TS 18508:2015, and a 2018-era text that calls them "a technical specification" is describing a status that ended with that revision

co_reduce

the argument is operation (not operator) and need not be commutative

Teams: team number of the initial team

the initial team has team number -1 and may be used in image selectors, num_images and image_index

Image numbering in a team

only guaranteed by new_index=; examples that assume an order are relying on the implementation

Put with notify, coarray components in arrays and allocatables

Fortran 2023; a book that predates it shows the event-based pattern and the scalar-only rule

Team-scoped and non-blocking collectives

only in the Fortran 2028 working draft

Compilers

older books say coarrays need a vendor compiler or OpenCoarrays; GCC 16 has native shared-memory coarrays and teams, and OpenCoarrays 2.10.3 (MPI based) does not link the teams of the same compiler

Standard version provenance and compiler support

Feature Introduced Support checked on 2026-10-19

Events, collectives, atomics (atomic_add family), teams, failed and stopped images

Fortran 2018, from TS 18508:2015 (Annex C.1)

gfortran 16.2 with libcaf_shmem runs them (with the sibling-selector, stopped_images and stopped-image synchronization limitations above); OpenCoarrays 2.10.3 runs events, collectives and atomics, not the teams; Flang 23 refuses all of it

atomic_define, atomic_ref

before Fortran 2018 (edition not confirmed here); the stat argument is from TS 18508 (Annex C.1)

gfortran 16.2 and OpenCoarrays 2.10.3

co_reduce argument operation, team expressions in change team and sync team, team_number of the initial team

changes of Fortran 2018 relative to TS 18508 (Annex C.1)

gfortran 16.2

Put with notify

Fortran 2023

rejected by gfortran 16.2 (notify_type not found in iso_fortran_env); not available in Flang 23

Array or allocatable object with a coarray component

Fortran 2023

rejected by gfortran 16.2

team= and completion= on collectives, complete, prefix collectives

Fortran 2028 working draft J3/26-007r1

rejected by gfortran 16.2

Not checked here, because no compiler for them is installed: Intel ifx, NAG, HPE Cray and NVIDIA HPC compilers. See History, Standards and Compilers for the per-compiler support table.

Pitfalls

  • Reading a on the wrong image after result_image=. The variable is undefined elsewhere.

  • Mismatched collectives. Every image of the team must call the same collective in the same order; a collective under if (this_image() == 1) deadlocks or terminates.

  • Assuming image numbers inside a team. Use new_index= or translate with image_index(x, [k], team).

  • Cosubscripts after change team. They count images of the current team; the global index needs team=get_team(initial_team).

  • Sibling selectors. Standard-conforming but refused by libcaf_shmem in GCC 16.2 at run time.

  • Waiting on someone else’s event. event wait takes the executing image’s own variable; post to the other one.

  • Timing-based failure demonstrations. A barrier that includes a stopped image may not return in the library used here; make the survivors poll image_status or use stat= on something that is not a barrier.

  • Treating atomics as synchronization. They order nothing outside the atom; publish other data with sync or an event.

  • Plain reads of an atomic. Use atomic_ref; a plain reference is not atomic.

  • Comparing stat with a literal. stat_failed_image and stat_stopped_image have processor-dependent values.

  • Writing against a feature your compiler lacks. Put with notify, component coarrays in arrays and team= collectives are not in gfortran 16.2; keep the event pattern behind a procedure so the code can change later.

  • Floating-point co_sum. The order of additions is not fixed; compare with a tolerance.

See also do concurrent and GPU Offload, OpenMP and OpenACC and MPI with mpi_f08 for the other ways to the same parallelism, and Error Handling and Program Termination for error stop and the termination of images.