home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / mtlock.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  14KB  |  677 lines

  1. /***
  2. *mtlock.c - Multi-thread locking routines
  3. *
  4. *       Copyright (c) 1987-1997, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *       Contains definitions for general-purpose multithread locking functions.
  8. *       _mtlockinit()
  9. *       _mtlock()
  10. *       _mtunlock()
  11. *
  12. *******************************************************************************/
  13.  
  14. #include <cruntime.h>
  15. #include <oscalls.h>
  16. #include <internal.h>
  17. #include <mtdll.h>
  18. #include <rterr.h>
  19. #include <stddef.h>
  20. #include <limits.h>
  21.  
  22. #ifdef _MT
  23.  
  24. void __cdecl _mtlockinit( PRTL_CRITICAL_SECTION pLk)
  25. {
  26.         /*
  27.          * Initialize the critical section.
  28.          */
  29.         InitializeCriticalSection( pLk );
  30. }
  31.  
  32. void __cdecl _mtlockterm( PRTL_CRITICAL_SECTION pLk)
  33. {
  34.         /*
  35.          * Initialize the critical section.
  36.          */
  37.         DeleteCriticalSection( pLk );
  38. }
  39.  
  40. _CRTIMP1 void __cdecl _mtlock ( PRTL_CRITICAL_SECTION pLk)
  41. {
  42.         /*
  43.          * Enter the critical section.
  44.          */
  45.         EnterCriticalSection( pLk );
  46. }
  47.  
  48. _CRTIMP1 void __cdecl _mtunlock ( PRTL_CRITICAL_SECTION pLk)
  49. {
  50.         /*
  51.          * leave the critical section.
  52.          */
  53.         LeaveCriticalSection( pLk );
  54. }
  55.  
  56. #endif  /* _MT */
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68. /* history: mlock.c */
  69.  
  70. #ifdef _IOSDEBUG
  71. #include <dbgint.h>
  72.  
  73. /*
  74.  * Local routines
  75.  */
  76.  
  77. static void __cdecl _lock_create (unsigned);
  78.  
  79. #ifdef _IOSDEBUG
  80. static struct _debug_lock * __cdecl _lock_validate(int);
  81. #endif  /* _IOSDEBUG */
  82.  
  83.  
  84. /*
  85.  * Global Data
  86.  */
  87.  
  88. /*
  89.  * Lock Table
  90.  * This table contains the critical section management structure of each
  91.  * lock.
  92.  */
  93.  
  94. RTL_CRITICAL_SECTION _locktable[_TOTAL_LOCKS];  /* array of locks */
  95.  
  96. /*
  97.  * Lock Bit Map
  98.  * This table contains one bit for each lock (i.e., each entry in the
  99.  * _locktable[] array).
  100.  *
  101.  *       If bit = 0, lock has not been created/opened
  102.  *       If bit = 1, lock has been created/opened
  103.  */
  104.  
  105. char _lockmap[(_TOTAL_LOCKS/CHAR_BIT)+1];       /* lock bit map */
  106.  
  107.  
  108.  
  109.  
  110. #ifdef _IOSDEBUG
  111. /*
  112.  * Lock Debug Data Table Segment
  113.  * Contains debugging data for each lock.
  114.  */
  115.  
  116. struct _debug_lock _debug_locktable[_TOTAL_LOCKS];
  117.  
  118. #endif  /* _IOSDEBUG */
  119.  
  120. #define _FATAL  _amsg_exit(_RT_LOCK)
  121.  
  122. /***
  123. * Bit map macros
  124. *
  125. *Purpose:
  126. *       _CLEARBIT() - Clear the specified bit
  127. *       _SETBIT()   - Set the specified bit
  128. *       _TESTBIT()  - Test the specified bit
  129. *
  130. *Entry:
  131. *       char a[] = character array
  132. *       unsigned b = bit number (0-based, range from 0 to whatever)
  133. *       unsigned x = bit number (0-based, range from 0 to 31)
  134. *
  135. *Exit:
  136. *       _CLEARBIT() = void
  137. *       _SETBIT()   = void
  138. *       _TESTBIT()  = 0 or 1
  139. *
  140. *Exceptions:
  141. *
  142. *******************************************************************************/
  143.  
  144. /*
  145.  * Macros for use when managing a bit in a character array (e.g., _lockmap)
  146.  * a = character array
  147.  * b = bit number (0-based)
  148.  */
  149.  
  150. #define _CLEARBIT(a,b) \
  151.                 ( a[b>>3] &= (~(1<<(b&7))) )
  152.  
  153. #define _SETBIT(a,b) \
  154.                 ( a[b>>3] |= (1<<(b&7)) )
  155.  
  156. #define _TESTBIT(a,b) \
  157.                 ( a[b>>3] & (1<<(b&7)) )
  158.  
  159. /*
  160.  * Macros for use when managing a bit in an unsigned int
  161.  * x = bit number (0-31)
  162.  */
  163.  
  164. #define _BIT_INDEX(x)   (1 << (x & 0x1F))
  165.  
  166.  
  167. /***
  168. *_mtinitlocks() - Initialize the semaphore lock data base
  169. *
  170. *Purpose:
  171. *       Initialize the mthread semaphore lock data base.
  172. *
  173. *       NOTES:
  174. *       (1) Only to be called ONCE at startup
  175. *       (2) Must be called BEFORE any mthread requests are made
  176. *
  177. *       Schemes for creating the mthread locks:
  178. *
  179. *       Create the locks one at a time on demand the first
  180. *       time the lock is attempted.  This is more complicated but
  181. *       is much faster than creating them all at startup time.
  182. *       These is currently the default scheme.
  183. *
  184. *       Create and open the semaphore that protects the lock data
  185. *       base.
  186. *
  187. *Entry:
  188. *       <none>
  189. *
  190. *Exit:
  191. *       returns on success
  192. *       calls _amsg_exit on failure
  193. *
  194. *Exceptions:
  195. *
  196. *******************************************************************************/
  197.  
  198. void __cdecl _mtinitlocks (
  199.         void
  200.         )
  201. {
  202.  
  203.         /*
  204.          * All we need to do is create the lock table lock
  205.          */
  206.  
  207.         _lock_create(_LOCKTAB_LOCK);
  208.  
  209.         /*
  210.          * Make sure the assumptions we make in this source are correct.
  211.          * The following is a tricky way to validate sizeof() assumptions
  212.          * at compile time without generating any runtime code (can't
  213.          * use sizeof() in an #ifdef).  If the assumption fails, the
  214.          * compiler will generate a divide by 0 error.
  215.          *
  216.          * This here only because it must be inside a subroutine.
  217.          */
  218.  
  219.         ( (sizeof(char) == 1) ? 1 : (1/0) );
  220.         ( (sizeof(int) == 4) ? 1 : (1/0) );
  221.  
  222. }
  223.  
  224.  
  225. /***
  226. *_lock_create() - Create and open a lock
  227. *
  228. *Purpose:
  229. *       Create and open a mthread lock.
  230. *
  231. *       NOTES:
  232. *
  233. *       (1) The caller must have previously determined that the lock
  234. *       needs to be created/opened (and this hasn't already been done).
  235. *
  236. *       (2) The caller must have aquired the _LOCKTAB_LOCK, if needed.
  237. *       (The only time this isn't required is at init time.)
  238. *
  239. *Entry:
  240. *       unsigned locknum = lock to create
  241. *
  242. *Exit:
  243. *
  244. *Exceptions:
  245. *
  246. *******************************************************************************/
  247.  
  248. static void __cdecl _lock_create (
  249.         unsigned locknum
  250.         )
  251. {
  252.  
  253. #ifdef _IOSDEBUG
  254.         /*
  255.          * See if the lock already exists; if so, die.
  256.          */
  257.  
  258.         if (_TESTBIT(_lockmap, locknum))
  259.                 _FATAL;
  260. #endif  /* _IOSDEBUG */
  261.  
  262.         /*
  263.          * Convert the lock number into a lock address
  264.          * and create the semaphore.
  265.          */
  266.  
  267.         /*
  268.          * Convert the lock number into a lock address
  269.          * and initialize the critical section.
  270.          */
  271.         InitializeCriticalSection( &_locktable[locknum] );
  272.  
  273.         /*
  274.          * Set the appropriate bit in the lock bit map.
  275.          */
  276.  
  277.         _SETBIT(_lockmap, locknum);
  278.  
  279. }
  280.  
  281.  
  282. /***
  283. * _lock_stream, etc. - Routines to lock/unlock streams, files, etc.
  284. *
  285. *Purpose:
  286. *       _lock_stream = Lock a stdio stream
  287. *       _unlock_stream = Unlock a stdio stream
  288. *       _lock_file = Lock a lowio file
  289. *       _unlock_file = Unlock a lowio file
  290. *
  291. *Entry:
  292. *       stream/file identifier
  293. *
  294. *Exit:
  295. *
  296. *Exceptions:
  297. *
  298. *******************************************************************************/
  299.  
  300. void __cdecl _lock_stream (
  301.         int stream_id
  302.         )
  303. {
  304.         _lock(stream_id+_STREAM_LOCKS);
  305. }
  306.  
  307. void __cdecl _unlock_stream (
  308.         int stream_id
  309.         )
  310. {
  311.         _unlock(stream_id+_STREAM_LOCKS);
  312. }
  313.  
  314. void __cdecl _lock_file (
  315.         int fh
  316.         )
  317. {
  318.         _lock(fh+_FH_LOCKS);
  319. }
  320.  
  321. void __cdecl _unlock_file (
  322.         int fh
  323.         )
  324. {
  325.         _unlock(fh+_FH_LOCKS);
  326. }
  327.  
  328.  
  329. /***
  330. * _lock - Acquire a multi-thread lock
  331. *
  332. *Purpose:
  333. *       Note that it is legal for a thread to aquire _EXIT_LOCK1
  334. *       multiple times.
  335. *
  336. *Entry:
  337. *       locknum = number of the lock to aquire
  338. *
  339. *Exit:
  340. *
  341. *Exceptions:
  342. *
  343. *******************************************************************************/
  344.  
  345. void __cdecl _lock (
  346.         int locknum
  347.         )
  348. {
  349.  
  350. #ifdef _IOSDEBUG
  351.         struct _debug_lock *deblock;
  352.         unsigned tidbit;
  353. #endif  /* _IOSDEBUG */
  354.  
  355.         /*
  356.          * Create/open the lock, if necessary
  357.          */
  358.  
  359.         if (!_TESTBIT(_lockmap, locknum)) {
  360.  
  361.                 _mlock(_LOCKTAB_LOCK);  /*** WARNING: Recursive lock call ***/
  362.  
  363.                 /* if lock still doesn't exist, create it */
  364.  
  365.                 if (!_TESTBIT(_lockmap, locknum))
  366.                         _lock_create(locknum);
  367.  
  368.                 _munlock(_LOCKTAB_LOCK);
  369.  
  370.         }
  371.  
  372. #ifdef _IOSDEBUG
  373.         /*
  374.          * Validate the lock and get pointer to debug lock structure, etc.
  375.          */
  376.  
  377.         deblock = _lock_validate(locknum);
  378.  
  379.         /*
  380.          * Set tidbit to 2**(index of ptd[] entry).
  381.          *
  382.          * call non-locking form of _getptd to avoid recursing
  383.          */
  384.         tidbit = _getptd_lk() - _ptd;   /* index of _ptd[] entry */
  385.  
  386.         tidbit = _BIT_INDEX(tidbit);
  387.  
  388.         /*
  389.          * Make sure we're not trying to get lock we already have
  390.          * (except for _EXIT_LOCK1).
  391.          */
  392.  
  393.         if (locknum != _EXIT_LOCK1)
  394.                 if ((deblock->holder) & tidbit)
  395.                         _FATAL;
  396.  
  397.         /*
  398.          * Set waiter bit for this thread
  399.          */
  400.  
  401.         deblock->waiters |= tidbit;
  402.  
  403. #endif  /* _IOSDEBUG */
  404.  
  405.         /*
  406.          * Get the lock
  407.          */
  408.  
  409.  
  410.         /*
  411.          * Enter the critical section.
  412.          */
  413.         EnterCriticalSection( &_locktable[locknum] );
  414.  
  415. #ifdef _IOSDEBUG
  416.         /*
  417.          * Clear waiter bit
  418.          */
  419.  
  420.         deblock->waiters &= (~tidbit);
  421.  
  422.         /*
  423.          * Make sure there are no lock holders (unless this is
  424.          * _EXIT_LOCK1); then set holder bit and bump lock count.
  425.          */
  426.  
  427.         _ASSERTE(THREADINTS==1);
  428.  
  429.         if (locknum != _EXIT_LOCK1)
  430.                 if ( (unsigned) deblock->holder != 0)
  431.                        _FATAL;
  432.  
  433.         deblock->holder &= tidbit;
  434.         deblock->lockcnt++;
  435.  
  436. #endif  /* _IOSDEBUG */
  437.  
  438. }
  439.  
  440.  
  441. /***
  442. * _unlock - Release multi-thread lock
  443. *
  444. *Purpose:
  445. *       Note that it is legal for a thread to aquire _EXIT_LOCK1
  446. *       multiple times.
  447. *
  448. *Entry:
  449. *       locknum = number of the lock to release
  450. *
  451. *Exit:
  452. *
  453. *Exceptions:
  454. *
  455. *******************************************************************************/
  456.  
  457. void __cdecl _unlock (
  458.         int locknum
  459.         )
  460. {
  461. #ifdef _IOSDEBUG
  462.         struct _debug_lock *deblock;
  463.         unsigned tidbit;
  464. #endif  /* _IOSDEBUG */
  465.  
  466. #ifdef _IOSDEBUG
  467.         /*
  468.          * Validate the lock and get pointer to debug lock structure, etc.
  469.          */
  470.  
  471.         deblock = _lock_validate(locknum);
  472.  
  473.         /*
  474.          * Set tidbit to 2**(index of ptd[] entry).
  475.          */
  476.         tidbit = _getptd_lk() - _ptd;   /* index of _ptd[] entry */
  477.  
  478.         tidbit = _BIT_INDEX(tidbit);
  479.  
  480.         /*
  481.          * Make sure we hold this lock then clear holder bit.
  482.          * [Note: Since it is legal to aquire _EXIT_LOCK1 several times,
  483.          * it's possible the holder bit is already clear.]
  484.          */
  485.  
  486.         if (locknum != _EXIT_LOCK1)
  487.                 if (!((deblock->holder) & tidbit))
  488.                         _FATAL;
  489.  
  490.         deblock->holder &= (~tidbit);
  491.  
  492.         /*
  493.          * See if anyone else is waiting for this lock.
  494.          */
  495.  
  496.         _ASSERTE(THREADINTS==1);
  497.  
  498.         if ((unsigned) deblock->waiters != 0)
  499.                 deblock->collcnt++;
  500.  
  501. #endif  /* _IOSDEBUG */
  502.  
  503.         /*
  504.          * leave the critical section.
  505.          */
  506.         LeaveCriticalSection( &_locktable[locknum] );
  507.  
  508.  
  509. }
  510.  
  511.  
  512. /*
  513.  * Debugging code
  514.  */
  515.  
  516. #ifdef _IOSDEBUG
  517.  
  518. /***
  519. *_lock_validate() - Validate a lock
  520. *
  521. *Purpose:
  522. *       Debug lock validations common to both lock and unlock.
  523. *
  524. *Entry:
  525. *       lock number
  526. *
  527. *Exit:
  528. *       ptr to lock's debug structure
  529. *
  530. *Exceptions:
  531. *
  532. *******************************************************************************/
  533.  
  534. static struct _debug_lock * __cdecl _lock_validate (
  535.         int locknum
  536.         )
  537. {
  538.         /*
  539.          * Make sure lock is legal
  540.          */
  541.  
  542.         if (locknum > _TOTAL_LOCKS)
  543.                 _FATAL;
  544.  
  545.         /*
  546.          * Return pointer to this lock's debug structure
  547.          */
  548.  
  549.         return(&_debug_locktable[locknum]);
  550.  
  551. }
  552.  
  553.  
  554. /***
  555. *_fh_locknum() - Return the lock number for a file handle
  556. *
  557. *Purpose:
  558. *
  559. *Entry:
  560. *       int fh = file handle
  561. *
  562. *Exit:
  563. *       int locknum = corresponding lock number
  564. *
  565. *Exceptions:
  566. *
  567. *******************************************************************************/
  568.  
  569. int  __cdecl _fh_locknum (
  570.         int fh
  571.         )
  572. {
  573.         return(fh+_FH_LOCKS);
  574. }
  575.  
  576.  
  577. /***
  578. *_stream_locknum() - Return the lock number for a stream
  579. *
  580. *Purpose:
  581. *
  582. *Entry:
  583. *       int stream = stream number (i.e., offset of the stream
  584. *                       in the _iob table)
  585. *
  586. *Exit:
  587. *       int locknum = corresponding lock number
  588. *
  589. *Exceptions:
  590. *
  591. *******************************************************************************/
  592.  
  593. int  __cdecl _stream_locknum (
  594.         int stream
  595.         )
  596. {
  597.         return(stream+_STREAM_LOCKS);
  598. }
  599.  
  600.  
  601. /***
  602. *_collide_cnt() - Return the collision count for a lock
  603. *
  604. *Purpose:
  605. *
  606. *Entry:
  607. *       int lock = lock number
  608. *
  609. *Exit:
  610. *       int count = collision count
  611. *
  612. *Exceptions:
  613. *
  614. *******************************************************************************/
  615.  
  616. int  __cdecl _collide_cnt (
  617.         int locknum
  618.         )
  619. {
  620.         return(_debug_locktable[locknum].collcnt);
  621. }
  622.  
  623.  
  624. /***
  625. *_lock_cnt() - Return the lock count for a lock
  626. *
  627. *Purpose:
  628. *
  629. *Entry:
  630. *       int lock = lock number
  631. *
  632. *Exit:
  633. *       int count = lock count
  634. *
  635. *Exceptions:
  636. *
  637. *******************************************************************************/
  638.  
  639. int  __cdecl _lock_cnt (
  640.         int locknum
  641.         )
  642. {
  643.         return(_debug_locktable[locknum].lockcnt);
  644. }
  645.  
  646.  
  647. /***
  648. *_lock_exist() - Check to see if a lock exists
  649. *
  650. *Purpose:
  651. *       Test lock bit map to see if the lock has
  652. *       been created or not.
  653. *
  654. *Entry:
  655. *       int lock = lock number
  656. *
  657. *Exit:
  658. *       int 0 = lock has NOT been created
  659. *           1 = lock HAS been created
  660. *
  661. *Exceptions:
  662. *
  663. *******************************************************************************/
  664.  
  665. int  __cdecl _lock_exist (
  666.         int locknum
  667.         )
  668. {
  669.         if (_TESTBIT(_lockmap, locknum))
  670.                 return(1);
  671.         else
  672.                 return(0);
  673. }
  674.  
  675. #endif  /* _IOSDEBUG */
  676. #endif  /* _IOSDEBUG */
  677.