📄 mpid_win_lock.c
字号:
/* (C)Copyright IBM Corp. 2007, 2008 *//** * \file src/onesided/mpid_win_lock.c * \brief MPI-DCMF MPI_Win_lock/unlock functionality */#include "mpid_onesided.h"/** * \brief Progress (advance) spin to acquire lock locally * * Adds a local waiter to the lock wait queue, to ensure that * we will eventually get a chance. This special waiter (ack[0].w0 == 0) * will result in the \e my_sync_done flag getting set, breaking us * out of the loop. At this point, we will have acquired the lock * (possibly shared with others). * * Called from MPID_Win_lock when the \e dest rank is ourself (local). * * \param[in] win Pointer to MPID_Win object * \param[in] rank Our rank (convenience) * \param[in] type Lock type * \return nothing */#define MPIDU_Spin_lock_acquire(win, rank, type) { \ if (local_lock(win, rank, type) == 0) { \ (win)->_dev.my_sync_done = 0; \ MPIDU_add_waiter(win, rank, type, NULL); \ MPIDU_Progress_spin((win)->_dev.my_sync_done == 0);\ } \}/* * * * * * Win Locks and Lock wait queue * * * * * *//** * \page lock_wait_design Lock Wait Queue Design * * When a lock cannot be immediately granted, the caller * specifics (rank, lock request type, DCMF ack info) is added * to the bottom of the lock wait queue. A lock may not be * granted for the following reasons: * * - Window is involved in some other type of epoch. * - Lock is in an incompatible state with request type. * - Lock and request are shared, but the lock wait queue * is not empty. This request must be queued to avoid * starvation of the waiter(s). * * When ever a lock is released, the top of the lock wait queue * is examined to see if the waiter is requesting a lock type * that is compatible with the current lock status. * * - Lock is free: any waiter can be granted * - Lock is shared: only a share waiter can be granted * - Lock is exclusive: no waiter can be granted * - Waiter is "dummy" requesting lock type 0, in which case * subsequent waiters are left waiting so that another type * of epoch can begin. * * The third case cannot happen since an unlock cannot result * in the lock (still) being locked exclusive. * * The second case would probably only find a waiter of type * exclusive, since any shared requests that came along would * have been granted. * * \ref unlk_wait_design *//** * \brief Shortcut for accessing the lock waiter queue * object in the window structure. */#define MPIDU_WIN_LOCK_QUEUE(w) ((struct mpid_qhead *)(w)->_dev._lock_queue)/** \brief Test whether lock queue has no waiters */#define MPIDU_LOCK_QUEUE_EMPTY(w) \ (MPIDU_WIN_LOCK_QUEUE(w)->blocks == NULL || \ MPIDU_WIN_LOCK_QUEUE(w)->blocks->next_used == NULL)/** \brief Test if lock is unlocked */#define MPID_LOCK_IS_FREE(w) ((w)->_dev.lock_granted == 0)/** \brief Test if lock is locked exclusive */#define MPID_LOCK_IS_EXCL(w) ((w)->_dev.lock_granted & INT_MSB)/** \brief Test if lock is locked shared */#define MPID_LOCK_IS_SHARE(w) ((w)->_dev.lock_granted && \ !((w)->_dev.lock_granted & INT_MSB))/** \brief Test if lock is locked exclusive by rank 'r' */#define MPID_LOCK_ISMY_EXCL(w, r) \ ((w)->_dev.lock_granted == ((r) | INT_MSB))/** \brief Test if lock is locked shared by rank 'r' \note Can't tell who's locked shared */#define MPID_LOCK_ISMY_SHARE(w, r) \ ((w)->_dev.lock_granted && \ !((w)->_dev.lock_granted & INT_MSB))/** \brief Test if lock is OK to lock exclusive */#define MPID_LOCK_OK_EXCL(w) ((w)->_dev.lock_granted == 0)/** \brief Test if lock is OK to lock shared */#define MPID_LOCK_OK_SHARE(w) (!((w)->_dev.lock_granted & INT_MSB))/** \brief Lock the lock in exclusive mode */#define MPID_LOCK_EXCL(w, r) ((w)->_dev.lock_granted = (r) | INT_MSB)/** \brief Unlock the lock (from exclusive mode) */#define MPID_UNLOCK_EXCL(w, r) ((w)->_dev.lock_granted = 0)/** \brief Lock the lock in shared mode */#define MPID_LOCK_SHARE(w, r) (++(w)->_dev.lock_granted)/** \brief Unlock the lock (from shared mode) */#define MPID_UNLOCK_SHARE(w, r) (--(w)->_dev.lock_granted)/** * \brief Examine local lock and return current lock type. * * \param[in] win Window object containing lock in question * \return Lock type (status): * - \e MPI_LOCK_EXCLUSIVE - locked in exclusive mode. * - \e MPI_LOCK_SHARED - locked by one or more nodes in shared mode. * - \e 0 - not locked. * * \ref rsrc_design\n * \ref lock_wait_design */static int local_lock_type(MPID_Win *win) { MPID_assert_debug(MPI_LOCK_EXCLUSIVE != 0 && MPI_LOCK_SHARED != 0); if (MPID_LOCK_IS_FREE(win)) { return 0; } else if (MPID_LOCK_IS_EXCL(win)) { return MPI_LOCK_EXCLUSIVE; } else /* MPID_LOCK_IS_SHARE(win) */ { return MPI_LOCK_SHARED; }}/** * \brief Local lock routine. * * Called from lock receive callback. * Also sets epoch_rma_ok if lock is acquired. * * \param[in] win Pointer to MPID_Win structure * \param[in] orig Rank of origin (locker) * \param[in] type Type of lock being requested * \return 1 if the lock was granted, or * 0 if the lock was refused (caller must wait). * * \ref lock_design\n * \ref rsrc_design\n * \ref lock_wait_design */static unsigned local_lock(MPID_Win *win, int orig, int type) { if (type == MPI_LOCK_EXCLUSIVE) { if (MPID_LOCK_OK_EXCL(win)) { MPID_LOCK_EXCL(win, orig); return 1; } } else /* type == MPI_LOCK_SHARED */ { if (MPID_LOCK_OK_SHARE(win)) { MPID_LOCK_SHARE(win, orig); MPID_assert_debug(MPID_LOCK_OK_SHARE(win)); return 1; } } return 0;}/** * \brief Local unlock routine. * * Called from unlock receive callback. * Gets origin (unlocker) rank, (expected) lock type, and origin RMA * ops count (number of RMA ops that node originated to this node). * Returns -1 if the lock is not in appropriate state to be unlocked * (by the calling node and expected lock type). * Clears epoch_rma_ok if lock is released (completely - i.e. last * shared lock release). * * \param[in] win Pointer to MPID_Win structure * \param[in] orig Rank of origin (locker) * \return 1 if the lock was released, or * 0 if the lock was "busy". * * \ref lock_design\n * \ref rsrc_design\n * \ref lock_wait_design */static unsigned local_unlock(MPID_Win *win, int orig) { MPID_assert_debug(!MPID_LOCK_IS_FREE(win)); if (MPID_LOCK_IS_EXCL(win)) { MPID_UNLOCK_EXCL(win, orig); } else if (MPID_LOCK_IS_SHARE(win)) { MPID_UNLOCK_SHARE(win, orig); MPID_assert_debug(MPID_LOCK_OK_SHARE(win)); } return 1;}/** \brief Number of Lock Wait Queue elements per allocation block */#define MPIDU_NUM_ALLOC_WAITERS 7/** * \brief Lock Wait Queue Element * * \ref rsrc_design\n * \ref lock_wait_design */struct mpid_lock_waiter { struct mpid_lock_waiter *next; /**< next used or next free */ int waiter_rank; /**< rank requesting lock */ int lock_type; /**< lock type requested */ int *_pad; /**< pad to even fraction of cacheline */ MPIDU_Onesided_ctl_t ackinfo; /**< dcmf context (opaque) info. * Not directly used in communications. */};/** \brief Padding for Lock Wait Queue Element resource block header */#define MPIDU_PAD_ALLOC_WAITERS \(sizeof(struct mpid_lock_waiter) - sizeof(struct mpid_resource))/** * \brief Setup a lock wait queue entry. * * Adds a new waiter to the (end of the) wait queue. * Saves rank, type, and ackinfo in the wait object. * Typically called from lock receive callback. * * A lock type of 0 (none) is used as a "break-point" to * ensure non-lock epoch starts will eventually succeed. * * \param[in] win Pointer to MPID_Win structure * \param[in] rank Rank of origin (locker) * \param[in] type Type of lock being requested * \param[in] ackinfo Additional info to save on queue (may be NULL) * \return nothing * * \ref rsrc_design\n * \ref lock_wait_design */static void MPIDU_add_waiter(MPID_Win *win, int rank, int type, MPIDU_Onesided_ctl_t *ackinfo) { struct mpid_lock_waiter wp; wp.waiter_rank = rank; wp.lock_type = type; if (ackinfo) { wp.ackinfo = *ackinfo; } else { memset(&wp.ackinfo, 0, sizeof(wp.ackinfo)); } (void)MPIDU_add_element(MPIDU_WIN_LOCK_QUEUE(win), &wp);}/** * \brief Conditionally pops next waiter off lock wait queue. * * Returns the next waiter on the window lock, provided its desired * lock type is compatible with the current lock status. * Fills in rank, type, and ackinfo with pertinent data, * on success. Called by unlock (recv callback). If lock status * (after the unlock) is (still) shared, then the next waiter must * want shared (probably not a likely scenario since it should have * been granted the lock when requested). However, this check ensures * that an exclusive lock waiter cannot grab a lock that is still in * shared mode. Typically called from unlock receive callback. * * \param[in] win Pointer to MPID_Win structure * \param[out] rank Rank of origin (locker) * \param[out] type Type of lock being requested * \param[out] ackinfo Additional info from original lock call (may be NULL) * \return 0 if lock waiter was popped, or * 1 if the queue was empty or next waiter incompatible * * \ref lock_design\n * \ref rsrc_design\n * \ref lock_wait_design */static int MPIDU_pop_waiter(MPID_Win *win, int *rank, int *type, MPIDU_Onesided_ctl_t *ackinfo) { struct mpid_lock_waiter wp; struct mpid_resource *lq = MPIDU_WIN_LOCK_QUEUE(win)->blocks; int lt; if (lq == NULL || lq->next_used == NULL) { return 1; /* no one waiting */ } if (MPIDU_peek_element(MPIDU_WIN_LOCK_QUEUE(win), &wp)) { return 1; /* no one waiting */ } if (!rank && !type && wp.lock_type != 0) { return 1; /* not our marker... */ } lt = local_lock_type(win); if (lt == MPI_LOCK_EXCLUSIVE || (lt != 0 && lt != wp.lock_type)) { return 1; /* not a compatible waiter */ } (void)MPIDU_pop_element(MPIDU_WIN_LOCK_QUEUE(win), NULL); if (rank) { *rank = wp.waiter_rank; } if (type) { *type = wp.lock_type; } if (ackinfo) { *ackinfo = wp.ackinfo; } return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -