745 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			745 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*  $Id$
 | ||
|  | 
 | ||
|  |     Part of the SWI-Prolog Semweb package | ||
|  | 
 | ||
|  |     Author:        Jan Wielemaker | ||
|  |     E-mail:        wielemak@science.uva.nl | ||
|  |     WWW:           http://www.swi-prolog.org
 | ||
|  |     Copyright (C): 2006, University of Amsterdam | ||
|  | 
 | ||
|  |     This program is free software; you can redistribute it and/or | ||
|  |     modify it under the terms of the GNU General Public License | ||
|  |     as published by the Free Software Foundation; either version 2 | ||
|  |     of the License, or (at your option) any later version. | ||
|  | 
 | ||
|  |     This program is distributed in the hope that it will be useful, | ||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|  |     GNU General Public License for more details. | ||
|  | 
 | ||
|  |     You should have received a copy of the GNU Lesser General Public | ||
|  |     License along with this library; if not, write to the Free Software | ||
|  |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||
|  | */ | ||
|  | 
 | ||
|  | #include <SWI-Stream.h>
 | ||
|  | #include <SWI-Prolog.h>
 | ||
|  | #include "lock.h"
 | ||
|  | #include "debug.h"
 | ||
|  | #include <assert.h>
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | static int | ||
|  | permission_error(const char *op, const char *type, const char *obj, | ||
|  | 		 const char *msg) | ||
|  | { term_t ex, ctx; | ||
|  | 
 | ||
|  |   if ( !(ex = PL_new_term_ref()) || | ||
|  |        !(ctx = PL_new_term_ref()) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( msg ) | ||
|  |   { if ( !PL_unify_term(ctx, PL_FUNCTOR_CHARS, "context", 2, | ||
|  | 			       PL_VARIABLE, | ||
|  | 			       PL_CHARS, msg) ) | ||
|  |       return FALSE; | ||
|  |   } | ||
|  | 
 | ||
|  |   if ( !PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 2, | ||
|  | 		      PL_FUNCTOR_CHARS, "permission_error", 3, | ||
|  | 		        PL_CHARS, op, | ||
|  | 		        PL_CHARS, type, | ||
|  | 		        PL_CHARS, obj, | ||
|  | 		      PL_TERM, ctx) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   return PL_raise_exception(ex); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | #ifdef _REENTRANT
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	   COMMON STUFF		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | static void | ||
|  | register_reader(rwlock *lock, int tid) | ||
|  | { while ( tid >= lock->thread_max ) | ||
|  |   { size_t osize = lock->thread_max*sizeof(lock->read_by_thread[0]); | ||
|  | 
 | ||
|  |     lock->read_by_thread = realloc(lock->read_by_thread, osize*2); | ||
|  |     memset((char*)lock->read_by_thread+osize, 0, osize); | ||
|  |     lock->thread_max *= 2; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->read_by_thread[tid]++; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	 WINDOWS VERSION	* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | #ifdef __WINDOWS__
 | ||
|  | 
 | ||
|  | #include <windows.h>
 | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | This  is  a  slightly  simplified  version  of  the  condition  variable | ||
|  | emulation used in SWI-Prolog. We have   deleted the broadcast facilities | ||
|  | of the CVs as this is not used in this code. | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | static int | ||
|  | win32_cond_init(win32_cond_t *cv) | ||
|  | { cv->events[SIGNAL]    = CreateEvent(NULL, FALSE, FALSE, NULL); | ||
|  |   cv->waiters = 0; | ||
|  | 
 | ||
|  |   return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | win32_cond_destroy(win32_cond_t *cv) | ||
|  | { CloseHandle(cv->events[SIGNAL]); | ||
|  | 
 | ||
|  |   return 0; | ||
|  | } | ||
|  | 
 | ||
|  | #define WAIT_INTR (-1)
 | ||
|  | 
 | ||
|  | static int | ||
|  | win32_cond_wait(win32_cond_t *cv, | ||
|  | 		CRITICAL_SECTION *external_mutex) | ||
|  | { int rc; | ||
|  | 
 | ||
|  |   cv->waiters++; | ||
|  | 
 | ||
|  |   LeaveCriticalSection(external_mutex); | ||
|  |   rc = MsgWaitForMultipleObjects(1, | ||
|  | 				 cv->events, | ||
|  | 				 FALSE,	/* wait for either event */ | ||
|  | 				 INFINITE, | ||
|  | 				 QS_ALLINPUT); | ||
|  |   if ( rc == WAIT_OBJECT_0+1 ) | ||
|  |   { MSG msg; | ||
|  | 
 | ||
|  |     while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) | ||
|  |     { TranslateMessage(&msg); | ||
|  |       DispatchMessage(&msg); | ||
|  |     } | ||
|  | 
 | ||
|  |     if ( PL_handle_signals() < 0 ) | ||
|  |     { EnterCriticalSection(external_mutex); | ||
|  |       return WAIT_INTR; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   EnterCriticalSection(external_mutex); | ||
|  | 
 | ||
|  |   cv->waiters--; | ||
|  | 
 | ||
|  |   return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | win32_cond_signal(win32_cond_t *cv)	/* must be holding associated mutex */ | ||
|  | { if ( cv->waiters > 0 ) | ||
|  |     SetEvent(cv->events[SIGNAL]); | ||
|  | 
 | ||
|  |   return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | rdlock(rwlock *lock) | ||
|  | { int self = PL_thread_self(); | ||
|  | 
 | ||
|  |   if ( lock->writer == self ) | ||
|  |   { lock->lock_level++;			/* read nested in write */ | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   EnterCriticalSection(&lock->mutex); | ||
|  | 
 | ||
|  |   if ( lock->allow_readers == TRUE ) | ||
|  |   { ok: | ||
|  | 
 | ||
|  |     lock->readers++; | ||
|  |     register_reader(lock, self); | ||
|  |     LeaveCriticalSection(&lock->mutex); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->waiting_readers++; | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { int rc = win32_cond_wait(&lock->rdcondvar, &lock->mutex); | ||
|  | 
 | ||
|  |     if ( rc == WAIT_INTR ) | ||
|  |     { lock->waiting_readers--; | ||
|  |       LeaveCriticalSection(&lock->mutex); | ||
|  |       return FALSE; | ||
|  |     } else if ( rc == 0 ) | ||
|  |     { if ( lock->allow_readers == TRUE ) | ||
|  |       { lock->waiting_readers--; | ||
|  | 	goto ok; | ||
|  |       } | ||
|  |     } else | ||
|  |     { assert(0);			/* TBD: OS errors */ | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | wrlock(rwlock *lock, int allow_readers) | ||
|  | { int self = PL_thread_self(); | ||
|  | 
 | ||
|  |   if ( lock->writer == self )		/* recursive write lock, used for */ | ||
|  |   { lock->lock_level++;			/* nested transactions */ | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   EnterCriticalSection(&lock->mutex); | ||
|  | 
 | ||
|  |   if ( lock->writer == -1 && lock->readers == 0 ) | ||
|  |   { ok: | ||
|  | 
 | ||
|  |     lock->writer = self; | ||
|  |     lock->lock_level = 1; | ||
|  |     lock->allow_readers = allow_readers; | ||
|  |     LeaveCriticalSection(&lock->mutex); | ||
|  |     DEBUG(3, Sdprintf("WRLOCK(%d): OK\n", self)); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   if ( self < lock->thread_max && lock->read_by_thread[self] > 0 ) | ||
|  |   { LeaveCriticalSection(&lock->mutex); | ||
|  |     return permission_error("write", "rdf_db", "default", | ||
|  | 			    "Operation would deadlock"); | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->waiting_writers++; | ||
|  |   DEBUG(3, Sdprintf("WRLOCK(%d): waiting ...\n", self)); | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { int rc = win32_cond_wait(&lock->wrcondvar, &lock->mutex); | ||
|  | 
 | ||
|  |     if ( rc == WAIT_INTR ) | ||
|  |     { lock->waiting_writers--; | ||
|  |       LeaveCriticalSection(&lock->mutex); | ||
|  |       return FALSE; | ||
|  |     } else if ( rc == 0 ) | ||
|  |     { if ( lock->writer == -1 && lock->readers == 0 ) | ||
|  |       { lock->waiting_writers--; | ||
|  | 	goto ok; | ||
|  |       } | ||
|  |     } else | ||
|  |     { assert(0);			/* TBD: OS errors */ | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | lockout_readers(rwlock *lock) | ||
|  | { EnterCriticalSection(&lock->mutex); | ||
|  | 
 | ||
|  |   if ( lock->readers == 0 ) | ||
|  |   { ok: | ||
|  | 
 | ||
|  |     lock->allow_readers = FALSE; | ||
|  |     LeaveCriticalSection(&lock->mutex); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->waiting_upgrade++; | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { int rc = win32_cond_wait(&lock->upcondvar, &lock->mutex); | ||
|  | 
 | ||
|  |     if ( rc == WAIT_INTR ) | ||
|  |     { lock->waiting_upgrade--; | ||
|  |       LeaveCriticalSection(&lock->mutex); | ||
|  |       return FALSE; | ||
|  |     } else if ( rc == 0 ) | ||
|  |     { if ( lock->readers == 0 ) | ||
|  |       { lock->waiting_upgrade--; | ||
|  | 	goto ok; | ||
|  |       } | ||
|  |     } else | ||
|  |     { assert(0);			/* TBD: OS errors */ | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void | ||
|  | reallow_readers(rwlock *lock) | ||
|  | { EnterCriticalSection(&lock->mutex); | ||
|  |   lock->allow_readers = TRUE; | ||
|  |   LeaveCriticalSection(&lock->mutex); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | unlock(rwlock *lock, int rd) | ||
|  | { int self = PL_thread_self(); | ||
|  |   int signal; | ||
|  | 
 | ||
|  |   if ( lock->writer == self && lock->lock_level > 1 ) | ||
|  |   { lock->lock_level--; | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   EnterCriticalSection(&lock->mutex); | ||
|  |   if ( rd )				/* must be a read lock */ | ||
|  |   { lock->readers--; | ||
|  |     lock->read_by_thread[self]--; | ||
|  |     signal = (lock->readers == 0); | ||
|  |   } else | ||
|  |   { lock->writer = -1; | ||
|  |     lock->allow_readers = TRUE; | ||
|  |     signal = TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   if ( signal ) | ||
|  |   { enum { NONE, READ, WRITE, UPGRADE } waiting; | ||
|  | 
 | ||
|  |     waiting = (lock->waiting_upgrade ? UPGRADE : | ||
|  | 	       lock->waiting_writers ? WRITE : | ||
|  | 	       lock->waiting_readers ? READ : NONE); | ||
|  | 
 | ||
|  |     switch(waiting) | ||
|  |     { case UPGRADE: | ||
|  | 	win32_cond_signal(&lock->upcondvar); | ||
|  | 	break; | ||
|  |       case WRITE: | ||
|  | 	win32_cond_signal(&lock->wrcondvar); | ||
|  | 	break; | ||
|  |       case READ: | ||
|  | 	win32_cond_signal(&lock->rdcondvar); | ||
|  | 	break; | ||
|  |       default: | ||
|  | 	; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   LeaveCriticalSection(&lock->mutex);	/* In our __WINDOWS__ emulation we */ | ||
|  | 					/* must hold the associated mutex */ | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | lock_misc(rwlock *lock) | ||
|  | { EnterCriticalSection(&lock->misc_mutex); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | unlock_misc(rwlock *lock) | ||
|  | { LeaveCriticalSection(&lock->misc_mutex); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | init_lock(rwlock *lock) | ||
|  | { InitializeCriticalSection(&lock->mutex); | ||
|  |   InitializeCriticalSection(&lock->misc_mutex); | ||
|  | 
 | ||
|  |   if ( !win32_cond_init(&lock->wrcondvar) == 0 || | ||
|  |        !win32_cond_init(&lock->rdcondvar) == 0 || | ||
|  |        !win32_cond_init(&lock->upcondvar) == 0 ) | ||
|  |   {					/* TBD: System error */ | ||
|  |     return FALSE; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->writer          = -1; | ||
|  |   lock->allow_readers   = TRUE; | ||
|  |   lock->readers         = 0; | ||
|  |   lock->waiting_readers = 0; | ||
|  |   lock->waiting_writers = 0; | ||
|  |   lock->waiting_upgrade = 0; | ||
|  |   lock->lock_level      = 0; | ||
|  | 
 | ||
|  |   lock->thread_max = 4; | ||
|  |   if ( !(lock->read_by_thread = malloc(lock->thread_max*sizeof(int))) ) | ||
|  |     return FALSE; | ||
|  |   memset(lock->read_by_thread, 0, lock->thread_max*sizeof(int)); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | destroy_lock(rwlock *lock) | ||
|  | { DeleteCriticalSection(&lock->mutex); | ||
|  |   DeleteCriticalSection(&lock->misc_mutex); | ||
|  |   win32_cond_destroy(&lock->wrcondvar); | ||
|  |   win32_cond_destroy(&lock->rdcondvar); | ||
|  |   win32_cond_destroy(&lock->upcondvar); | ||
|  | 
 | ||
|  |   free(lock->read_by_thread); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | #else /*__WINDOWS__*/
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	   POSIX VERSION	* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | #include <errno.h>
 | ||
|  | 
 | ||
|  | int | ||
|  | rdlock(rwlock *lock) | ||
|  | { int self = PL_thread_self(); | ||
|  | 
 | ||
|  |   if ( lock->writer == self ) | ||
|  |   { lock->lock_level++;			/* read nested in write */ | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   pthread_mutex_lock(&lock->mutex); | ||
|  | 
 | ||
|  |   if ( lock->allow_readers == TRUE ) | ||
|  |   { ok: | ||
|  | 
 | ||
|  |     lock->readers++; | ||
|  |     register_reader(lock, self); | ||
|  |     pthread_mutex_unlock(&lock->mutex); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->waiting_readers++; | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { int rc = pthread_cond_wait(&lock->rdcondvar, &lock->mutex); | ||
|  | 
 | ||
|  |     if ( rc == EINTR ) | ||
|  |     { if ( PL_handle_signals() < 0 ) | ||
|  |       { lock->waiting_readers--; | ||
|  | 	pthread_mutex_unlock(&lock->mutex); | ||
|  | 	return FALSE; | ||
|  |       } | ||
|  |       continue; | ||
|  |     } else if ( rc == 0 ) | ||
|  |     { if ( lock->allow_readers == TRUE ) | ||
|  |       { lock->waiting_readers--; | ||
|  | 	goto ok; | ||
|  |       } | ||
|  |     } else | ||
|  |     { assert(0);			/* TBD: OS errors */ | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | WRLOCK() and LOCKOUT_READERS() can be  used   in  two ways. Conventional | ||
|  | write locks are established using   WRLOCK(db,  FALSE) ... WRUNLOCK(db). | ||
|  | For transactions, we allow concurrent  readers   until  we  are ready to | ||
|  | commit, in which case we use  WRLOCK(db, TRUE) ... LOCKOUT_READERS() ... | ||
|  | WRUNLOCK(db) | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | int | ||
|  | wrlock(rwlock *lock, int allow_readers) | ||
|  | { int self = PL_thread_self(); | ||
|  | 
 | ||
|  |   if ( lock->writer == self )		/* recursive write lock, used for */ | ||
|  |   { lock->lock_level++;			/* nested transactions */ | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   pthread_mutex_lock(&lock->mutex); | ||
|  | 
 | ||
|  |   if ( lock->writer == -1 && lock->readers == 0 ) | ||
|  |   { ok: | ||
|  | 
 | ||
|  |     lock->writer = self; | ||
|  |     lock->lock_level = 1; | ||
|  |     lock->allow_readers = allow_readers; | ||
|  |     pthread_mutex_unlock(&lock->mutex); | ||
|  |     DEBUG(3, Sdprintf("WRLOCK(%d): OK\n", self)); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   if ( self < lock->thread_max && lock->read_by_thread[self] > 0 ) | ||
|  |   { DEBUG(1, Sdprintf("SELF(%d) has %d readers\n", | ||
|  | 		      self, lock->read_by_thread[self])); | ||
|  |     pthread_mutex_unlock(&lock->mutex); | ||
|  |     return permission_error("write", "rdf_db", "default", | ||
|  | 			    "Operation would deadlock"); | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->waiting_writers++; | ||
|  |   DEBUG(3, Sdprintf("WRLOCK(%d): waiting ...\n", self)); | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { int rc = pthread_cond_wait(&lock->wrcondvar, &lock->mutex); | ||
|  | 
 | ||
|  |     if ( rc == EINTR ) | ||
|  |     { if ( PL_handle_signals() < 0 ) | ||
|  |       { lock->waiting_writers--; | ||
|  | 	pthread_mutex_unlock(&lock->mutex); | ||
|  | 	return FALSE; | ||
|  |       } | ||
|  |       continue; | ||
|  |     } else if ( rc == 0 ) | ||
|  |     { if ( lock->writer == -1 && lock->readers == 0 ) | ||
|  |       { lock->waiting_writers--; | ||
|  | 	goto ok; | ||
|  |       } | ||
|  |     } else | ||
|  |     { assert(0);			/* TBD: OS errors */ | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | lockout_readers(rwlock *lock) | ||
|  | { pthread_mutex_lock(&lock->mutex); | ||
|  | 
 | ||
|  |   if ( lock->readers == 0 ) | ||
|  |   { ok: | ||
|  | 
 | ||
|  |     lock->allow_readers = FALSE; | ||
|  |     pthread_mutex_unlock(&lock->mutex); | ||
|  | 
 | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->waiting_upgrade++; | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { int rc = pthread_cond_wait(&lock->upcondvar, &lock->mutex); | ||
|  | 
 | ||
|  |     if ( rc == EINTR ) | ||
|  |     { if ( PL_handle_signals() < 0 ) | ||
|  |       { lock->waiting_upgrade--; | ||
|  | 	pthread_mutex_unlock(&lock->mutex); | ||
|  | 	return FALSE; | ||
|  |       } | ||
|  |       continue; | ||
|  |     } else if ( rc == 0 ) | ||
|  |     { if ( lock->readers == 0 ) | ||
|  |       { lock->waiting_upgrade--; | ||
|  | 	goto ok; | ||
|  |       } | ||
|  |     } else | ||
|  |     { assert(0);			/* TBD: OS errors */ | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void | ||
|  | reallow_readers(rwlock *lock) | ||
|  | { pthread_mutex_lock(&lock->mutex); | ||
|  |   lock->allow_readers = TRUE; | ||
|  |   pthread_mutex_unlock(&lock->mutex); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | unlock(rwlock *lock, int rd)		/* TRUE: read lock */ | ||
|  | { int self = PL_thread_self(); | ||
|  |   int signal; | ||
|  | 
 | ||
|  |   if ( lock->writer == self && lock->lock_level > 1 ) | ||
|  |   { lock->lock_level--; | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   pthread_mutex_lock(&lock->mutex); | ||
|  |   if ( rd )				/* read lock */ | ||
|  |   { lock->readers--; | ||
|  |     lock->read_by_thread[self]--; | ||
|  |     signal = (lock->readers == 0); | ||
|  |   } else | ||
|  |   { lock->writer = -1; | ||
|  |     lock->allow_readers = TRUE; | ||
|  |     signal = TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   if ( signal ) | ||
|  |   { enum { NONE, READ, WRITE, UPGRADE } waiting; | ||
|  | 
 | ||
|  |     waiting = (lock->waiting_upgrade ? UPGRADE : | ||
|  | 	       lock->waiting_writers ? WRITE : | ||
|  | 	       lock->waiting_readers ? READ : NONE); | ||
|  |     pthread_mutex_unlock(&lock->mutex); | ||
|  | 
 | ||
|  |     switch(waiting) | ||
|  |     { case UPGRADE: | ||
|  | 	pthread_cond_signal(&lock->upcondvar); | ||
|  | 	break; | ||
|  |       case WRITE: | ||
|  | 	pthread_cond_signal(&lock->wrcondvar); | ||
|  | 	break; | ||
|  |       case READ: | ||
|  | 	pthread_cond_signal(&lock->rdcondvar); | ||
|  | 	break; | ||
|  |       default: | ||
|  | 	; | ||
|  |     } | ||
|  |   } else | ||
|  |   { pthread_mutex_unlock(&lock->mutex); | ||
|  |   } | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | lock_misc(rwlock *lock) | ||
|  | { return pthread_mutex_lock(&lock->misc_mutex) == 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | unlock_misc(rwlock *lock) | ||
|  | { return pthread_mutex_unlock(&lock->misc_mutex) == 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | init_lock(rwlock *lock) | ||
|  | { if ( !pthread_mutex_init(&lock->mutex, NULL) == 0 || | ||
|  |        !pthread_mutex_init(&lock->misc_mutex, NULL) == 0 || | ||
|  |        !pthread_cond_init(&lock->wrcondvar, NULL) == 0 || | ||
|  |        !pthread_cond_init(&lock->rdcondvar, NULL) == 0 || | ||
|  |        !pthread_cond_init(&lock->upcondvar, NULL) == 0 ) | ||
|  |   {					/* TBD: System error */ | ||
|  |     return FALSE; | ||
|  |   } | ||
|  | 
 | ||
|  |   lock->writer          = -1; | ||
|  |   lock->readers	        = 0; | ||
|  |   lock->allow_readers   = TRUE; | ||
|  |   lock->waiting_readers = 0; | ||
|  |   lock->waiting_writers = 0; | ||
|  |   lock->waiting_upgrade = 0; | ||
|  |   lock->lock_level      = 0; | ||
|  | 
 | ||
|  |   lock->thread_max = 4; | ||
|  |   if ( !(lock->read_by_thread = malloc(lock->thread_max*sizeof(int))) ) | ||
|  |     return FALSE; | ||
|  |   memset(lock->read_by_thread, 0, lock->thread_max*sizeof(int)); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | destroy_lock(rwlock *lock) | ||
|  | { if ( !pthread_mutex_destroy(&lock->mutex) || | ||
|  |        !pthread_mutex_destroy(&lock->misc_mutex) || | ||
|  |        !pthread_cond_destroy(&lock->wrcondvar) || | ||
|  |        !pthread_cond_destroy(&lock->rdcondvar) || | ||
|  |        !pthread_cond_destroy(&lock->upcondvar) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   free(lock->read_by_thread); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | #endif /*__WINDOWS__*/
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #else /*_REENTRANT*/
 | ||
|  | 
 | ||
|  | int | ||
|  | rdlock(rwlock *lock) | ||
|  | { lock->readers++; | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | int | ||
|  | wrlock(rwlock *lock, int allow_readers) | ||
|  | { if ( lock->readers ) | ||
|  |     return permission_error("write", "rdf_db", "default", | ||
|  | 			    "Operation would deadlock"); | ||
|  | 
 | ||
|  |   lock->writer = 0; | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | int | ||
|  | unlock(rwlock *lock, int rd) | ||
|  | { if ( rd ) | ||
|  |   { lock->readers--; | ||
|  |   } else | ||
|  |   { lock->writer = -1; | ||
|  |   } | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | lock_misc(rwlock *lock) | ||
|  | { return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | unlock_misc(rwlock *lock) | ||
|  | { return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | init_lock(rwlock *lock) | ||
|  | { lock->writer = -1; | ||
|  |   lock->readers = 0; | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | lockout_readers(rwlock *lock) | ||
|  | { return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void | ||
|  | reallow_readers(rwlock *lock) | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int | ||
|  | destroy_lock(rwlock *lock) | ||
|  | { return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | #endif /*_REENTRANT*/
 | ||
|  | 
 |