rwlock.c

来自「非常好的dns解析软件」· C语言 代码 · 共 809 行 · 第 1/2 页

C
809
字号
/* * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001, 2003  Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: rwlock.c,v 1.37.18.5 2005/07/12 01:22:30 marka Exp $ *//*! \file */#include <config.h>#include <stddef.h>#include <isc/atomic.h>#include <isc/magic.h>#include <isc/msgs.h>#include <isc/platform.h>#include <isc/rwlock.h>#include <isc/util.h>#define RWLOCK_MAGIC		ISC_MAGIC('R', 'W', 'L', 'k')#define VALID_RWLOCK(rwl)	ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC)#ifdef ISC_PLATFORM_USETHREADS#ifndef RWLOCK_DEFAULT_READ_QUOTA#define RWLOCK_DEFAULT_READ_QUOTA 4#endif#ifndef RWLOCK_DEFAULT_WRITE_QUOTA#define RWLOCK_DEFAULT_WRITE_QUOTA 4#endif#ifdef ISC_RWLOCK_TRACE#include <stdio.h>		/* Required for fprintf/stderr. */#include <isc/thread.h>		/* Requried for isc_thread_self(). */static voidprint_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {	fprintf(stderr,		isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,			       ISC_MSG_PRINTLOCK,			       "rwlock %p thread %lu %s(%s): %s, %u active, "			       "%u granted, %u rwaiting, %u wwaiting\n"),		rwl, isc_thread_self(), operation,		(type == isc_rwlocktype_read ? 		 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				ISC_MSG_READ, "read") :		 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				ISC_MSG_WRITE, "write")),	        (rwl->type == isc_rwlocktype_read ?		 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				ISC_MSG_READING, "reading") : 		 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				ISC_MSG_WRITING, "writing")),	        rwl->active, rwl->granted, rwl->readers_waiting,		rwl->writers_waiting);}#endifisc_result_tisc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,		unsigned int write_quota){	isc_result_t result;	REQUIRE(rwl != NULL);	/*	 * In case there's trouble initializing, we zero magic now.  If all	 * goes well, we'll set it to RWLOCK_MAGIC.	 */	rwl->magic = 0;#if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)	rwl->write_requests = 0;	rwl->write_completions = 0;	rwl->cnt_and_flag = 0;	rwl->readers_waiting = 0;	rwl->write_granted = 0;	if (read_quota != 0) {		UNEXPECTED_ERROR(__FILE__, __LINE__,				 "read quota is not supported");	}	if (write_quota == 0)		write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;	rwl->write_quota = write_quota;#else	rwl->type = isc_rwlocktype_read;	rwl->original = isc_rwlocktype_none;	rwl->active = 0;	rwl->granted = 0;	rwl->readers_waiting = 0;	rwl->writers_waiting = 0;	if (read_quota == 0)		read_quota = RWLOCK_DEFAULT_READ_QUOTA;	rwl->read_quota = read_quota;	if (write_quota == 0)		write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;	rwl->write_quota = write_quota;#endif	result = isc_mutex_init(&rwl->lock);	if (result != ISC_R_SUCCESS)		return (result);	result = isc_condition_init(&rwl->readable);	if (result != ISC_R_SUCCESS) {		UNEXPECTED_ERROR(__FILE__, __LINE__,				 "isc_condition_init(readable) %s: %s",				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,						ISC_MSG_FAILED, "failed"),				 isc_result_totext(result));		result = ISC_R_UNEXPECTED;		goto destroy_lock;	}	result = isc_condition_init(&rwl->writeable);	if (result != ISC_R_SUCCESS) {		UNEXPECTED_ERROR(__FILE__, __LINE__,				 "isc_condition_init(writeable) %s: %s",				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,						ISC_MSG_FAILED, "failed"),				 isc_result_totext(result));		result = ISC_R_UNEXPECTED;		goto destroy_rcond;	}	rwl->magic = RWLOCK_MAGIC;	return (ISC_R_SUCCESS);  destroy_rcond:	(void)isc_condition_destroy(&rwl->readable);  destroy_lock:	DESTROYLOCK(&rwl->lock);	return (result);}voidisc_rwlock_destroy(isc_rwlock_t *rwl) {	REQUIRE(VALID_RWLOCK(rwl));#if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)	REQUIRE(rwl->write_requests == rwl->write_completions &&		rwl->cnt_and_flag == 0 && rwl->readers_waiting == 0);#else	LOCK(&rwl->lock);	REQUIRE(rwl->active == 0 &&		rwl->readers_waiting == 0 &&		rwl->writers_waiting == 0);	UNLOCK(&rwl->lock);#endif	rwl->magic = 0;	(void)isc_condition_destroy(&rwl->readable);	(void)isc_condition_destroy(&rwl->writeable);	DESTROYLOCK(&rwl->lock);}#if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)/* * When some architecture-dependent atomic operations are available, * rwlock can be more efficient than the generic algorithm defined below. * The basic algorithm is described in the following URL: *   http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html * * The key is to use the following integer variables modified atomically: *   write_requests, write_completions, and cnt_and_flag. * * write_requests and write_completions act as a waiting queue for writers * in order to ensure the FIFO order.  Both variables begin with the initial * value of 0.  When a new writer tries to get a write lock, it increments * write_requests and gets the previous value of the variable as a "ticket". * When write_completions reaches the ticket number, the new writer can start * writing.  When the writer completes its work, it increments * write_completions so that another new writer can start working.  If the * write_requests is not equal to write_completions, it means a writer is now * working or waiting.  In this case, a new readers cannot start reading, or * in other words, this algorithm basically prefers writers. * * cnt_and_flag is a "lock" shared by all readers and writers.  This integer * variable is a kind of structure with two members: writer_flag (1 bit) and * reader_count (31 bits).  The writer_flag shows whether a writer is working, * and the reader_count shows the number of readers currently working or almost * ready for working.  A writer who has the current "ticket" tries to get the * lock by exclusively setting the writer_flag to 1, provided that the whole * 32-bit is 0 (meaning no readers or writers working).  On the other hand, * a new reader tries to increment the "reader_count" field provided that * the writer_flag is 0 (meaning there is no writer working). * * If some of the above operations fail, the reader or the writer sleeps * until the related condition changes.  When a working reader or writer * completes its work, some readers or writers are sleeping, and the condition * that suspended the reader or writer has changed, it wakes up the sleeping * readers or writers. * * As already noted, this algorithm basically prefers writers.  In order to * prevent readers from starving, however, the algorithm also introduces the * "writer quota" (Q).  When Q consecutive writers have completed their work, * suspending readers, the last writer will wake up the readers, even if a new * writer is waiting. * * Implementation specific note: due to the combination of atomic operations * and a mutex lock, ordering between the atomic operation and locks can be * very sensitive in some cases.  In particular, it is generally very important * to check the atomic variable that requires a reader or writer to sleep after * locking the mutex and before actually sleeping; otherwise, it could be very * likely to cause a deadlock.  For example, assume "var" is a variable * atomically modified, then the corresponding code would be: *	if (var == need_sleep) { *		LOCK(lock); *		if (var == need_sleep) *			WAIT(cond, lock); *		UNLOCK(lock); *	} * The second check is important, since "var" is protected by the atomic * operation, not by the mutex, and can be changed just before sleeping. * (The first "if" could be omitted, but this is also important in order to * make the code efficient by avoiding the use of the mutex unless it is * really necessary.) */#define WRITER_ACTIVE	0x1#define READER_INCR	0x2isc_result_tisc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {	isc_int32_t cntflag;	REQUIRE(VALID_RWLOCK(rwl));#ifdef ISC_RWLOCK_TRACE	print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				  ISC_MSG_PRELOCK, "prelock"), rwl, type);#endif	if (type == isc_rwlocktype_read) {		if (rwl->write_requests != rwl->write_completions) {			/* there is a waiting or active writer */			LOCK(&rwl->lock);			if (rwl->write_requests != rwl->write_completions) {				rwl->readers_waiting++;				WAIT(&rwl->readable, &rwl->lock);				rwl->readers_waiting--;			}			UNLOCK(&rwl->lock);		}		cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);		while (1) {			if ((rwl->cnt_and_flag & WRITER_ACTIVE) == 0)				break;			/* A writer is still working */			LOCK(&rwl->lock);			rwl->readers_waiting++;			if ((rwl->cnt_and_flag & WRITER_ACTIVE) != 0)				WAIT(&rwl->readable, &rwl->lock);			rwl->readers_waiting--;			UNLOCK(&rwl->lock);			/*			 * Typically, the reader should be able to get a lock			 * at this stage:			 *   (1) there should have been no pending writer when			 *       the reader was trying to increment the			 *       counter; otherwise, the writer should be in			 *       the waiting queue, preventing the reader from			 *       proceeding to this point.			 *   (2) once the reader increments the counter, no			 *       more writer can get a lock.			 * Still, it is possible another writer can work at			 * this point, e.g. in the following scenario:			 *   A previous writer unlocks the writer lock.			 *   This reader proceeds to point (1).			 *   A new writer appears, and gets a new lock before			 *   the reader increments the counter.			 *   The reader then increments the counter.			 *   The previous writer notices there is a waiting			 *   reader who is almost ready, and wakes it up.			 * So, the reader needs to confirm whether it can now			 * read explicitly (thus we loop).  Note that this is			 * not an infinite process, since the reader has			 * incremented the counter at this point.			 */		}		/*		 * If we are temporarily preferred to writers due to the writer		 * quota, reset the condition (race among readers doesn't		 * matter).		 */		rwl->write_granted = 0;	} else {		isc_int32_t prev_writer;		/* enter the waiting queue, and wait for our turn */		prev_writer = isc_atomic_xadd(&rwl->write_requests, 1);		while (rwl->write_completions != prev_writer) {			LOCK(&rwl->lock);			if (rwl->write_completions != prev_writer) {				WAIT(&rwl->writeable, &rwl->lock);				UNLOCK(&rwl->lock);				continue;			}			UNLOCK(&rwl->lock);			break;		}		while (1) {			cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0,						     WRITER_ACTIVE);			if (cntflag == 0)				break;			/* Another active reader or writer is working. */			LOCK(&rwl->lock);			if (rwl->cnt_and_flag != 0)				WAIT(&rwl->writeable, &rwl->lock);			UNLOCK(&rwl->lock);		}		INSIST((rwl->cnt_and_flag & WRITER_ACTIVE) != 0);		rwl->write_granted++;	}#ifdef ISC_RWLOCK_TRACE	print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				  ISC_MSG_POSTLOCK, "postlock"), rwl, type);#endif	return (ISC_R_SUCCESS);}isc_result_tisc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {	isc_int32_t cntflag;	REQUIRE(VALID_RWLOCK(rwl));#ifdef ISC_RWLOCK_TRACE	print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				  ISC_MSG_PRELOCK, "prelock"), rwl, type);#endif	if (type == isc_rwlocktype_read) {		/* If a writer is waiting or working, we fail. */		if (rwl->write_requests != rwl->write_completions)			return (ISC_R_LOCKBUSY);		/* Otherwise, be ready for reading. */		cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);		if ((cntflag & WRITER_ACTIVE) != 0) {			/*			 * A writer is working.  We lose, and cancel the read			 * request.			 */			cntflag = isc_atomic_xadd(&rwl->cnt_and_flag,						  -READER_INCR);			/*			 * If no other readers are waiting and we've suspended			 * new writers in this short period, wake them up.			 */			if (cntflag == READER_INCR &&			    rwl->write_completions != rwl->write_requests) {				LOCK(&rwl->lock);				BROADCAST(&rwl->writeable);				UNLOCK(&rwl->lock);			}						return (ISC_R_LOCKBUSY);		}	} else {		/* Try locking without entering the waiting queue. */		cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0,					     WRITER_ACTIVE);		if (cntflag != 0)			return (ISC_R_LOCKBUSY);		/*		 * XXXJT: jump into the queue, possibly breaking the writer		 * order.		 */		(void)isc_atomic_xadd(&rwl->write_completions, -1);		rwl->write_granted++;	}#ifdef ISC_RWLOCK_TRACE	print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,				  ISC_MSG_POSTLOCK, "postlock"), rwl, type);

⌨️ 快捷键说明

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