Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

934 lines
23 KiB

  1. /*++
  2. Copyright (c) 1989-1993 Microsoft Corporation
  3. Module Name:
  4. Resource.cpp
  5. Abstract:
  6. This module implements the executive functions to acquire and release
  7. a shared resource.
  8. Author:
  9. Mark Lucovsky (markl) 04-Aug-1989
  10. Environment:
  11. These routines are statically linked in the caller's executable and
  12. are callable in only from user mode. They make use of Nt system
  13. services.
  14. Revision History:
  15. 02-Feb-97 vlads Adapted to Win9x environment to support portable syncronization
  16. ( RTL calls do not exist)
  17. --*/
  18. #include "cplusinc.h"
  19. #include "sticomm.h"
  20. #include <lock.h>
  21. //
  22. // The semaphore wait time before retrying the wait
  23. //
  24. #define _RES_TIMEOUT (2 * 60 * 1000)
  25. BOOL
  26. InitializeResource(
  27. IN PRTL_RESOURCE Resource
  28. )
  29. /*++
  30. Routine Description:
  31. This routine initializes the input resource variable
  32. Arguments:
  33. Resource - Supplies the resource variable being initialized
  34. Return Value:
  35. None
  36. --*/
  37. {
  38. //
  39. // Initialize the lock fields, the count indicates how many are waiting
  40. // to enter or are in the critical section, LockSemaphore is the object
  41. // to wait on when entering the critical section. SpinLock is used
  42. // for the add interlock instruction.
  43. //
  44. __try {
  45. #ifdef UNICODE
  46. if(!InitializeCriticalSectionAndSpinCount( &Resource->CriticalSection, MINLONG ))
  47. return FALSE;
  48. #else
  49. InitializeCriticalSection( &Resource->CriticalSection);
  50. #endif
  51. }
  52. __except(EXCEPTION_EXECUTE_HANDLER) {
  53. return FALSE;
  54. }
  55. Resource->DebugInfo = NULL;
  56. //
  57. // Initialize flags so there is a default value.
  58. // (Some apps may set RTL_RESOURCE_FLAGS_LONG_TERM to affect timeouts.)
  59. //
  60. Resource->Flags = 0;
  61. //
  62. // Initialize the shared and exclusive waiting counters and semaphore.
  63. // The counters indicate how many are waiting for access to the resource
  64. // and the semaphores are used to wait on the resource. Note that
  65. // the semaphores can also indicate the number waiting for a resource
  66. // however there is a race condition in the alogrithm on the acquire
  67. // side if count if not updated before the critical section is exited.
  68. //
  69. Resource->SharedSemaphore = CreateSemaphore( NULL,
  70. 0,
  71. MAXLONG,
  72. NULL );
  73. if ( !Resource->SharedSemaphore ) {
  74. return FALSE;
  75. }
  76. Resource->NumberOfWaitingShared = 0;
  77. Resource->ExclusiveSemaphore = CreateSemaphore( NULL,
  78. 0,
  79. MAXLONG,
  80. NULL );
  81. if ( !Resource->ExclusiveSemaphore ){
  82. CloseHandle( Resource->SharedSemaphore );
  83. return FALSE;
  84. }
  85. Resource->NumberOfWaitingExclusive = 0;
  86. //
  87. // Initialize the current state of the resource
  88. //
  89. Resource->NumberOfActive = 0;
  90. Resource->ExclusiveOwnerThread = NULL;
  91. return TRUE;
  92. }
  93. BOOL
  94. AcquireResourceShared(
  95. IN PRTL_RESOURCE Resource,
  96. IN BOOL Wait
  97. )
  98. /*++
  99. Routine Description:
  100. The routine acquires the resource for shared access. Upon return from
  101. the procedure the resource is acquired for shared access.
  102. Arguments:
  103. Resource - Supplies the resource to acquire
  104. Wait - Indicates if the call is allowed to wait for the resource
  105. to become available for must return immediately
  106. Return Value:
  107. BOOL - TRUE if the resource is acquired and FALSE otherwise
  108. --*/
  109. {
  110. DWORD ret;
  111. ULONG TimeoutCount = 0;
  112. DWORD TimeoutTime = _RES_TIMEOUT;
  113. //
  114. // Enter the critical section
  115. //
  116. EnterCriticalSection(&Resource->CriticalSection);
  117. //
  118. // If it is not currently acquired for exclusive use then we can acquire
  119. // the resource for shared access. Note that this can potentially
  120. // starve an exclusive waiter however, this is necessary given the
  121. // ability to recursively acquire the resource shared. Otherwise we
  122. // might/will reach a deadlock situation where a thread tries to acquire
  123. // the resource recusively shared but is blocked by an exclusive waiter.
  124. //
  125. // The test to reanable not starving an exclusive waiter is:
  126. //
  127. // if ((Resource->NumberOfWaitingExclusive == 0) &&
  128. // (Resource->NumberOfActive >= 0)) {
  129. //
  130. if (Resource->NumberOfActive >= 0) {
  131. //
  132. // The resource is ours, so indicate that we have it and
  133. // exit the critical section
  134. //
  135. Resource->NumberOfActive += 1;
  136. LeaveCriticalSection(&Resource->CriticalSection);
  137. //
  138. // Otherwise check to see if this thread is the one currently holding
  139. // exclusive access to the resource. And if it is then we change
  140. // this shared request to an exclusive recusive request and grant
  141. // access to the resource.
  142. //
  143. } else if (Resource->ExclusiveOwnerThread == (HANDLE)ULongToPtr(GetCurrentThreadId())) {
  144. //
  145. // The resource is ours (recusively) so indicate that we have it
  146. // and exit the critial section
  147. //
  148. Resource->NumberOfActive -= 1;
  149. LeaveCriticalSection(&Resource->CriticalSection);
  150. //
  151. // Otherwise we'll have to wait for access.
  152. //
  153. } else {
  154. //
  155. // Check if we are allowed to wait or must return immedately, and
  156. // indicate that we didn't acquire the resource
  157. //
  158. if (!Wait) {
  159. LeaveCriticalSection(&Resource->CriticalSection);
  160. return FALSE;
  161. }
  162. //
  163. // Otherwise we need to wait to acquire the resource.
  164. // To wait we will increment the number of waiting shared,
  165. // release the lock, and wait on the shared semaphore
  166. //
  167. Resource->NumberOfWaitingShared += 1;
  168. LeaveCriticalSection(&Resource->CriticalSection);
  169. rewait:
  170. if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
  171. TimeoutTime = INFINITE;
  172. }
  173. ret = WaitForSingleObject(
  174. Resource->SharedSemaphore,
  175. TimeoutTime
  176. );
  177. if ( ret == WAIT_TIMEOUT ) {
  178. #if DBG
  179. OutputDebugString( TEXT("[AcquireResourceShared] Sem timeout\n") );
  180. #endif
  181. TimeoutCount++;
  182. if ( TimeoutCount > 2 ) {
  183. #if DBG
  184. DebugBreak();
  185. #endif
  186. }
  187. #if DBG
  188. OutputDebugString(TEXT("[AcquireResourceShared]: Re-Waiting\n"));
  189. #endif
  190. goto rewait;
  191. } else if ( ret != WAIT_OBJECT_0 ) {
  192. #if DBG
  193. OutputDebugString(TEXT("[AcquireResourceShared]: WaitForSingleObjectFailed\n"));
  194. #endif
  195. }
  196. }
  197. //
  198. // Now the resource is ours, for shared access
  199. //
  200. return TRUE;
  201. }
  202. BOOL
  203. AcquireResourceExclusive(
  204. IN PRTL_RESOURCE Resource,
  205. IN BOOL Wait
  206. )
  207. /*++
  208. Routine Description:
  209. The routine acquires the resource for exclusive access. Upon return from
  210. the procedure the resource is acquired for exclusive access.
  211. Arguments:
  212. Resource - Supplies the resource to acquire
  213. Wait - Indicates if the call is allowed to wait for the resource
  214. to become available for must return immediately
  215. Return Value:
  216. BOOL - TRUE if the resource is acquired and FALSE otherwise
  217. --*/
  218. {
  219. ULONG TimeoutCount = 0;
  220. DWORD TimeoutTime = _RES_TIMEOUT;
  221. DWORD ret;
  222. //
  223. // Loop until the resource is ours or exit if we cannot wait.
  224. //
  225. while (TRUE) {
  226. //
  227. // Enter the critical section
  228. //
  229. EnterCriticalSection(&Resource->CriticalSection);
  230. //
  231. // If there are no shared users and it is not currently acquired for
  232. // exclusive use then we can acquire the resource for exclusive
  233. // access. We also can acquire it if the resource indicates exclusive
  234. // access but there isn't currently an owner.
  235. //
  236. if ((Resource->NumberOfActive == 0)
  237. ||
  238. ((Resource->NumberOfActive == -1) &&
  239. (Resource->ExclusiveOwnerThread == NULL))) {
  240. //
  241. // The resource is ours, so indicate that we have it and
  242. // exit the critical section
  243. //
  244. Resource->NumberOfActive = -1;
  245. Resource->ExclusiveOwnerThread = (HANDLE) ULongToPtr(GetCurrentThreadId());
  246. LeaveCriticalSection(&Resource->CriticalSection);
  247. return TRUE;
  248. }
  249. //
  250. // Otherwise check to see if we already have exclusive access to the
  251. // resource and can simply recusively acquire it again.
  252. //
  253. if (Resource->ExclusiveOwnerThread == (HANDLE) ULongToPtr(GetCurrentThreadId())) {
  254. //
  255. // The resource is ours (recusively) so indicate that we have it
  256. // and exit the critial section
  257. //
  258. Resource->NumberOfActive -= 1;
  259. LeaveCriticalSection(&Resource->CriticalSection);
  260. return TRUE;
  261. }
  262. //
  263. // Check if we are allowed to wait or must return immedately, and
  264. // indicate that we didn't acquire the resource
  265. //
  266. if (!Wait) {
  267. LeaveCriticalSection(&Resource->CriticalSection);
  268. return FALSE;
  269. }
  270. //
  271. // Otherwise we need to wait to acquire the resource.
  272. // To wait we will increment the number of waiting exclusive,
  273. // release the lock, and wait on the exclusive semaphore
  274. //
  275. Resource->NumberOfWaitingExclusive += 1;
  276. LeaveCriticalSection(&Resource->CriticalSection);
  277. rewait:
  278. if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
  279. TimeoutTime = INFINITE;
  280. }
  281. ret = WaitForSingleObject(
  282. Resource->ExclusiveSemaphore,
  283. TimeoutTime
  284. );
  285. if ( ret == WAIT_TIMEOUT ) {
  286. #if DBG
  287. OutputDebugString( TEXT("[AcquireResourceExclusive] Sem timeout\n") );
  288. #endif
  289. TimeoutCount++;
  290. if ( TimeoutCount > 2 ) {
  291. #if DBG
  292. DebugBreak();
  293. #endif
  294. }
  295. #if DBG
  296. OutputDebugString(TEXT("[AcquireResourceExclusive]: Re-Waiting\n"));
  297. #endif
  298. goto rewait;
  299. } else if ( ret != WAIT_OBJECT_0 ) {
  300. #if DBG
  301. OutputDebugString(TEXT("[AcquireResourceExclusive]: WaitForSingleObject Failed\n"));
  302. #endif
  303. }
  304. }
  305. return TRUE;
  306. }
  307. BOOL
  308. ReleaseResource(
  309. IN PRTL_RESOURCE Resource
  310. )
  311. /*++
  312. Routine Description:
  313. This routine release the input resource. The resource can have been
  314. acquired for either shared or exclusive access.
  315. Arguments:
  316. Resource - Supplies the resource to release
  317. Return Value:
  318. None.
  319. --*/
  320. {
  321. LONG PreviousCount;
  322. //
  323. // Enter the critical section
  324. //
  325. EnterCriticalSection(&Resource->CriticalSection);
  326. //
  327. // Test if the resource is acquired for shared or exclusive access
  328. //
  329. if (Resource->NumberOfActive > 0) {
  330. //
  331. // Releasing shared access to the resource, so decrement
  332. // the number of shared users
  333. //
  334. Resource->NumberOfActive -= 1;
  335. //
  336. // If the resource is now available and there is a waiting
  337. // exclusive user then give the resource to the waiting thread
  338. //
  339. if ((Resource->NumberOfActive == 0) &&
  340. (Resource->NumberOfWaitingExclusive > 0)) {
  341. //
  342. // Set the resource state to exclusive (but not owned),
  343. // decrement the number of waiting exclusive, and release
  344. // one exclusive waiter
  345. //
  346. Resource->NumberOfActive = -1;
  347. Resource->ExclusiveOwnerThread = NULL;
  348. Resource->NumberOfWaitingExclusive -= 1;
  349. if ( !ReleaseSemaphore(
  350. Resource->ExclusiveSemaphore,
  351. 1,
  352. &PreviousCount
  353. )) {
  354. return FALSE;
  355. }
  356. }
  357. } else if (Resource->NumberOfActive < 0) {
  358. //
  359. // Releasing exclusive access to the resource, so increment the
  360. // number of active by one. And continue testing only
  361. // if the resource is now available.
  362. //
  363. Resource->NumberOfActive += 1;
  364. if (Resource->NumberOfActive == 0) {
  365. //
  366. // The resource is now available. Remove ourselves as the
  367. // owner thread
  368. //
  369. Resource->ExclusiveOwnerThread = NULL;
  370. //
  371. // If there is another waiting exclusive then give the resource
  372. // to it.
  373. //
  374. if (Resource->NumberOfWaitingExclusive > 0) {
  375. //
  376. // Set the resource to exclusive, and its owner undefined.
  377. // Decrement the number of waiting exclusive and release one
  378. // exclusive waiter
  379. //
  380. Resource->NumberOfActive = -1;
  381. Resource->NumberOfWaitingExclusive -= 1;
  382. if ( !ReleaseSemaphore(
  383. Resource->ExclusiveSemaphore,
  384. 1,
  385. &PreviousCount
  386. )) {
  387. return FALSE;
  388. }
  389. //
  390. // Check to see if there are waiting shared, who should now get
  391. // the resource
  392. //
  393. } else if (Resource->NumberOfWaitingShared > 0) {
  394. //
  395. // Set the new state to indicate that all of the shared
  396. // requesters have access and there are no more waiting
  397. // shared requesters, and then release all of the shared
  398. // requsters
  399. //
  400. Resource->NumberOfActive = Resource->NumberOfWaitingShared;
  401. Resource->NumberOfWaitingShared = 0;
  402. if ( !ReleaseSemaphore(
  403. Resource->SharedSemaphore,
  404. Resource->NumberOfActive,
  405. &PreviousCount
  406. )) {
  407. return FALSE;
  408. }
  409. }
  410. }
  411. #if DBG
  412. } else {
  413. //
  414. // The resource isn't current acquired, there is nothing to release
  415. // so tell the user the mistake
  416. //
  417. OutputDebugString(TEXT("[ReleaseResource] - Resource released too many times!\n"));
  418. DebugBreak();
  419. #endif
  420. }
  421. //
  422. // Exit the critical section, and return to the caller
  423. //
  424. LeaveCriticalSection(&Resource->CriticalSection);
  425. return TRUE;
  426. }
  427. BOOL
  428. ConvertSharedToExclusive(
  429. IN PRTL_RESOURCE Resource
  430. )
  431. /*++
  432. Routine Description:
  433. This routine converts a resource acquired for shared access into
  434. one acquired for exclusive access. Upon return from the procedure
  435. the resource is acquired for exclusive access
  436. Arguments:
  437. Resource - Supplies the resource to acquire for shared access, it
  438. must already be acquired for shared access
  439. Return Value:
  440. None
  441. --*/
  442. {
  443. DWORD ret;
  444. DWORD TimeoutTime = _RES_TIMEOUT;
  445. ULONG TimeoutCount = 0;
  446. //
  447. // Enter the critical section
  448. //
  449. EnterCriticalSection(&Resource->CriticalSection);
  450. //
  451. // If there is only one shared user (it's us) and we can acquire the
  452. // resource for exclusive access.
  453. //
  454. if (Resource->NumberOfActive == 1) {
  455. //
  456. // The resource is ours, so indicate that we have it and
  457. // exit the critical section, and return
  458. //
  459. Resource->NumberOfActive = -1;
  460. Resource->ExclusiveOwnerThread = (HANDLE) ULongToPtr(GetCurrentThreadId());
  461. LeaveCriticalSection(&Resource->CriticalSection);
  462. return TRUE;
  463. }
  464. //
  465. // If the resource is currently acquired exclusive and it's us then
  466. // we already have exclusive access
  467. //
  468. if ((Resource->NumberOfActive < 0) &&
  469. (Resource->ExclusiveOwnerThread == (HANDLE) ULongToPtr(GetCurrentThreadId()))) {
  470. //
  471. // We already have exclusive access to the resource so we'll just
  472. // exit the critical section and return
  473. //
  474. LeaveCriticalSection(&Resource->CriticalSection);
  475. return TRUE;
  476. }
  477. //
  478. // If the resource is acquired by more than one shared then we need
  479. // to wait to get exclusive access to the resource
  480. //
  481. if (Resource->NumberOfActive > 1) {
  482. //
  483. // To wait we will decrement the fact that we have the resource for
  484. // shared, and then loop waiting on the exclusive lock, and then
  485. // testing to see if we can get exclusive access to the resource
  486. //
  487. Resource->NumberOfActive -= 1;
  488. while (TRUE) {
  489. //
  490. // Increment the number of waiting exclusive, exit and critical
  491. // section and wait on the exclusive semaphore
  492. //
  493. Resource->NumberOfWaitingExclusive += 1;
  494. LeaveCriticalSection(&Resource->CriticalSection);
  495. rewait:
  496. if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
  497. TimeoutTime = INFINITE;
  498. }
  499. ret = WaitForSingleObject(
  500. Resource->ExclusiveSemaphore,
  501. TimeoutTime
  502. );
  503. if ( ret == WAIT_TIMEOUT ) {
  504. #if DBG
  505. OutputDebugString( TEXT("[ConverSharedToExclusive] Sem timeout\n") );
  506. #endif
  507. TimeoutCount++;
  508. if ( TimeoutCount > 2 ) {
  509. #if DBG
  510. DebugBreak();
  511. #endif
  512. }
  513. #if DBG
  514. OutputDebugString(TEXT("[ConvertSharedToExclusive]: Re-Waiting\n"));
  515. #endif
  516. goto rewait;
  517. } else if ( ret != WAIT_OBJECT_0 ) {
  518. #if DBG
  519. OutputDebugString(TEXT("[ConvertSharedToExclusive]: WaitForSingleObject Failed\n"));
  520. #endif
  521. return FALSE;
  522. }
  523. //
  524. // Enter the critical section
  525. //
  526. EnterCriticalSection(&Resource->CriticalSection);
  527. //
  528. // If there are no shared users and it is not currently acquired
  529. // for exclusive use then we can acquire the resource for
  530. // exclusive access. We can also acquire it if the resource
  531. // indicates exclusive access but there isn't currently an owner
  532. //
  533. if ((Resource->NumberOfActive == 0)
  534. ||
  535. ((Resource->NumberOfActive == -1) &&
  536. (Resource->ExclusiveOwnerThread == NULL))) {
  537. //
  538. // The resource is ours, so indicate that we have it and
  539. // exit the critical section and return.
  540. //
  541. Resource->NumberOfActive = -1;
  542. Resource->ExclusiveOwnerThread = (HANDLE) ULongToPtr(GetCurrentThreadId());
  543. LeaveCriticalSection(&Resource->CriticalSection);
  544. return TRUE;
  545. }
  546. //
  547. // Otherwise check to see if we already have exclusive access to
  548. // the resource and can simply recusively acquire it again.
  549. //
  550. if (Resource->ExclusiveOwnerThread == (HANDLE) ULongToPtr(GetCurrentThreadId())) {
  551. //
  552. // The resource is ours (recusively) so indicate that we have
  553. // it and exit the critical section and return.
  554. //
  555. Resource->NumberOfActive -= 1;
  556. LeaveCriticalSection(&Resource->CriticalSection);
  557. return TRUE;
  558. }
  559. }
  560. }
  561. //
  562. // The resource is not currently acquired for shared so this is a
  563. // spurious call
  564. //
  565. #if DBG
  566. OutputDebugString(TEXT("[ConvertSharedToExclusive]: Failed error - SHARED_RESOURCE_CONV_ERROR\n"));
  567. DebugBreak();
  568. #endif
  569. return FALSE;
  570. }
  571. BOOL
  572. ConvertExclusiveToShared(
  573. IN PRTL_RESOURCE Resource
  574. )
  575. /*++
  576. Routine Description:
  577. This routine converts a resource acquired for exclusive access into
  578. one acquired for shared access. Upon return from the procedure
  579. the resource is acquired for shared access
  580. Arguments:
  581. Resource - Supplies the resource to acquire for shared access, it
  582. must already be acquired for exclusive access
  583. Return Value:
  584. None
  585. --*/
  586. {
  587. LONG PreviousCount;
  588. //
  589. // Enter the critical section
  590. //
  591. EnterCriticalSection(&Resource->CriticalSection);
  592. //
  593. // If there is only one shared user (it's us) and we can acquire the
  594. // resource for exclusive access.
  595. //
  596. if (Resource->NumberOfActive == -1) {
  597. Resource->ExclusiveOwnerThread = NULL;
  598. //
  599. // Check to see if there are waiting shared, who should now get the
  600. // resource along with us
  601. //
  602. if (Resource->NumberOfWaitingShared > 0) {
  603. //
  604. // Set the new state to indicate that all of the shared requesters
  605. // have access including us, and there are no more waiting shared
  606. // requesters, and then release all of the shared requsters
  607. //
  608. Resource->NumberOfActive = Resource->NumberOfWaitingShared + 1;
  609. Resource->NumberOfWaitingShared = 0;
  610. if ( !ReleaseSemaphore(
  611. Resource->SharedSemaphore,
  612. Resource->NumberOfActive - 1,
  613. &PreviousCount
  614. )) {
  615. return FALSE;
  616. }
  617. } else {
  618. //
  619. // There is no one waiting for shared access so it's only ours
  620. //
  621. Resource->NumberOfActive = 1;
  622. }
  623. LeaveCriticalSection(&Resource->CriticalSection);
  624. return TRUE;
  625. }
  626. //
  627. // The resource is not currently acquired for exclusive, or we've
  628. // recursively acquired it, so this must be a spurious call
  629. //
  630. #if DBG
  631. OutputDebugString(TEXT("[ConvertExclusiveToShared] Failed error - SHARED_RESOURCE_CONV_ERROR\n"));
  632. DebugBreak();
  633. #endif
  634. return FALSE;
  635. }
  636. VOID
  637. DeleteResource (
  638. IN PRTL_RESOURCE Resource
  639. )
  640. /*++
  641. Routine Description:
  642. This routine deletes (i.e., uninitializes) the input resource variable
  643. Arguments:
  644. Resource - Supplies the resource variable being deleted
  645. Return Value:
  646. None
  647. --*/
  648. {
  649. DeleteCriticalSection( &Resource->CriticalSection );
  650. CloseHandle(Resource->SharedSemaphore);
  651. CloseHandle(Resource->ExclusiveSemaphore);
  652. RtlZeroMemory( Resource, sizeof( *Resource ) );
  653. return;
  654. }