⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 proc.c

📁 postgresql8.3.4源码,开源数据库
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* Turn off the deadlock timer, if it's still running (see ProcSleep) */	disable_sig_alarm(false);	/* Unlink myself from the wait queue, if on it (might not be anymore!) */	partitionLock = LockHashPartitionLock(lockAwaited->hashcode);	LWLockAcquire(partitionLock, LW_EXCLUSIVE);	if (MyProc->links.next != INVALID_OFFSET)	{		/* We could not have been granted the lock yet */		RemoveFromWaitQueue(MyProc, lockAwaited->hashcode);	}	else	{		/*		 * Somebody kicked us off the lock queue already.  Perhaps they		 * granted us the lock, or perhaps they detected a deadlock. If they		 * did grant us the lock, we'd better remember it in our local lock		 * table.		 */		if (MyProc->waitStatus == STATUS_OK)			GrantAwaitedLock();	}	lockAwaited = NULL;	LWLockRelease(partitionLock);	/*	 * We used to do PGSemaphoreReset() here to ensure that our proc's wait	 * semaphore is reset to zero.	This prevented a leftover wakeup signal	 * from remaining in the semaphore if someone else had granted us the lock	 * we wanted before we were able to remove ourselves from the wait-list.	 * However, now that ProcSleep loops until waitStatus changes, a leftover	 * wakeup signal isn't harmful, and it seems not worth expending cycles to	 * get rid of a signal that most likely isn't there.	 */}/* * ProcReleaseLocks() -- release locks associated with current transaction *			at main transaction commit or abort * * At main transaction commit, we release all locks except session locks. * At main transaction abort, we release all locks including session locks; * this lets us clean up after a VACUUM FULL failure. * * At subtransaction commit, we don't release any locks (so this func is not * needed at all); we will defer the releasing to the parent transaction. * At subtransaction abort, we release all locks held by the subtransaction; * this is implemented by retail releasing of the locks under control of * the ResourceOwner mechanism. * * Note that user locks are not released in any case. */voidProcReleaseLocks(bool isCommit){	if (!MyProc)		return;	/* If waiting, get off wait queue (should only be needed after error) */	LockWaitCancel();	/* Release locks */	LockReleaseAll(DEFAULT_LOCKMETHOD, !isCommit);}/* * RemoveProcFromArray() -- Remove this process from the shared ProcArray. */static voidRemoveProcFromArray(int code, Datum arg){	Assert(MyProc != NULL);	ProcArrayRemove(MyProc, InvalidTransactionId);}/* * ProcKill() -- Destroy the per-proc data structure for *		this process. Release any of its held LW locks. */static voidProcKill(int code, Datum arg){	/* use volatile pointer to prevent code rearrangement */	volatile PROC_HDR *procglobal = ProcGlobal;	Assert(MyProc != NULL);	/*	 * Release any LW locks I am holding.  There really shouldn't be any, but	 * it's cheap to check again before we cut the knees off the LWLock	 * facility by releasing our PGPROC ...	 */	LWLockReleaseAll();	SpinLockAcquire(ProcStructLock);	/* Return PGPROC structure (and semaphore) to freelist */	if (IsAutoVacuumWorkerProcess())	{		MyProc->links.next = procglobal->autovacFreeProcs;		procglobal->autovacFreeProcs = MAKE_OFFSET(MyProc);	}	else	{		MyProc->links.next = procglobal->freeProcs;		procglobal->freeProcs = MAKE_OFFSET(MyProc);	}	/* PGPROC struct isn't mine anymore */	MyProc = NULL;	/* Update shared estimate of spins_per_delay */	procglobal->spins_per_delay = update_spins_per_delay(procglobal->spins_per_delay);	SpinLockRelease(ProcStructLock);	/* wake autovac launcher if needed -- see comments in FreeWorkerInfo */	if (AutovacuumLauncherPid != 0)		kill(AutovacuumLauncherPid, SIGUSR1);}/* * AuxiliaryProcKill() -- Cut-down version of ProcKill for auxiliary *		processes (bgwriter, etc).	The PGPROC and sema are not released, only *		marked as not-in-use. */static voidAuxiliaryProcKill(int code, Datum arg){	int			proctype = DatumGetInt32(arg);	PGPROC	   *auxproc;	Assert(proctype >= 0 && proctype < NUM_AUXILIARY_PROCS);	auxproc = &AuxiliaryProcs[proctype];	Assert(MyProc == auxproc);	/* Release any LW locks I am holding (see notes above) */	LWLockReleaseAll();	SpinLockAcquire(ProcStructLock);	/* Mark auxiliary proc no longer in use */	MyProc->pid = 0;	/* PGPROC struct isn't mine anymore */	MyProc = NULL;	/* Update shared estimate of spins_per_delay */	ProcGlobal->spins_per_delay = update_spins_per_delay(ProcGlobal->spins_per_delay);	SpinLockRelease(ProcStructLock);}/* * ProcQueue package: routines for putting processes to sleep *		and  waking them up *//* * ProcQueueAlloc -- alloc/attach to a shared memory process queue * * Returns: a pointer to the queue or NULL * Side Effects: Initializes the queue if we allocated one */#ifdef NOT_USEDPROC_QUEUE *ProcQueueAlloc(char *name){	bool		found;	PROC_QUEUE *queue = (PROC_QUEUE *)	ShmemInitStruct(name, sizeof(PROC_QUEUE), &found);	if (!queue)		return NULL;	if (!found)		ProcQueueInit(queue);	return queue;}#endif/* * ProcQueueInit -- initialize a shared memory process queue */voidProcQueueInit(PROC_QUEUE *queue){	SHMQueueInit(&(queue->links));	queue->size = 0;}/* * ProcSleep -- put a process to sleep on the specified lock * * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process (under all XIDs). * * The lock table's partition lock must be held at entry, and will be held * at exit. * * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock). * * ASSUME: that no one will fiddle with the queue until after *		we release the partition lock. * * NOTES: The process queue is now a priority queue for locking. * * P() on the semaphore should put us to sleep.  The process * semaphore is normally zero, so when we try to acquire it, we sleep. */intProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable){	LOCKMODE	lockmode = locallock->tag.mode;	LOCK	   *lock = locallock->lock;	PROCLOCK   *proclock = locallock->proclock;	uint32		hashcode = locallock->hashcode;	LWLockId	partitionLock = LockHashPartitionLock(hashcode);	PROC_QUEUE *waitQueue = &(lock->waitProcs);	LOCKMASK	myHeldLocks = MyProc->heldLocks;	bool		early_deadlock = false;	bool		allow_autovacuum_cancel = true;	int			myWaitStatus;	PGPROC	   *proc;	int			i;	/*	 * Determine where to add myself in the wait queue.	 *	 * Normally I should go at the end of the queue.  However, if I already	 * hold locks that conflict with the request of any previous waiter, put	 * myself in the queue just in front of the first such waiter. This is not	 * a necessary step, since deadlock detection would move me to before that	 * waiter anyway; but it's relatively cheap to detect such a conflict	 * immediately, and avoid delaying till deadlock timeout.	 *	 * Special case: if I find I should go in front of some waiter, check to	 * see if I conflict with already-held locks or the requests before that	 * waiter.	If not, then just grant myself the requested lock immediately.	 * This is the same as the test for immediate grant in LockAcquire, except	 * we are only considering the part of the wait queue before my insertion	 * point.	 */	if (myHeldLocks != 0)	{		LOCKMASK	aheadRequests = 0;		proc = (PGPROC *) MAKE_PTR(waitQueue->links.next);		for (i = 0; i < waitQueue->size; i++)		{			/* Must he wait for me? */			if (lockMethodTable->conflictTab[proc->waitLockMode] & myHeldLocks)			{				/* Must I wait for him ? */				if (lockMethodTable->conflictTab[lockmode] & proc->heldLocks)				{					/*					 * Yes, so we have a deadlock.	Easiest way to clean up					 * correctly is to call RemoveFromWaitQueue(), but we					 * can't do that until we are *on* the wait queue. So, set					 * a flag to check below, and break out of loop.  Also,					 * record deadlock info for later message.					 */					RememberSimpleDeadLock(MyProc, lockmode, lock, proc);					early_deadlock = true;					break;				}				/* I must go before this waiter.  Check special case. */				if ((lockMethodTable->conflictTab[lockmode] & aheadRequests) == 0 &&					LockCheckConflicts(lockMethodTable,									   lockmode,									   lock,									   proclock,									   MyProc) == STATUS_OK)				{					/* Skip the wait and just grant myself the lock. */					GrantLock(lock, proclock, lockmode);					GrantAwaitedLock();					return STATUS_OK;				}				/* Break out of loop to put myself before him */				break;			}			/* Nope, so advance to next waiter */			aheadRequests |= LOCKBIT_ON(proc->waitLockMode);			proc = (PGPROC *) MAKE_PTR(proc->links.next);		}		/*		 * If we fall out of loop normally, proc points to waitQueue head, so		 * we will insert at tail of queue as desired.		 */	}	else	{		/* I hold no locks, so I can't push in front of anyone. */		proc = (PGPROC *) &(waitQueue->links);	}	/*	 * Insert self into queue, ahead of the given proc (or at tail of queue).	 */	SHMQueueInsertBefore(&(proc->links), &(MyProc->links));	waitQueue->size++;	lock->waitMask |= LOCKBIT_ON(lockmode);	/* Set up wait information in PGPROC object, too */	MyProc->waitLock = lock;	MyProc->waitProcLock = proclock;	MyProc->waitLockMode = lockmode;	MyProc->waitStatus = STATUS_WAITING;	/*	 * If we detected deadlock, give up without waiting.  This must agree with	 * CheckDeadLock's recovery code, except that we shouldn't release the	 * semaphore since we haven't tried to lock it yet.	 */	if (early_deadlock)	{		RemoveFromWaitQueue(MyProc, hashcode);		return STATUS_ERROR;	}	/* mark that we are waiting for a lock */	lockAwaited = locallock;	/*	 * Release the lock table's partition lock.	 *	 * NOTE: this may also cause us to exit critical-section state, possibly	 * allowing a cancel/die interrupt to be accepted. This is OK because we	 * have recorded the fact that we are waiting for a lock, and so	 * LockWaitCancel will clean up if cancel/die happens.	 */	LWLockRelease(partitionLock);	/* Reset deadlock_state before enabling the signal handler */	deadlock_state = DS_NOT_YET_CHECKED;	/*	 * Set timer so we can wake up after awhile and check for a deadlock. If a	 * deadlock is detected, the handler releases the process's semaphore and	 * sets MyProc->waitStatus = STATUS_ERROR, allowing us to know that we	 * must report failure rather than success.	 *	 * By delaying the check until we've waited for a bit, we can avoid	 * running the rather expensive deadlock-check code in most cases.	 */	if (!enable_sig_alarm(DeadlockTimeout, false))		elog(FATAL, "could not set timer for process wakeup");	/*	 * If someone wakes us between LWLockRelease and PGSemaphoreLock,	 * PGSemaphoreLock will not block.	The wakeup is "saved" by the semaphore	 * implementation.	While this is normally good, there are cases where a	 * saved wakeup might be leftover from a previous operation (for example,	 * we aborted ProcWaitForSignal just before someone did ProcSendSignal).	 * So, loop to wait again if the waitStatus shows we haven't been granted	 * nor denied the lock yet.	 *	 * We pass interruptOK = true, which eliminates a window in which	 * cancel/die interrupts would be held off undesirably.  This is a promise	 * that we don't mind losing control to a cancel/die interrupt here.  We	 * don't, because we have no shared-state-change work to do after being	 * granted the lock (the grantor did it all).  We do have to worry about	 * updating the locallock table, but if we lose control to an error,	 * LockWaitCancel will fix that up.	 */	do	{		PGSemaphoreLock(&MyProc->sem, true);		/*		 * waitStatus could change from STATUS_WAITING to something else		 * asynchronously.	Read it just once per loop to prevent surprising		 * behavior (such as missing log messages).		 */		myWaitStatus = MyProc->waitStatus;		/*		 * If we are not deadlocked, but are waiting on an autovacuum-induced		 * task, send a signal to interrupt it.		 */		if (deadlock_state == DS_BLOCKED_BY_AUTOVACUUM && allow_autovacuum_cancel)		{			PGPROC	   *autovac = GetBlockingAutoVacuumPgproc();			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);			/*			 * Only do it if the worker is not working to protect against Xid			 * wraparound.			 */			if ((autovac != NULL) &&				(autovac->vacuumFlags & PROC_IS_AUTOVACUUM) &&				!(autovac->vacuumFlags & PROC_VACUUM_FOR_WRAPAROUND))			{				int			pid = autovac->pid;				elog(DEBUG2, "sending cancel to blocking autovacuum pid = %d",					 pid);				/* don't hold the lock across the kill() syscall */				LWLockRelease(ProcArrayLock);				/* send the autovacuum worker Back to Old Kent Road */				if (kill(pid, SIGINT) < 0)				{					/* Just a warning to allow multiple callers */					ereport(WARNING,							(errmsg("could not send signal to process %d: %m",									pid)));				}			}			else				LWLockRelease(ProcArrayLock);			/* prevent signal from being resent more than once */			allow_autovacuum_cancel = false;		}		/*		 * If awoken after the deadlock check interrupt has run, and		 * log_lock_waits is on, then report about the wait.		 */		if (log_lock_waits && deadlock_state != DS_NOT_YET_CHECKED)		{			StringInfoData buf;			const char *modename;			long		secs;			int			usecs;			long		msecs;			initStringInfo(&buf);			DescribeLockTag(&buf, &locallock->tag.lock);			modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,									   lockmode);			TimestampDifference(timeout_start_time, GetCurrentTimestamp(),								&secs, &usecs);			msecs = secs * 1000 + usecs / 1000;			usecs = usecs % 1000;			if (deadlock_state == DS_SOFT_DEADLOCK)				ereport(LOG,						(errmsg("process %d avoided deadlock for %s on %s by rearranging queue order after %ld.%03d ms",							  MyProcPid, modename, buf.data, msecs, usecs)));			else if (deadlock_state == DS_HARD_DEADLOCK)			{				/*				 * This message is a bit redundant with the error that will be				 * reported subsequently, but in some cases the error report				 * might not make it to the log (eg, if it's caught by an				 * exception handler), and we want to ensure all long-wait				 * events get logged.				 */				ereport(LOG,						(errmsg("process %d detected deadlock while waiting for %s on %s after %ld.%03d ms",							  MyProcPid, modename, buf.data, msecs, usecs)));			}			if (myWaitStatus == STATUS_WAITING)				ereport(LOG,						(errmsg("process %d still waiting for %s on %s after %ld.%03d ms",							  MyProcPid, modename, buf.data, msecs, usecs)));			else if (myWaitStatus == STATUS_OK)				ereport(LOG,					(errmsg("process %d acquired %s on %s after %ld.%03d ms",							MyProcPid, modename, buf.data, msecs, usecs)));			else			{				Assert(myWaitStatus == STATUS_ERROR);				/*				 * Currently, the deadlock checker always kicks its own				 * process, which means that we'll only see STATUS_ERROR when				 * deadlock_state == DS_HARD_DEADLOCK, and there's no need to				 * print redundant messages.  But for completeness and				 * future-proofing, print a message if it looks like someone				 * else kicked us off the lock.				 */				if (deadlock_state != DS_HARD_DEADLOCK)					ereport(LOG,							(errmsg("process %d failed to acquire %s on %s after %ld.%03d ms",							  MyProcPid, modename, buf.data, msecs, usecs)));			}			/*			 * At this point we might still need to wait for the lock. Reset			 * state so we don't print the above messages again.			 */			deadlock_state = DS_NO_DEADLOCK;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -