Skip to content

Commit 4164cba

Browse files
committed
Fix sync images race condition.
Syncing a set of images using SYNC IMAGES is done by this simple algorithm: 1. Set up asynchronous mpi-receives for all images in the set to sync to, 2. Send the current image's status to all images in the set to sync to, and 3. Wait for the receives from step 1 to finish. When one of the receives returns a stopped image state, than abort the sync on the current image immediately, else wait until all receives have been answered. This commit also adds a new testcase: syncimages_ring, where each image is syncing to its neighbours with wrap around. The testcase is called for 3 (the minimum number of images required to show that the sync works), 13 and 23 images. This commit fixes the timeout of syncimage_status.f90 mentioned in #298. This commit superseeds pull request 299 (close #299).
1 parent 1454647 commit 4164cba

File tree

6 files changed

+113
-66
lines changed

6 files changed

+113
-66
lines changed

.travis.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,11 @@ after_success:
160160
- gcov-6 --version
161161
- bash <(curl -s https://codecov.io/bash) -x $(which gcov-6)
162162

163-
notifications:
164-
webhooks:
165-
urls:
166-
- https://webhooks.gitter.im/e/93dbafbdf76c1732a623
167-
- https://webhooks.gitter.im/e/935231573bf1b9f2fe40
168-
on_success: change # options: [always|never|change]
169-
on_failure: always
170-
on_start: always
163+
#notifications:
164+
#webhooks:
165+
#urls:
166+
#- https://webhooks.gitter.im/e/93dbafbdf76c1732a623
167+
#- https://webhooks.gitter.im/e/935231573bf1b9f2fe40
168+
#on_success: change # options: [always|never|change]
169+
#on_failure: always
170+
#on_start: always

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@ if(opencoarrays_aware_compiler)
391391
add_mpi_test(duplicate_syncimages 8 ${tests_root}/unit/sync/duplicate_syncimages)
392392
add_mpi_test(co_reduce 4 ${tests_root}/unit/collectives/co_reduce_test)
393393
add_mpi_test(syncimages_status 32 ${tests_root}/unit/sync/syncimages_status)
394+
add_mpi_test(syncimages_ring_np3 3 ${tests_root}/unit/sync/syncimages_ring)
395+
add_mpi_test(syncimages_ring_np13 13 ${tests_root}/unit/sync/syncimages_ring)
396+
add_mpi_test(syncimages_ring_np23 23 ${tests_root}/unit/sync/syncimages_ring)
394397
add_mpi_test(simpleatomics 32 ${tests_root}/unit/simple/atomics)
395398
# possible logic error in the following test
396399
# add_mpi_test(increment_my_neighbor 32 ${tests_root}/unit/simple/increment_my_neighbor)

src/mpi/mpi_caf.c

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ static int caf_is_finalized = 0;
7575
/*Sync image part*/
7676

7777
static int *orders;
78-
MPI_Request *handlers;
7978
static int *images_full;
79+
MPI_Request *sync_handles;
8080
static int *arrived;
8181

8282
/* Pending puts */
@@ -96,8 +96,8 @@ caf_static_t *caf_tot = NULL;
9696

9797
/* Image status variable */
9898

99-
static int *img_status = NULL;
100-
MPI_Win *stat_tok;
99+
static int img_status = 0;
100+
static MPI_Win *stat_tok;
101101

102102
/* Active messages variables */
103103

@@ -400,27 +400,31 @@ PREFIX (init) (int *argc, char ***argv)
400400
orders = calloc (caf_num_images, sizeof (int));
401401
arrived = calloc (caf_num_images, sizeof (int));
402402

403-
handlers = malloc(caf_num_images * sizeof(MPI_Request));
403+
sync_handles = malloc(caf_num_images * sizeof(MPI_Request));
404404

405405
stat_tok = malloc (sizeof(MPI_Win));
406406

407407
#if MPI_VERSION >= 3
408408
MPI_Info_create (&mpi_info_same_size);
409409
MPI_Info_set (mpi_info_same_size, "same_size", "true");
410410
/* Setting img_status */
411-
MPI_Win_allocate(sizeof(int), 1, mpi_info_same_size, CAF_COMM_WORLD, &img_status, stat_tok);
411+
MPI_Win_create(&img_status, sizeof(int), 1, mpi_info_same_size, CAF_COMM_WORLD, stat_tok);
412412
# ifndef CAF_MPI_LOCK_UNLOCK
413413
MPI_Win_lock_all(MPI_MODE_NOCHECK, *stat_tok);
414414
# endif // CAF_MPI_LOCK_UNLOCK
415415
#else
416416
MPI_Alloc_mem(sizeof(int), MPI_INFO_NULL, &img_status, stat_tok);
417417
MPI_Win_create(img_status, sizeof(int), 1, MPI_INFO_NULL, CAF_COMM_WORLD, stat_tok);
418418
#endif // MPI_VERSION
419-
*img_status = 0;
420419
}
421420
/* MPI_Barrier(CAF_COMM_WORLD); */
422421
}
423422

423+
/* Forward declaration of sync_images. */
424+
425+
void
426+
sync_images_internal (int count, int images[], int *stat, char *errmsg,
427+
int errmsg_len, bool internal);
424428

425429
/* Finalize coarray program. */
426430

@@ -431,10 +435,17 @@ _gfortran_caf_finalize (void)
431435
PREFIX (finalize) (void)
432436
#endif
433437
{
434-
*img_status = STAT_STOPPED_IMAGE; /* GFC_STAT_STOPPED_IMAGE = 6000 */
435-
MPI_Win_sync(*stat_tok);
438+
/* For future security enclose setting img_status in a lock. */
439+
CAF_Win_lock (MPI_LOCK_EXCLUSIVE, caf_this_image - 1, *stat_tok);
440+
img_status = STAT_STOPPED_IMAGE; /* GFC_STAT_STOPPED_IMAGE = 6000 */
441+
CAF_Win_unlock (caf_this_image - 1, *stat_tok);
436442

437-
MPI_Barrier(CAF_COMM_WORLD);
443+
/* Announce to all other images, that this one is stopped. */
444+
for (int i = 0; i < caf_num_images - 1; ++i)
445+
MPI_Send (&img_status, 1, MPI_INT, images_full[i] - 1, 0, CAF_COMM_WORLD);
446+
447+
/* Add a conventional barrier to prevent images from quitting to early. */
448+
MPI_Barrier (CAF_COMM_WORLD);
438449

439450
while (caf_static_list != NULL)
440451
{
@@ -477,6 +488,10 @@ PREFIX (finalize) (void)
477488
MPI_Info_free (&mpi_info_same_size);
478489
#endif // MPI_VERSION
479490

491+
# ifndef CAF_MPI_LOCK_UNLOCK
492+
MPI_Win_unlock_all (*stat_tok);
493+
# endif // CAF_MPI_LOCK_UNLOCK
494+
MPI_Win_free (stat_tok);
480495
MPI_Comm_free(&CAF_COMM_WORLD);
481496

482497
/* Only call Finalize if CAF runtime Initialized MPI. */
@@ -486,7 +501,7 @@ PREFIX (finalize) (void)
486501
pthread_mutex_lock(&lock_am);
487502
caf_is_finalized = 1;
488503
pthread_mutex_unlock(&lock_am);
489-
exit(0);
504+
free (sync_handles);
490505
}
491506

492507

@@ -2988,7 +3003,14 @@ void
29883003
PREFIX (sync_images) (int count, int images[], int *stat, char *errmsg,
29893004
int errmsg_len)
29903005
{
2991-
int ierr = 0, i = 0, remote_stat = 0, j = 0;
3006+
sync_images_internal (count, images, stat, errmsg, errmsg_len, false);
3007+
}
3008+
3009+
void
3010+
sync_images_internal (int count, int images[], int *stat, char *errmsg,
3011+
int errmsg_len, bool internal)
3012+
{
3013+
int ierr = 0, i = 0, j = 0, int_zero = 0, done_count = 0;
29923014
MPI_Status s;
29933015

29943016
if (count == 0 || (count == 1 && images[0] == caf_this_image))
@@ -3042,59 +3064,54 @@ PREFIX (sync_images) (int count, int images[], int *stat, char *errmsg,
30423064
explicit_flush();
30433065
#endif
30443066

3067+
/* A rather simple way to synchronice:
3068+
- expect all images to sync with receiving an int,
3069+
- on the other side, send all processes to sync with an int,
3070+
- when the int received is STAT_STOPPED_IMAGE the return immediately,
3071+
else wait until all images in the current set of images have send
3072+
some data, i.e., synced.
3073+
3074+
This approach as best as possible implements the syncing of different
3075+
sets of images and figuring that an image has stopped. MPI does not
3076+
provide any direct means of syncing non-coherent sets of images.
3077+
The groups/communicators of MPI always need to be consistent, i.e.,
3078+
have the same members on all images participating. This is
3079+
contradictiory to the sync images statement, where syncing, e.g., in a
3080+
ring pattern is possible.
3081+
3082+
This implementation guarantees, that as long as no image is stopped
3083+
an image only is allowed to continue, when all its images to sync to
3084+
also have reached a sync images statement. This implementation makes
3085+
no assumption when the image continues or in which order synced
3086+
images continue. */
30453087
for(i = 0; i < count; ++i)
30463088
/* Need to have the request handlers contigously in the handlers
30473089
array or waitany below will trip about the handler as illegal. */
30483090
ierr = MPI_Irecv (&arrived[images[i] - 1], 1, MPI_INT, images[i] - 1, 0,
3049-
CAF_COMM_WORLD, &handlers[i]);
3091+
CAF_COMM_WORLD, &sync_handles[i]);
30503092
for(i = 0; i < count; ++i)
3093+
MPI_Send (&int_zero, 1, MPI_INT, images[i] - 1, 0, CAF_COMM_WORLD);
3094+
done_count = 0;
3095+
while (done_count < count)
30513096
{
3052-
# ifdef CAF_MPI_LOCK_UNLOCK
3053-
MPI_Win_lock (MPI_LOCK_SHARED, images[i] - 1, 0, *stat_tok);
3054-
# endif // CAF_MPI_LOCK_UNLOCK
3055-
ierr = MPI_Get (&remote_stat, 1, MPI_INT,
3056-
images[i] - 1, 0, 1, MPI_INT, *stat_tok);
3057-
# ifdef CAF_MPI_LOCK_UNLOCK
3058-
MPI_Win_unlock (images[i] - 1, *stat_tok);
3059-
# else // CAF_MPI_LOCK_UNLOCK
3060-
MPI_Win_flush (images[i] - 1, *stat_tok);
3061-
# endif // CAF_MPI_LOCK_UNLOCK
3062-
if(remote_stat != 0)
3097+
ierr = MPI_Waitany (count, sync_handles, &i, &s);
3098+
if (i != MPI_UNDEFINED)
30633099
{
3064-
ierr = STAT_STOPPED_IMAGE;
3065-
/* Let the other images know, that at least one image is
3066-
stopped by sending STAT_STOPPED_IMAGE instead of our id. */
3067-
for(i = 0; i < count; ++i)
3068-
MPI_Send (&ierr, 1, MPI_INT, images[i] - 1, 0, CAF_COMM_WORLD);
3069-
break;
3070-
}
3071-
}
3072-
if (ierr == 0)
3073-
{
3074-
int done_count = 0;
3075-
for(i = 0; i < count; ++i)
3076-
{
3077-
if (arrived[images[i] - 1] != STAT_STOPPED_IMAGE)
3078-
/* Only send, when no stopped images have been found. */
3079-
ierr = MPI_Send (&caf_this_image, 1, MPI_INT, images[i] - 1, 0,
3080-
CAF_COMM_WORLD);
3081-
else
3082-
ierr = STAT_STOPPED_IMAGE;
3083-
}
3084-
3085-
while (ierr != STAT_STOPPED_IMAGE && done_count < count)
3086-
{
3087-
ierr = MPI_Waitany (count, handlers, &i, &s);
3088-
if (i != MPI_UNDEFINED)
3089-
++done_count;
3090-
if (i != MPI_UNDEFINED && arrived[i] == STAT_STOPPED_IMAGE)
3091-
ierr = STAT_STOPPED_IMAGE;
3092-
else if (ierr != MPI_SUCCESS)
3093-
break;
3100+
++done_count;
3101+
if (ierr == MPI_SUCCESS && arrived[i] == STAT_STOPPED_IMAGE)
3102+
{
3103+
/* Possible future extension: Abort pending receives. At the
3104+
moment the receives are discarded by the program
3105+
termination. For the tested mpi-implementation this is ok.
3106+
*/
3107+
ierr = STAT_STOPPED_IMAGE;
3108+
break;
3109+
}
30943110
}
3111+
else if (ierr != MPI_SUCCESS)
3112+
/* Abort receives here, too, when implemented above. */
3113+
break;
30953114
}
3096-
3097-
memset(arrived, 0, sizeof(int) * caf_num_images);
30983115
}
30993116

31003117
sync_images_err_chk:
@@ -3117,8 +3134,8 @@ PREFIX (sync_images) (int count, int images[], int *stat, char *errmsg,
31173134
if (errmsg_len > len)
31183135
memset (&errmsg[len], ' ', errmsg_len-len);
31193136
}
3120-
else
3121-
caf_runtime_error (msg);
3137+
else if (!internal)
3138+
caf_runtime_error (msg);
31223139
}
31233140
}
31243141

src/tests/unit/sync/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ target_link_libraries(duplicate_syncimages OpenCoarrays)
1212

1313
add_executable(syncimages_status syncimages_status.f90)
1414
target_link_libraries(syncimages_status OpenCoarrays)
15+
16+
add_executable(syncimages_ring syncimages_ring.f90)
17+
target_link_libraries(syncimages_ring OpenCoarrays)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
! SYNC IMAGES([this_image - 1, this_image + 1]) with the STAT=STAT_STOPPED_IMAGE
2+
! specifier and wrap around of image numbers. The test is intended to check
3+
! that syncing in a ring with a stopped image still terminates all images.
4+
5+
program sync_images_ring
6+
use, intrinsic:: iso_fortran_env
7+
implicit none
8+
9+
integer :: stat_var = 0
10+
11+
if (num_images() .lt. 2) error stop "Need at least two images to test."
12+
13+
associate (me => this_image())
14+
if (me /= 1) then
15+
associate (lhs => merge(me - 1, num_images(), me /= 1), &
16+
rhs => merge(me + 1, 1, me /= num_images()))
17+
sync images([lhs, rhs], STAT=stat_var)
18+
! Only on image 2 and num_images() testing whether a stopped image is
19+
! present can be done reliably. All other images could be up ahead.
20+
if (stat_var /= STAT_STOPPED_IMAGE .and. me == 2) error stop "Error: stat_var /= STAT_STOPPED_IMAGE: "
21+
if(me == 2) print *, 'Test passed.'
22+
end associate
23+
end if
24+
end associate
25+
end program sync_images_ring

src/tests/unit/sync/syncimages_status.f90

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ program sync_images_stat
1010
me = this_image()
1111

1212
if (me /= 1 ) then
13-
call sleep(1)
1413
sync images(*,STAT=stat_var)
1514
if ( stat_var /= STAT_STOPPED_IMAGE) then
1615
print *, "Error:stat_var /= STAT_STOPPED_IMAGE: ", me

0 commit comments

Comments
 (0)