/*** *mtlock.c - Multi-thread locking routines * * Copyright (c) 1987-1992, Microsoft Corporation. All rights reserved. * *Purpose: * Contains definitions for general-purpose multithread locking functions. * _mtlockinit() * _mtlock() * _mtunlock() * *Revision History: * 03-10-92 KRS Created from mlock.c. * *******************************************************************************/ #include #include #include #include #include #include #include #ifdef MTHREAD void _CALLTYPE2 _mtlockinit( PRTL_CRITICAL_SECTION pLk) { #ifdef _CRUISER_ if ( DOSCREATEMUTEXSEM(NULL, pLk, 0, 0) != 0) _FATAL; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Initialize the critical section. */ InitializeCriticalSection( pLk ); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ } void _CALLTYPE2 _mtlock ( PRTL_CRITICAL_SECTION pLk) { #ifdef _CRUISER_ if (DOSREQUESTMUTEXSEM(pLk,-1) != 0) _FATAL; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Enter the critical section. */ EnterCriticalSection( pLk ); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ } void _CALLTYPE2 _mtunlock ( PRTL_CRITICAL_SECTION pLk) { #ifdef _CRUISER_ /* * Release the lock */ if (DOSRELEASEMUTEXSEM(pLk) != 0) _FATAL; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * leave the critical section. */ LeaveCriticalSection( pLk ); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ } #endif /* MTHREAD */ /* history: mlock.c */ #if 0 #ifdef DEBUG #include #endif /* * Local routines */ static void _CALLTYPE1 _lock_create (unsigned); #ifdef DEBUG static struct _debug_lock * _CALLTYPE1 _lock_validate(int); #endif /* * Global Data */ #ifdef _CRUISER_ /* * Lock Table * This table contains the semaphore handle for each lock (lock number * is used as an index into the table). */ unsigned _locktable[_TOTAL_LOCKS]; /* array of locks */ #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Lock Table * This table contains the critical section management structure of each * lock. */ RTL_CRITICAL_SECTION _locktable[_TOTAL_LOCKS]; /* array of locks */ #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ /* * Lock Bit Map * This table contains one bit for each lock (i.e., each entry in the * _locktable[] array). * * If bit = 0, lock has not been created/opened * If bit = 1, lock has been created/opened */ char _lockmap[(_TOTAL_LOCKS/CHAR_BIT)+1]; /* lock bit map */ #ifdef _LOCKCOUNT /* * Total number of locks held */ unsigned _lockcnt = 0; #endif #ifdef DEBUG /* * Lock Debug Data Table Segment * Contains debugging data for each lock. */ struct _debug_lock _debug_locktable[_TOTAL_LOCKS]; #endif #define _FATAL _amsg_exit(_RT_LOCK) /*** * Bit map macros * *Purpose: * _CLEARBIT() - Clear the specified bit * _SETBIT() - Set the specified bit * _TESTBIT() - Test the specified bit * *Entry: * char a[] = character array * unsigned b = bit number (0-based, range from 0 to whatever) * unsigned x = bit number (0-based, range from 0 to 31) * *Exit: * _CLEARBIT() = void * _SETBIT() = void * _TESTBIT() = 0 or 1 * *Exceptions: * *******************************************************************************/ /* * Macros for use when managing a bit in a character array (e.g., _lockmap) * a = character array * b = bit number (0-based) */ #define _CLEARBIT(a,b) \ ( a[b>>3] &= (~(1<<(b&7))) ) #define _SETBIT(a,b) \ ( a[b>>3] |= (1<<(b&7)) ) #define _TESTBIT(a,b) \ ( a[b>>3] & (1<<(b&7)) ) /* * Macros for use when managing a bit in an unsigned int * x = bit number (0-31) */ #define _BIT_INDEX(x) (1 << (x & 0x1F)) /*** *_mtinitlocks() - Initialize the semaphore lock data base * *Purpose: * Initialize the mthread semaphore lock data base. * * NOTES: * (1) Only to be called ONCE at startup * (2) Must be called BEFORE any mthread requests are made * * Schemes for creating the mthread locks: * * Create the locks one at a time on demand the first * time the lock is attempted. This is more complicated but * is much faster than creating them all at startup time. * These is currently the default scheme. * * Create and open the semaphore that protects the lock data * base. * *Entry: * * *Exit: * returns on success * calls _amsg_exit on failure * *Exceptions: * *******************************************************************************/ void _CALLTYPE1 _mtinitlocks ( void ) { /* * All we need to do is create the lock table lock */ _lock_create(_LOCKTAB_LOCK); /* * Make sure the assumptions we make in this source are correct. * The following is a tricky way to validate sizeof() assumptions * at compile time without generating any runtime code (can't * use sizeof() in an #ifdef). If the assumption fails, the * compiler will generate a divide by 0 error. * * This here only because it must be inside a subroutine. */ ( (sizeof(char) == 1) ? 1 : (1/0) ); ( (sizeof(int) == 4) ? 1 : (1/0) ); } /*** *_lock_create() - Create and open a lock * *Purpose: * Create and open a mthread lock. * * NOTES: * * (1) The caller must have previously determined that the lock * needs to be created/opened (and this hasn't already been done). * * (2) The caller must have aquired the _LOCKTAB_LOCK, if needed. * (The only time this isn't required is at init time.) * *Entry: * unsigned locknum = lock to create * *Exit: * *Exceptions: * *******************************************************************************/ static void _CALLTYPE1 _lock_create ( unsigned locknum ) { #ifdef DEBUG /* * See if the lock already exists; if so, die. */ if (_TESTBIT(_lockmap, locknum)) _FATAL; #endif /* * Convert the lock number into a lock address * and create the semaphore. */ #ifdef _CRUISER_ if ( DOSCREATEMUTEXSEM(NULL, &_locktable[locknum], 0, 0) != 0) _FATAL; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Convert the lock number into a lock address * and initialize the critical section. */ InitializeCriticalSection( &_locktable[locknum] ); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ /* * Set the appropriate bit in the lock bit map. */ _SETBIT(_lockmap, locknum); } /*** * _lock_stream, etc. - Routines to lock/unlock streams, files, etc. * *Purpose: * _lock_stream = Lock a stdio stream * _unlock_stream = Unlock a stdio stream * _lock_file = Lock a lowio file * _unlock_file = Unlock a lowio file * *Entry: * stream/file identifier * *Exit: * *Exceptions: * *******************************************************************************/ void _CALLTYPE2 _lock_stream ( int stream_id ) { _lock(stream_id+_STREAM_LOCKS); } void _CALLTYPE2 _unlock_stream ( int stream_id ) { _unlock(stream_id+_STREAM_LOCKS); } void _CALLTYPE2 _lock_file ( int fh ) { _lock(fh+_FH_LOCKS); } void _CALLTYPE2 _unlock_file ( int fh ) { _unlock(fh+_FH_LOCKS); } /*** * _lock - Acquire a multi-thread lock * *Purpose: * Note that it is legal for a thread to aquire _EXIT_LOCK1 * multiple times. * *Entry: * locknum = number of the lock to aquire * *Exit: * *Exceptions: * *******************************************************************************/ void _CALLTYPE2 _lock ( int locknum ) { #ifdef DEBUG struct _debug_lock *deblock; unsigned tidbit; #endif /* * Create/open the lock, if necessary */ if (!_TESTBIT(_lockmap, locknum)) { _mlock(_LOCKTAB_LOCK); /*** WARNING: Recursive lock call ***/ /* if lock still doesn't exist, create it */ if (!_TESTBIT(_lockmap, locknum)) _lock_create(locknum); _munlock(_LOCKTAB_LOCK); } #ifdef DEBUG /* * Validate the lock and get pointer to debug lock structure, etc. */ deblock = _lock_validate(locknum); #ifdef _CRUISER_ tidbit = _BIT_INDEX(*_threadid); #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Set tidbit to 2**(index of ptd[] entry). * * call non-locking form of _getptd to avoid recursing */ tidbit = _getptd_lk() - _ptd; /* index of _ptd[] entry */ tidbit = _BIT_INDEX(tidbit); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ /* * Make sure we're not trying to get lock we already have * (except for _EXIT_LOCK1). */ if (locknum != _EXIT_LOCK1) if ((deblock->holder) & tidbit) _FATAL; /* * Set waiter bit for this thread */ deblock->waiters |= tidbit; #endif /* DEBUG */ /* * Get the lock */ #ifdef _LOCKCOUNT _lockcnt++; #endif #ifdef _CRUISER_ if (DOSREQUESTMUTEXSEM(_locktable[locknum],-1) != 0) _FATAL; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Enter the critical section. */ EnterCriticalSection( &_locktable[locknum] ); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ #ifdef DEBUG /* * Clear waiter bit */ deblock->waiters &= (~tidbit); /* * Make sure there are no lock holders (unless this is * _EXIT_LOCK1); then set holder bit and bump lock count. */ assert(THREADINTS==1); if (locknum != _EXIT_LOCK1) if ( (unsigned) deblock->holder != 0) _FATAL; deblock->holder &= tidbit; deblock->lockcnt++; #endif } /*** * _unlock - Release multi-thread lock * *Purpose: * Note that it is legal for a thread to aquire _EXIT_LOCK1 * multiple times. * *Entry: * locknum = number of the lock to release * *Exit: * *Exceptions: * *******************************************************************************/ void _CALLTYPE2 _unlock ( int locknum ) { #ifdef DEBUG struct _debug_lock *deblock; unsigned tidbit; #endif #ifdef DEBUG /* * Validate the lock and get pointer to debug lock structure, etc. */ deblock = _lock_validate(locknum); #ifdef _CRUISER_ tidbit = _BIT_INDEX(*_threadid); #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * Set tidbit to 2**(index of ptd[] entry). */ tidbit = _getptd_lk() - _ptd; /* index of _ptd[] entry */ tidbit = _BIT_INDEX(tidbit); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ /* * Make sure we hold this lock then clear holder bit. * [Note: Since it is legal to aquire _EXIT_LOCK1 several times, * it's possible the holder bit is already clear.] */ if (locknum != _EXIT_LOCK1) if (!((deblock->holder) & tidbit)) _FATAL; deblock->holder &= (~tidbit); /* * See if anyone else is waiting for this lock. */ assert(THREADINTS==1); if ((unsigned) deblock->waiters != 0) deblock->collcnt++; #endif #ifdef _CRUISER_ /* * Release the lock */ if (DOSRELEASEMUTEXSEM(_locktable[locknum]) != 0) _FATAL; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ /* * leave the critical section. */ LeaveCriticalSection( &_locktable[locknum] ); #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ #ifdef _LOCKCOUNT _lockcnt--; #endif } /* * Debugging code */ #ifdef DEBUG /*** *_lock_validate() - Validate a lock * *Purpose: * Debug lock validations common to both lock and unlock. * *Entry: * lock number * *Exit: * ptr to lock's debug structure * *Exceptions: * *******************************************************************************/ static struct _debug_lock * _CALLTYPE1 _lock_validate ( int locknum ) { /* * Make sure lock is legal */ if (locknum > _TOTAL_LOCKS) _FATAL; #ifdef _CRUISER_ /* * Make sure threadid is in range */ if (*_threadid > MAXTHREADID) _FATAL; #endif /* _CRUISER_ */ /* * Return pointer to this lock's debug structure */ return(&_debug_locktable[locknum]); } /*** *_fh_locknum() - Return the lock number for a file handle * *Purpose: * *Entry: * int fh = file handle * *Exit: * int locknum = corresponding lock number * *Exceptions: * *******************************************************************************/ int _CALLTYPE2 _fh_locknum ( int fh ) { return(fh+_FH_LOCKS); } /*** *_stream_locknum() - Return the lock number for a stream * *Purpose: * *Entry: * int stream = stream number (i.e., offset of the stream * in the _iob table) * *Exit: * int locknum = corresponding lock number * *Exceptions: * *******************************************************************************/ int _CALLTYPE2 _stream_locknum ( int stream ) { return(stream+_STREAM_LOCKS); } #ifdef _CRUISER_ /*** *_check_lock() - Make sure a lock is in good shape * *Purpose: * This routine checks to make sure that a lock is in a 'good' * released state. That is, abort if any of the following * are true: * (1) lock is held * (2) holder bits are set * (3) waiter bits are set * * NOTE: This routine does NOT aquire the lock but simply looks * at the lock data bases. * *Entry: * locknum = number of the lock to aquire * *Exit: * success = 0 * failure = !0 * *Exceptions: * *******************************************************************************/ int _CALLTYPE2 _check_lock ( int locknum ) { unsigned pid; unsigned long tid; unsigned cnt; struct _debug_lock *deblock; /* * See if lock exists. */ if (_TESTBIT(_lockmap, locknum)) { /* * Lock exists. Ask the OS about the lock and * make sure lock isn't held. */ if (DOSQUERYMUTEXSEM(_locktable[locknum], &pid, &tid, &cnt) != 0) goto error_return; if (cnt != 0) goto error_return; } else { /* * Lock doesn't exist, handle better be 0. */ if (_locktable[locknum] != 0) goto error_return; } /* * Make sure there are no lock waiters or holders. */ assert(THREADINTS==1); if ((deblock->waiters != 0) || (deblock->holder != 0)) goto error_return; /* * Good Return */ return(0); /* * Error return */ error_return: return(-1); } #endif /* _CRUISER_ */ /*** *_collide_cnt() - Return the collision count for a lock * *Purpose: * *Entry: * int lock = lock number * *Exit: * int count = collision count * *Exceptions: * *******************************************************************************/ int _CALLTYPE2 _collide_cnt ( int locknum ) { return(_debug_locktable[locknum].collcnt); } /*** *_lock_cnt() - Return the lock count for a lock * *Purpose: * *Entry: * int lock = lock number * *Exit: * int count = lock count * *Exceptions: * *******************************************************************************/ int _CALLTYPE2 _lock_cnt ( int locknum ) { return(_debug_locktable[locknum].lockcnt); } /*** *_lock_exist() - Check to see if a lock exists * *Purpose: * Test lock bit map to see if the lock has * been created or not. * *Entry: * int lock = lock number * *Exit: * int 0 = lock has NOT been created * 1 = lock HAS been created * *Exceptions: * *******************************************************************************/ int _CALLTYPE2 _lock_exist ( int locknum ) { if (_TESTBIT(_lockmap, locknum)) return(1); else return(0); } #endif #endif