lwlock.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 695 行 · 第 1/2 页

C
695
字号
		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;		/* 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++;				mustwait = false;			}			else				mustwait = true;		}		else		{			if (lock->exclusive == 0)			{				lock->shared++;				mustwait = false;			}			else				mustwait = true;		}		if (!mustwait)			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);		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_STATS		block_counts[lockid]++;#endif		PG_TRACE2(lwlock__startwait, lockid, mode);		for (;;)		{			/* "false" means cannot accept cancel/die interrupt here. */			PGSemaphoreLock(&proc->sem, false);			if (!proc->lwWaiting)				break;			extraWaits++;		}		PG_TRACE2(lwlock__endwait, lockid, mode);		LOG_LWDEBUG("LWLockAcquire", lockid, "awakened");		/* 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);	/* Add lock to list of locks held by this backend */	held_lwlocks[num_held_lwlocks++] = lockid;	/*	 * Fix the process wait semaphore's count for any absorbed wakeups.	 */	while (extraWaits-- > 0)		PGSemaphoreUnlock(&proc->sem);}/* * 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. */boolLWLockConditionalAcquire(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++;			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	{		/* Add lock to list of locks held by this backend */		held_lwlocks[num_held_lwlocks++] = lockid;		PG_TRACE2(lwlock__condacquire, lockid, mode);	}	return !mustwait;}/* * LWLockRelease - release a previously acquired lock */voidLWLockRelease(LWLockId lockid){	volatile LWLock *lock = &(LWLockArray[lockid].lock);	PGPROC	   *head;	PGPROC	   *proc;	int			i;	PRINT_LWDEBUG("LWLockRelease", lockid, lock);	/*	 * Remove lock from list of locks held.  Usually, but not always, it will	 * be the latest-acquired lock; so search array backwards.	 */	for (i = num_held_lwlocks; --i >= 0;)	{		if (lockid == held_lwlocks[i])			break;	}	if (i < 0)		elog(ERROR, "lock %d is not held", (int) lockid);	num_held_lwlocks--;	for (; i < num_held_lwlocks; i++)		held_lwlocks[i] = held_lwlocks[i + 1];	/* Acquire mutex.  Time spent holding mutex should be short! */	SpinLockAcquire(&lock->mutex);	/* Release my hold on lock */	if (lock->exclusive > 0)		lock->exclusive--;	else	{		Assert(lock->shared > 0);		lock->shared--;	}	/*	 * See if I need to awaken any waiters.  If I released a non-last shared	 * hold, there cannot be anything to do.  Also, do not awaken any waiters	 * if someone has already awakened waiters that haven't yet acquired the	 * lock.	 */	head = lock->head;	if (head != NULL)	{		if (lock->exclusive == 0 && lock->shared == 0 && lock->releaseOK)		{			/*			 * Remove the to-be-awakened PGPROCs from the queue.  If the front			 * waiter wants exclusive lock, awaken him only. Otherwise awaken			 * as many waiters as want shared access.			 */			proc = head;			if (!proc->lwExclusive)			{				while (proc->lwWaitLink != NULL &&					   !proc->lwWaitLink->lwExclusive)					proc = proc->lwWaitLink;			}			/* proc is now the last PGPROC to be released */			lock->head = proc->lwWaitLink;			proc->lwWaitLink = NULL;			/* prevent additional wakeups until retryer gets to run */			lock->releaseOK = false;		}		else		{			/* lock is still held, can't awaken anything */			head = NULL;		}	}	/* We are done updating shared state of the lock itself. */	SpinLockRelease(&lock->mutex);	PG_TRACE1(lwlock__release, lockid);	/*	 * Awaken any waiters I removed from the queue.	 */	while (head != NULL)	{		LOG_LWDEBUG("LWLockRelease", lockid, "release waiter");		proc = head;		head = proc->lwWaitLink;		proc->lwWaitLink = NULL;		proc->lwWaiting = false;		PGSemaphoreUnlock(&proc->sem);	}	/*	 * Now okay to allow cancel/die interrupts.	 */	RESUME_INTERRUPTS();}/* * LWLockReleaseAll - release all currently-held locks * * Used to clean up after ereport(ERROR). An important difference between this * function and retail LWLockRelease calls is that InterruptHoldoffCount is * unchanged by this operation.  This is necessary since InterruptHoldoffCount * has been set to an appropriate level earlier in error recovery. We could * decrement it below zero if we allow it to drop for each released lock! */voidLWLockReleaseAll(void){	while (num_held_lwlocks > 0)	{		HOLD_INTERRUPTS();		/* match the upcoming RESUME_INTERRUPTS */		LWLockRelease(held_lwlocks[num_held_lwlocks - 1]);	}}/* * LWLockHeldByMe - test whether my process currently holds a lock * * This is meant as debug support only.  We do not distinguish whether the * lock is held shared or exclusive. */boolLWLockHeldByMe(LWLockId lockid){	int			i;	for (i = 0; i < num_held_lwlocks; i++)	{		if (held_lwlocks[i] == lockid)			return true;	}	return false;}

⌨️ 快捷键说明

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