#include "mpi.h" static int MCS_LOCKRANK = MPI_KEYVAL_INVALID; enum { nextRank = 0, blocked = 1, lockTail = 2 }; void MCSLockInit(MPI_Comm comm, MPI_Win * win) { int *lmem, rank; MPI_Aint winsize; MPI_Comm_rank(comm, &rank); if (MCS_LOCKRANK == MPI_KEYVAL_INVALID) MPI_Win_create_keyval(MPI_WIN_NULL_COPY_FN, MPI_WIN_NULL_DELETE_FN, &MCS_LOCKRANK, (void *) 0); winsize = 2 * sizeof(int); if (rank == 0) winsize += sizeof(int); MPI_Win_allocate(winsize, sizeof(int), MPI_INFO_NULL, comm, &lmem, win); lmem[nextRank] = -1; lmem[blocked] = 0; if (rank == 0) { lmem[lockTail] = -1; } MPI_Win_set_attr(*win, MCS_LOCKRANK, (void *) (MPI_Aint) rank); MPI_Barrier(comm); } void MCSLockAcquire(MPI_Win win) { int flag, myrank, predecessor, *lmem; void *attrval; MPI_Win_get_attr(win, MCS_LOCKRANK, &attrval, &flag); myrank = (int) (MPI_Aint) attrval; MPI_Win_get_attr(win, MPI_WIN_BASE, &lmem, &flag); lmem[blocked] = 1; /* In case we are blocked */ // lmem[nextRank] = -1; /* According to MCS paper, advances much further including this line */ MPI_Win_lock_all(0, win); MPI_Fetch_and_op(&myrank, &predecessor, MPI_INT, 0, lockTail, MPI_REPLACE, win); MPI_Win_flush(0, win); if (predecessor != -1) { /* We didn't get the lock. Add us to the tail of the list */ MPI_Accumulate(&myrank, 1, MPI_INT, predecessor, nextRank, 1, MPI_INT, MPI_REPLACE, win); /* Now spin on our local value "blocked" until we are * given the lock */ do { MPI_Win_sync(win); /* Ensure memory updated */ } while (lmem[blocked] == 1); } // else we have the lock MPI_Win_unlock_all(win); } void MCSLockRelease(MPI_Win win) { int nullrank = -1, zero = 0, myrank, curtail, flag, *lmem; void *attrval; MPI_Win_get_attr(win, MCS_LOCKRANK, &attrval, &flag); myrank = (int) (MPI_Aint) attrval; MPI_Win_get_attr(win, MPI_WIN_BASE, &lmem, &flag); MPI_Win_lock_all(0, win); if (lmem[nextRank] == -1) { /* See if we're waiting for the next to notify us */ MPI_Compare_and_swap(&nullrank, &myrank, &curtail, MPI_INT, 0, lockTail, win); if (curtail == myrank) { /* We are the only process in the list */ MPI_Win_unlock_all(win); return; } /* Otherwise, someone else has added themselves to the list. */ do { MPI_Win_sync(win); } while (lmem[nextRank] == -1); } /* Now we can notify them. Use accumulate with replace instead * of put since we want an atomic update of the location */ MPI_Accumulate(&zero, 1, MPI_INT, lmem[nextRank], blocked, 1, MPI_INT, MPI_REPLACE, win); MPI_Win_unlock_all(win); }