/* * Check if time slice since last check-for-interrupts (CFI) has been exceeded */ void TimeSliceCheck(const char *file, int line) { Assert(gp_test_time_slice); Assert(IsValidReportLevel(gp_test_time_slice_report_level)); struct rusage ru; int32 elapsedMs = 0; /* CFI is disabled inside critical sections */ if (0 != InterruptHoldoffCount || 0 != CritSectionCount) { return; } /* get current user time */ if (getrusage(RUSAGE_SELF, &ru) != 0) { elog(ERROR, "Time slicer: Failed to retrieve user time"); } elapsedMs = timeElapsedMs(&ru.ru_utime, &userTimeLastCFI); Assert(0 <= elapsedMs); /* check elapsed time since last CFI */ if (gp_test_time_slice_interval < elapsedMs) { void *stackAddressesCurrent[MAX_FRAME_DEPTH]; uint32 stackDepthCurrent = gp_backtrace(stackAddressesCurrent, MAX_FRAME_DEPTH); char *stackTraceLastCFI = gp_stacktrace(stackAddressesLastCFI, stackDepthLastCFI); char *stackTraceCurrent = gp_stacktrace(stackAddressesCurrent, stackDepthCurrent); /* report time slice violation error */ ereport(gp_test_time_slice_report_level, (errmsg("Time slice of %d ms exceeded at (%s:%d), last CFI before %d ms.\n" "Stack trace of last CFI:\n%s\n" "Current stack trace:\n%s\n", gp_test_time_slice_interval, file, line, elapsedMs, stackTraceLastCFI, stackTraceCurrent))); } /* reset time slice */ userTimeLastCFI = ru.ru_utime; stackDepthLastCFI = gp_backtrace(stackAddressesLastCFI, MAX_FRAME_DEPTH); }
/* * check if lightweight lock(s) are held; * print stack trace where lock(s) got acquired and error out; */ void LWLockHeldDetect(const void *pv, int lockmode) { Assert(gp_test_deadlock_hazard); Assert(IsValidReportLevel(gp_test_deadlock_hazard_report_level)); const LOCKTAG *locktag = (const LOCKTAG *) pv; if (0 < LWLocksHeld()) { void *stackAddressesCurrent[MAX_FRAME_DEPTH]; uint32 stackDepthCurrent = gp_backtrace(stackAddressesCurrent, MAX_FRAME_DEPTH); char *stackTraceCurrent = gp_stacktrace(stackAddressesCurrent, stackDepthCurrent); const char *stackTraces = LWLocksHeldStackTraces(); Assert(NULL != stackTraces); /* report time slice violation error */ ereport(gp_test_deadlock_hazard_report_level, (errmsg("Attempting to acquire database lock (%s:%d:%d:%d:%d) while holding lightweight lock (%d:%p).\n" "Stack trace(s) where lightweight lock(s) got acquired:\n%s\n" "Current stack trace:\n%s\n", GetLockmodeName(locktag->locktag_lockmethodid, lockmode), locktag->locktag_field1, locktag->locktag_field2, locktag->locktag_field3, locktag->locktag_field4, LWLockHeldLatestId(), LWLockHeldLatestCaller(), stackTraces, stackTraceCurrent))); } }
/* * Reset time slice */ void TimeSliceReset() { struct rusage ru; if (0 != getrusage(RUSAGE_SELF, &ru)) { elog(ERROR, "Time slicer: Failed to retrieve user time"); } userTimeLastCFI = ru.ru_utime; stackDepthLastCFI = gp_backtrace(stackAddressesLastCFI, MAX_FRAME_DEPTH); }
/* * LWLockConditionalAcquire - acquire a lightweight lock in the specified mode * * If the lock is not available, return FALSE with no side-effects. * * If successful, cancel/die interrupts are held off until lock release. */ bool LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode) { volatile LWLock *lock = &(LWLockArray[lockid].lock); bool mustwait; PRINT_LWDEBUG("LWLockConditionalAcquire", lockid, lock); /* Ensure we will have room to remember the lock */ if (num_held_lwlocks >= MAX_SIMUL_LWLOCKS) elog(ERROR, "too many LWLocks taken"); /* * Lock out cancel/die interrupts until we exit the code section protected * by the LWLock. This ensures that interrupts will not interfere with * manipulations of data structures in shared memory. */ HOLD_INTERRUPTS(); /* Acquire mutex. Time spent holding mutex should be short! */ SpinLockAcquire(&lock->mutex); /* If I can get the lock, do so quickly. */ if (mode == LW_EXCLUSIVE) { if (lock->exclusive == 0 && lock->shared == 0) { lock->exclusive++; lock->exclusivePid = MyProcPid; mustwait = false; } else mustwait = true; } else { if (lock->exclusive == 0) { lock->shared++; mustwait = false; } else mustwait = true; } /* We are done updating shared state of the lock itself. */ SpinLockRelease(&lock->mutex); if (mustwait) { /* Failed to get lock, so release interrupt holdoff */ RESUME_INTERRUPTS(); LOG_LWDEBUG("LWLockConditionalAcquire", lockid, "failed"); PG_TRACE2(lwlock__condacquire__fail, lockid, mode); } else { #ifdef LWLOCK_TRACE_MIRROREDLOCK if (lockid == MirroredLock) elog(LOG, "LWLockConditionalAcquire: MirroredLock by PID %u in held_lwlocks[%d] %s", MyProcPid, num_held_lwlocks, (mode == LW_EXCLUSIVE ? "Exclusive" : "Shared")); #endif #ifdef USE_TEST_UTILS_X86 /* keep track of stack trace where lock got acquired */ held_lwlocks_depth[num_held_lwlocks] = gp_backtrace(held_lwlocks_addresses[num_held_lwlocks], MAX_FRAME_DEPTH); #endif /* USE_TEST_UTILS_X86 */ /* Add lock to list of locks held by this backend */ held_lwlocks_exclusive[num_held_lwlocks] = (mode == LW_EXCLUSIVE); held_lwlocks[num_held_lwlocks++] = lockid; PG_TRACE2(lwlock__condacquire, lockid, mode); } return !mustwait; }
/* * LWLockAcquire - acquire a lightweight lock in the specified mode * * If the lock is not available, sleep until it is. * * Side effect: cancel/die interrupts are held off until lock release. */ void LWLockAcquire(LWLockId lockid, LWLockMode mode) { volatile LWLock *lock = &(LWLockArray[lockid].lock); PGPROC *proc = MyProc; bool retry = false; int extraWaits = 0; PRINT_LWDEBUG("LWLockAcquire", lockid, lock); #ifdef LWLOCK_STATS /* Set up local count state first time through in a given process */ if (counts_for_pid != MyProcPid) { int *LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int)); int numLocks = LWLockCounter[1]; sh_acquire_counts = calloc(numLocks, sizeof(int)); ex_acquire_counts = calloc(numLocks, sizeof(int)); block_counts = calloc(numLocks, sizeof(int)); if(!sh_acquire_counts || !ex_acquire_counts || !block_counts) ereport(ERROR, errcode(ERRCODE_OUT_OF_MEMORY), errmsg("LWLockAcquire failed: out of memory")); counts_for_pid = MyProcPid; on_shmem_exit(print_lwlock_stats, 0); } /* Count lock acquisition attempts */ if (mode == LW_EXCLUSIVE) ex_acquire_counts[lockid]++; else sh_acquire_counts[lockid]++; #endif /* LWLOCK_STATS */ /* * We can't wait if we haven't got a PGPROC. This should only occur * during bootstrap or shared memory initialization. Put an Assert here * to catch unsafe coding practices. */ Assert(!(proc == NULL && IsUnderPostmaster)); /* Ensure we will have room to remember the lock */ if (num_held_lwlocks >= MAX_SIMUL_LWLOCKS) elog(ERROR, "too many LWLocks taken"); /* * Lock out cancel/die interrupts until we exit the code section protected * by the LWLock. This ensures that interrupts will not interfere with * manipulations of data structures in shared memory. */ HOLD_INTERRUPTS(); /* * Loop here to try to acquire lock after each time we are signaled by * LWLockRelease. * * NOTE: it might seem better to have LWLockRelease actually grant us the * lock, rather than retrying and possibly having to go back to sleep. But * in practice that is no good because it means a process swap for every * lock acquisition when two or more processes are contending for the same * lock. Since LWLocks are normally used to protect not-very-long * sections of computation, a process needs to be able to acquire and * release the same lock many times during a single CPU time slice, even * in the presence of contention. The efficiency of being able to do that * outweighs the inefficiency of sometimes wasting a process dispatch * cycle because the lock is not free when a released waiter finally gets * to run. See pgsql-hackers archives for 29-Dec-01. */ for (;;) { bool mustwait; int c; /* Acquire mutex. Time spent holding mutex should be short! */ SpinLockAcquire(&lock->mutex); /* If retrying, allow LWLockRelease to release waiters again */ if (retry) lock->releaseOK = true; /* If I can get the lock, do so quickly. */ if (mode == LW_EXCLUSIVE) { if (lock->exclusive == 0 && lock->shared == 0) { lock->exclusive++; lock->exclusivePid = MyProcPid; mustwait = false; } else mustwait = true; } else { if (lock->exclusive == 0) { lock->shared++; mustwait = false; } else mustwait = true; } if (!mustwait) { LOG_LWDEBUG("LWLockAcquire", lockid, "acquired!"); break; /* got the lock */ } /* * Add myself to wait queue. * * If we don't have a PGPROC structure, there's no way to wait. This * should never occur, since MyProc should only be null during shared * memory initialization. */ if (proc == NULL) elog(PANIC, "cannot wait without a PGPROC structure"); proc->lwWaiting = true; proc->lwExclusive = (mode == LW_EXCLUSIVE); lwWaitingLockId = lockid; proc->lwWaitLink = NULL; if (lock->head == NULL) lock->head = proc; else lock->tail->lwWaitLink = proc; lock->tail = proc; /* Can release the mutex now */ SpinLockRelease(&lock->mutex); /* * Wait until awakened. * * Since we share the process wait semaphore with the regular lock * manager and ProcWaitForSignal, and we may need to acquire an LWLock * while one of those is pending, it is possible that we get awakened * for a reason other than being signaled by LWLockRelease. If so, * loop back and wait again. Once we've gotten the LWLock, * re-increment the sema by the number of additional signals received, * so that the lock manager or signal manager will see the received * signal when it next waits. */ LOG_LWDEBUG("LWLockAcquire", lockid, "waiting"); #ifdef LWLOCK_TRACE_MIRROREDLOCK if (lockid == MirroredLock) elog(LOG, "LWLockAcquire: waiting for MirroredLock (PID %u)", MyProcPid); #endif #ifdef LWLOCK_STATS block_counts[lockid]++; #endif for (c = 0; c < num_held_lwlocks; c++) { if (held_lwlocks[c] == lockid) elog(PANIC, "Waiting on lock already held!"); } PG_TRACE2(lwlock__startwait, lockid, mode); for (;;) { /* "false" means cannot accept cancel/die interrupt here. */ #ifndef LOCK_DEBUG PGSemaphoreLock(&proc->sem, false); #else LWLockTryLockWaiting(proc, lockid, mode); #endif if (!proc->lwWaiting) break; extraWaits++; } PG_TRACE2(lwlock__endwait, lockid, mode); LOG_LWDEBUG("LWLockAcquire", lockid, "awakened"); #ifdef LWLOCK_TRACE_MIRROREDLOCK if (lockid == MirroredLock) elog(LOG, "LWLockAcquire: awakened for MirroredLock (PID %u)", MyProcPid); #endif /* Now loop back and try to acquire lock again. */ retry = true; } /* We are done updating shared state of the lock itself. */ SpinLockRelease(&lock->mutex); PG_TRACE2(lwlock__acquire, lockid, mode); #ifdef LWLOCK_TRACE_MIRROREDLOCK if (lockid == MirroredLock) elog(LOG, "LWLockAcquire: MirroredLock by PID %u in held_lwlocks[%d] %s", MyProcPid, num_held_lwlocks, (mode == LW_EXCLUSIVE ? "Exclusive" : "Shared")); #endif #ifdef USE_TEST_UTILS_X86 /* keep track of stack trace where lock got acquired */ held_lwlocks_depth[num_held_lwlocks] = gp_backtrace(held_lwlocks_addresses[num_held_lwlocks], MAX_FRAME_DEPTH); #endif /* USE_TEST_UTILS_X86 */ /* Add lock to list of locks held by this backend */ held_lwlocks_exclusive[num_held_lwlocks] = (mode == LW_EXCLUSIVE); held_lwlocks[num_held_lwlocks++] = lockid; /* * Fix the process wait semaphore's count for any absorbed wakeups. */ while (extraWaits-- > 0) PGSemaphoreUnlock(&proc->sem); }