/* * __wt_cond_wait -- * Wait on a mutex, optionally timing out. */ int __wt_cond_wait(WT_SESSION_IMPL *session, WT_CONDVAR *cond, long usecs) { struct timespec ts; WT_DECL_RET; int locked; locked = 0; WT_ASSERT(session, usecs >= 0); /* Fast path if already signalled. */ if (WT_ATOMIC_ADD(cond->waiters, 1) == 0) return (0); /* * !!! * This function MUST handle a NULL session handle. */ if (session != NULL) { WT_VERBOSE_RET( session, mutex, "wait %s cond (%p)", cond->name, cond); WT_STAT_FAST_CONN_INCR(session, cond_wait); } WT_ERR(pthread_mutex_lock(&cond->mtx)); locked = 1; if (usecs > 0) { WT_ERR(__wt_epoch(session, &ts)); ts.tv_sec += (ts.tv_nsec + 1000 * usecs) / WT_BILLION; ts.tv_nsec = (ts.tv_nsec + 1000 * usecs) % WT_BILLION; ret = pthread_cond_timedwait(&cond->cond, &cond->mtx, &ts); } else ret = pthread_cond_wait(&cond->cond, &cond->mtx); /* * Check pthread_cond_wait() return for EINTR, ETIME and * ETIMEDOUT, some systems return these errors. */ if (ret == EINTR || #ifdef ETIME ret == ETIME || #endif ret == ETIMEDOUT) ret = 0; (void)WT_ATOMIC_SUB(cond->waiters, 1); err: if (locked) WT_TRET(pthread_mutex_unlock(&cond->mtx)); if (ret == 0) return (0); WT_RET_MSG(session, ret, "pthread_cond_wait"); }
/* * __lsm_unpin_chunks -- * Decrement the reference count for a set of chunks. Allowing those * chunks to be considered for deletion. */ static void __lsm_unpin_chunks(WT_SESSION_IMPL *session, WT_LSM_WORKER_COOKIE *cookie) { u_int i; for (i = 0; i < cookie->nchunks; i++) { if (cookie->chunk_array[i] == NULL) continue; WT_ASSERT(session, cookie->chunk_array[i]->refcnt > 0); (void)WT_ATOMIC_SUB(cookie->chunk_array[i]->refcnt, 1); } /* Ensure subsequent calls don't double decrement. */ cookie->nchunks = 0; }
/* * __wt_lsm_tree_get -- * get an LSM tree structure for the given name. */ void __wt_lsm_tree_release(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree) { WT_ASSERT(session, lsm_tree->refcnt > 0); (void)WT_ATOMIC_SUB(lsm_tree->refcnt, 1); }
/* * __wt_txn_refresh -- * Allocate a transaction ID and/or a snapshot. */ void __wt_txn_refresh(WT_SESSION_IMPL *session, uint64_t max_id, int get_snapshot) { WT_CONNECTION_IMPL *conn; WT_TXN *txn; WT_TXN_GLOBAL *txn_global; WT_TXN_STATE *s, *txn_state; uint64_t current_id, id, snap_min, oldest_id, prev_oldest_id; uint32_t i, n, session_cnt; int32_t count; conn = S2C(session); txn = &session->txn; txn_global = &conn->txn_global; txn_state = &txn_global->states[session->id]; prev_oldest_id = txn_global->oldest_id; current_id = snap_min = txn_global->current; /* For pure read-only workloads, avoid updates to shared state. */ if (!get_snapshot) { /* * If we are trying to update the oldest ID and it is already * equal to the current ID, there is no point scanning. */ if (prev_oldest_id == current_id) return; } else if (txn->id == max_id && txn->snapshot_count == 0 && txn->snap_min == snap_min && TXNID_LE(prev_oldest_id, snap_min)) { txn_state->snap_min = txn->snap_min; /* If nothing has changed in the meantime, we're done. */ if (txn_global->scan_count == 0 && txn_global->oldest_id == prev_oldest_id) return; } /* * We're going to scan. Increment the count of scanners to prevent the * oldest ID from moving forwards. Spin if the count is negative, * which indicates that some thread is moving the oldest ID forwards. */ do { if ((count = txn_global->scan_count) < 0) WT_PAUSE(); } while (count < 0 || !WT_ATOMIC_CAS(txn_global->scan_count, count, count + 1)); /* The oldest ID cannot change until the scan count goes to zero. */ prev_oldest_id = txn_global->oldest_id; current_id = snap_min = txn_global->current; /* If the maximum ID is constrained, so is the oldest. */ oldest_id = (max_id != WT_TXN_NONE) ? max_id : snap_min; /* Walk the array of concurrent transactions. */ WT_ORDERED_READ(session_cnt, conn->session_cnt); for (i = n = 0, s = txn_global->states; i < session_cnt; i++, s++) { /* * Ignore the ID if we are committing (indicated by max_id * being set): it is about to be released. * * Also ignore the ID if it is older than the oldest ID we saw. * This can happen if we race with a thread that is allocating * an ID -- the ID will not be used because the thread will * keep spinning until it gets a valid one. */ if ((id = s->id) != WT_TXN_NONE && id + 1 != max_id && TXNID_LE(prev_oldest_id, id)) { if (get_snapshot) txn->snapshot[n++] = id; if (TXNID_LT(id, snap_min)) snap_min = id; } /* * Ignore the session's own snap_min if we are in the process * of updating it. */ if (get_snapshot && s == txn_state) continue; /* * !!! * Note: Don't ignore snap_min values older than the previous * oldest ID. Read-uncommitted operations publish snap_min * values without incrementing scan_count to protect the global * table. See the comment in __wt_txn_cursor_op for * more details. */ if ((id = s->snap_min) != WT_TXN_NONE && TXNID_LT(id, oldest_id)) oldest_id = id; } if (TXNID_LT(snap_min, oldest_id)) oldest_id = snap_min; if (get_snapshot) { WT_ASSERT(session, TXNID_LE(prev_oldest_id, snap_min)); WT_ASSERT(session, prev_oldest_id == txn_global->oldest_id); txn_state->snap_min = snap_min; } /* * Update the last running ID if we have a much newer value or we are * forcing an update. */ if (!get_snapshot || snap_min > txn_global->last_running + 100) txn_global->last_running = snap_min; /* * Update the oldest ID if we have a newer ID and we can get exclusive * access. During normal snapshot refresh, only do this if we have a * much newer value. Once we get exclusive access, do another pass to * make sure nobody else is using an earlier ID. */ if (max_id == WT_TXN_NONE && TXNID_LT(prev_oldest_id, oldest_id) && (!get_snapshot || oldest_id - prev_oldest_id > 100) && WT_ATOMIC_CAS(txn_global->scan_count, 1, -1)) { WT_ORDERED_READ(session_cnt, conn->session_cnt); for (i = 0, s = txn_global->states; i < session_cnt; i++, s++) { if ((id = s->id) != WT_TXN_NONE && TXNID_LT(id, oldest_id)) oldest_id = id; if ((id = s->snap_min) != WT_TXN_NONE && TXNID_LT(id, oldest_id)) oldest_id = id; } if (TXNID_LT(txn_global->oldest_id, oldest_id)) txn_global->oldest_id = oldest_id; txn_global->scan_count = 0; } else { WT_ASSERT(session, txn_global->scan_count > 0); (void)WT_ATOMIC_SUB(txn_global->scan_count, 1); } if (get_snapshot) __txn_sort_snapshot(session, n, current_id); }