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.

608 lines
16 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. sputils.c
  5. Abstract:
  6. Core sputils library file
  7. Author:
  8. Jamie Hunter (JamieHun) Jun-27-2000
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. typedef ULONG (__cdecl *PFNDbgPrintEx)(IN ULONG ComponentId,IN ULONG Level,IN PCH Format, ...);
  14. static PFNDbgPrintEx pfnDbgPrintEx = NULL;
  15. static BOOL fInitDebug = FALSE;
  16. static LONG RefCount = 0; // when this falls back to zero, release all resources
  17. static BOOL SucceededInit = FALSE;
  18. #define COUNTING 1
  19. #if COUNTING
  20. static DWORD pSpCheckHead = 0xaabbccdd;
  21. static LONG pSpFailCount = 0;
  22. static LONG pSpInitCount = 0;
  23. static LONG pSpUninitCount = 0;
  24. static LONG pSpConflictCount = 0;
  25. static BOOL pSpDoneInit = FALSE;
  26. static BOOL pSpFailedInit = FALSE;
  27. static DWORD pSpCheckTail = 0xddccbbaa;
  28. #endif
  29. //
  30. // At some point, a thread, process or module will call pSetupInitializeUtils,
  31. // and follow by a call to pSetupUninitializeUtils when done (cleanup)
  32. //
  33. // prior to this point, there's been no initialization other than static
  34. // constants (above) pSetupInitializeUtils and pSetupUninitializeUtils must be
  35. // mut-ex with each other and themselves
  36. // thread A may call pSetupInitializeUtils while thread B is calling
  37. // pSetupUninitializeUtils, the init in this case must succeed
  38. // we can't use a single mutex or event object, since it must be cleaned up
  39. // when pSetupUninitializeUtils succeeds
  40. // we can't use a simple user-mode spin-lock, since priorities may be different,
  41. // and it's just plain ugly using Sleep(0)
  42. // so we have the _AcquireInitMutex and _ReleaseInitMutex implementations below
  43. // it's guarenteed that when _AcquireInitMutex returns, it is not using any
  44. // resources to hold the lock
  45. // it will hold an event object if the thread is blocked, per blocked thread.
  46. // This is ok since the number of blocked threads at any time will be few.
  47. //
  48. // It works as follows:
  49. //
  50. // a linked list of requests is maintained, with head at pWaitHead
  51. // The head is interlocked, and when an item is inserted at pWaitHead
  52. // it's entries must be valid, and can no longer be touched until
  53. // the mutex is acquired.
  54. //
  55. // if the request is the very first, it need not block, will not block,
  56. // as (at worst) the other thread has just removed it's request from the head
  57. // and is about to return. The thread that inserts the first request into the
  58. // list automatically owns the mutex.
  59. //
  60. // if the request is anything but the first, it will have an event object
  61. // that will eventually be signalled, and at that point owns the mutex.
  62. //
  63. // the Thread that owns the mutex may modify anything on the wait-list,
  64. // including pWaitHead.
  65. //
  66. // If the thread that owns the mutex is pWaitHead at the point it's releasing
  67. // mutex, it does not need to signal anyone. This is protected by
  68. // InterlockedCompareExchangePointer. If it finds itself in this state, the next
  69. // pSetupInitializeUtils will automatically obtain the mutex, also protected
  70. // by InterlockedCompareExchangePointer.
  71. //
  72. // If there are waiting entries in the list, then the tail-most waiting entry is
  73. // signalled, at which point the related thread now owns the mutex.
  74. //
  75. #ifdef UNICODE
  76. typedef struct _LinkWaitList {
  77. HANDLE hEvent; // for this item
  78. struct _LinkWaitList *pNext; // from Head to Tail
  79. struct _LinkWaitList *pPrev; // from Tail to Head
  80. } LinkWaitList;
  81. static LinkWaitList * pWaitHead = NULL; // insert new wait items here
  82. static
  83. BOOL
  84. _AcquireInitMutex(
  85. OUT LinkWaitList *pEntry
  86. )
  87. /*++
  88. Routine Description:
  89. Atomically acquire process mutex
  90. with no pre-requisite initialization other than
  91. static globals.
  92. Each blocked call will require an event to be created.
  93. Requests cannot be nested per thread (deadlock will occur)
  94. Arguments:
  95. pEntry - structure to hold mutex information. This structure
  96. must persist until call to _ReleaseInitMutex.
  97. Global:pWaitHead - atomic linked list of mutex requests
  98. Return Value:
  99. TRUE if mutex acquired.
  100. FALSE on failure (no resources)
  101. --*/
  102. {
  103. LinkWaitList *pTop;
  104. DWORD res;
  105. pEntry->pPrev = NULL;
  106. pEntry->pNext = NULL;
  107. pEntry->hEvent = NULL;
  108. //
  109. // fast lock, this will only succeed if we're the first and we have no reason to wait
  110. // this saves us needlessly creating an event
  111. //
  112. if(!InterlockedCompareExchangePointer(&pWaitHead,pEntry,NULL)) {
  113. return TRUE;
  114. }
  115. #if COUNTING
  116. InterlockedIncrement(&pSpConflictCount);
  117. #endif
  118. //
  119. // someone has (or, at least a moment ago, had) the lock, so we need an event
  120. //
  121. pEntry->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  122. if(!pEntry->hEvent) {
  123. return FALSE;
  124. }
  125. //
  126. // once pEntry is added to list, it cannot be touched until
  127. // WaitSingleObject is satisfied (unless we were the first)
  128. // if pWaitHead changes in the middle of the loop, we'll repeat again
  129. //
  130. do {
  131. pTop = pWaitHead;
  132. pEntry->pNext = pTop;
  133. } while (pTop != InterlockedCompareExchangePointer(&pWaitHead,pEntry,pTop));
  134. if(pTop) {
  135. //
  136. // we're not the first on the list
  137. // the owner of pTop will signal our event, wait for it.
  138. //
  139. res = WaitForSingleObject(pEntry->hEvent,INFINITE);
  140. } else {
  141. res = WAIT_OBJECT_0;
  142. }
  143. //
  144. // don't need event any more, the fact we've been signalled indicates we've
  145. // now got the lock there's no race condition wrt pEntry
  146. // (however someone can still insert themselves at head pointing to us)
  147. //
  148. CloseHandle(pEntry->hEvent);
  149. pEntry->hEvent = NULL;
  150. if(res != WAIT_OBJECT_0) {
  151. MYASSERT(res == WAIT_OBJECT_0);
  152. return FALSE;
  153. }
  154. return TRUE;
  155. }
  156. static
  157. VOID
  158. _ReleaseInitMutex(
  159. IN LinkWaitList *pEntry
  160. )
  161. /*++
  162. Routine Description:
  163. release process mutex previously acquired by _AcquireInitMutex
  164. thread must own the mutex
  165. no resources required for this action.
  166. This call may only be done once for each _AcquireInitMutex
  167. Arguments:
  168. pEntry - holding mutex information. This structure
  169. must have been initialized by _AcquireInitMutex.
  170. Global:pWaitHead - atomic linked list of mutex requests
  171. Return Value:
  172. none.
  173. --*/
  174. {
  175. LinkWaitList *pHead;
  176. LinkWaitList *pWalk;
  177. LinkWaitList *pPrev;
  178. MYASSERT(!pEntry->pNext);
  179. pHead = InterlockedCompareExchangePointer(&pWaitHead,NULL,pEntry);
  180. if(pHead == pEntry) {
  181. //
  182. // we were at head of list as well as at tail of list
  183. // list has now been reset to NULL
  184. // and may even already contain an entry due to a pLock call
  185. return;
  186. }
  187. if(!pEntry->pPrev) {
  188. //
  189. // we need to walk down list from pHead to pEntry
  190. // at the same time, remember back links
  191. // so we don't need to do this every time
  192. // note, we will never get here if pHead==pEntry
  193. //
  194. MYASSERT(pHead);
  195. MYASSERT(!pHead->pPrev);
  196. for(pWalk = pHead;pWalk != pEntry;pWalk = pWalk->pNext) {
  197. MYASSERT(pWalk->pNext);
  198. MYASSERT(!pWalk->pNext->pPrev);
  199. pWalk->pNext->pPrev = pWalk;
  200. }
  201. }
  202. pPrev = pEntry->pPrev;
  203. pPrev->pNext = NULL; // aids debugging, even in free build.
  204. SetEvent(pPrev->hEvent);
  205. return;
  206. }
  207. #else
  208. //
  209. // ANSI functions *MUST* work on Win95
  210. // to support install of Whistler
  211. // InterlockedCompareExchange(Pointer)
  212. // is not supported
  213. // so we'll use something simple/functional instead
  214. // that uses the supported InterlockedExchange
  215. //
  216. static LONG SimpleCritSec = FALSE;
  217. typedef PVOID LinkWaitList;
  218. static
  219. BOOL
  220. _AcquireInitMutex(
  221. OUT LinkWaitList *pEntry
  222. )
  223. {
  224. while(InterlockedExchange(&SimpleCritSec,TRUE) == TRUE) {
  225. //
  226. // release our timeslice
  227. // we should rarely be spinning here
  228. // starvation can occur in some circumstances
  229. // if initializing threads are of different priorities
  230. //
  231. Sleep(0);
  232. }
  233. return TRUE;
  234. }
  235. static
  236. VOID
  237. _ReleaseInitMutex(
  238. IN LinkWaitList *pEntry
  239. )
  240. {
  241. if(InterlockedExchange(&SimpleCritSec,FALSE) == FALSE) {
  242. MYASSERT(0 && SimpleCritSec);
  243. }
  244. }
  245. #endif
  246. BOOL
  247. pSetupInitializeUtils(
  248. VOID
  249. )
  250. /*++
  251. Routine Description:
  252. Initialize this library
  253. balance each successful call to this function with
  254. equal number of calls to pSetupUninitializeUtils
  255. Arguments:
  256. none
  257. Return Value:
  258. TRUE if init succeeded, FALSE otherwise
  259. --*/
  260. {
  261. LinkWaitList Lock;
  262. if(!_AcquireInitMutex(&Lock)) {
  263. #if COUNTING
  264. InterlockedIncrement(&pSpFailCount);
  265. #endif
  266. return FALSE;
  267. }
  268. #if COUNTING
  269. InterlockedIncrement(&pSpInitCount);
  270. #endif
  271. RefCount++;
  272. if(RefCount==1) {
  273. pSpDoneInit = TRUE;
  274. SucceededInit = _pSpUtilsMemoryInitialize();
  275. if(!SucceededInit) {
  276. pSpFailedInit = TRUE;
  277. _pSpUtilsMemoryUninitialize();
  278. }
  279. }
  280. _ReleaseInitMutex(&Lock);
  281. return SucceededInit;
  282. }
  283. BOOL
  284. pSetupUninitializeUtils(
  285. VOID
  286. )
  287. /*++
  288. Routine Description:
  289. Uninitialize this library
  290. This should be called for each successful call to
  291. pSetupInitializeUtils
  292. Arguments:
  293. none
  294. Return Value:
  295. TRUE if cleanup succeeded, FALSE otherwise
  296. --*/
  297. {
  298. LinkWaitList Lock;
  299. #if COUNTING
  300. InterlockedIncrement(&pSpUninitCount);
  301. #endif
  302. if(!SucceededInit) {
  303. return FALSE;
  304. }
  305. if(!_AcquireInitMutex(&Lock)) {
  306. return FALSE;
  307. }
  308. RefCount--;
  309. if(RefCount == 0) {
  310. _pSpUtilsMemoryUninitialize();
  311. SucceededInit = FALSE;
  312. }
  313. _ReleaseInitMutex(&Lock);
  314. return TRUE;
  315. }
  316. VOID
  317. _pSpUtilsAssertFail(
  318. IN PCSTR FileName,
  319. IN UINT LineNumber,
  320. IN PCSTR Condition
  321. )
  322. {
  323. int i;
  324. CHAR Name[MAX_PATH];
  325. PCHAR p;
  326. LPSTR Msg;
  327. DWORD msglen;
  328. DWORD sz;
  329. //
  330. // obtain module name
  331. //
  332. sz = GetModuleFileNameA(NULL,Name,MAX_PATH);
  333. if((sz == 0) || (sz > MAX_PATH)) {
  334. strcpy(Name,"?");
  335. }
  336. if(p = strrchr(Name,'\\')) {
  337. p++;
  338. } else {
  339. p = Name;
  340. }
  341. msglen = strlen(p)+strlen(FileName)+strlen(Condition)+128;
  342. //
  343. // assert might be out of memory condition
  344. // stack alloc is more likely to succeed than memory alloc
  345. //
  346. try {
  347. Msg = (LPSTR)_alloca(msglen);
  348. wsprintfA(
  349. Msg,
  350. "SPUTILS: Assertion failure at line %u in file %s!%s: %s\r\n",
  351. LineNumber,
  352. p,
  353. FileName,
  354. Condition
  355. );
  356. } except (EXCEPTION_EXECUTE_HANDLER) {
  357. Msg = "SpUTILS ASSERT!!!! (out of stack)\r\n";
  358. }
  359. OutputDebugStringA(Msg);
  360. DebugBreak();
  361. }
  362. VOID
  363. pSetupDebugPrintEx(
  364. DWORD Level,
  365. PCTSTR format,
  366. ... OPTIONAL
  367. )
  368. /*++
  369. Routine Description:
  370. Send a formatted string to the debugger.
  371. Note that this is expected to work cross-platform, but use preferred debugger
  372. Arguments:
  373. format - standard printf format string.
  374. Return Value:
  375. NONE.
  376. --*/
  377. {
  378. TCHAR buf[1026]; // bigger than max size
  379. va_list arglist;
  380. if (!fInitDebug) {
  381. pfnDbgPrintEx = (PFNDbgPrintEx)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "DbgPrintEx");
  382. fInitDebug = TRUE;
  383. }
  384. va_start(arglist, format);
  385. wvsprintf(buf, format, arglist);
  386. if (pfnDbgPrintEx) {
  387. #ifdef UNICODE
  388. (*pfnDbgPrintEx)(DPFLTR_SETUP_ID, Level, "%ls",buf);
  389. #else
  390. (*pfnDbgPrintEx)(DPFLTR_SETUP_ID, Level, "%s",buf);
  391. #endif
  392. } else {
  393. OutputDebugString(buf);
  394. }
  395. }
  396. LONG
  397. _pSpUtilsExceptionFilter(
  398. DWORD ExceptionCode
  399. )
  400. /*++
  401. Routine Description:
  402. This routine acts as the exception filter for SpUtils. We will handle all
  403. exceptions except for the following:
  404. EXCEPTION_SPAPI_UNRECOVERABLE_STACK_OVERFLOW
  405. This means we previously tried to reinstate the guard page after a
  406. stack overflow, but couldn't. We have no choice but to let the
  407. exception trickle all the way back out.
  408. EXCEPTION_POSSIBLE_DEADLOCK
  409. We are not allowed to handle this exception which fires when the
  410. deadlock detection gflags option has been enabled.
  411. Arguments:
  412. ExceptionCode - Specifies the exception that occurred (i.e., as returned
  413. by GetExceptionCode)
  414. Return Value:
  415. If the exception should be handled, the return value is
  416. EXCEPTION_EXECUTE_HANDLER.
  417. Otherwise, the return value is EXCEPTION_CONTINUE_SEARCH.
  418. --*/
  419. {
  420. if((ExceptionCode == EXCEPTION_SPAPI_UNRECOVERABLE_STACK_OVERFLOW) ||
  421. (ExceptionCode == EXCEPTION_POSSIBLE_DEADLOCK)) {
  422. return EXCEPTION_CONTINUE_SEARCH;
  423. } else {
  424. return EXCEPTION_EXECUTE_HANDLER;
  425. }
  426. }
  427. VOID
  428. _pSpUtilsExceptionHandler(
  429. IN DWORD ExceptionCode,
  430. IN DWORD AccessViolationError,
  431. OUT PDWORD Win32ErrorCode OPTIONAL
  432. )
  433. /*++
  434. Routine Description:
  435. This routine, called from inside an exception handler block, provides
  436. common exception handling functionality to be used throughout SpUtils.
  437. It has knowledge of which exceptions require extra work (e.g., stack
  438. overflow), and also optionally returns a Win32 error code that represents
  439. the exception. (The caller specifies the error to be used when an access
  440. violation occurs.)
  441. Arguments:
  442. ExceptionCode - Specifies the exception that occurred (i.e., as returned
  443. by GetExceptionCode)
  444. AccessViolationError - Specifies the Win32 error code to be returned via
  445. the optional Win32ErrorCode OUT parameter when the exception
  446. encountered was EXCEPTION_ACCESS_VIOLATION.
  447. Win32ErrorCode - Optionally, supplies the address of a DWORD that receives
  448. the Win32 error code corresponding to the exception (taking into
  449. account the AccessViolationError code supplied above, if applicable).
  450. Return Value:
  451. None
  452. --*/
  453. {
  454. DWORD Err;
  455. //
  456. // Exception codes we should never attempt to handle...
  457. //
  458. MYASSERT(ExceptionCode != EXCEPTION_SPAPI_UNRECOVERABLE_STACK_OVERFLOW);
  459. MYASSERT(ExceptionCode != EXCEPTION_POSSIBLE_DEADLOCK);
  460. if(ExceptionCode == STATUS_STACK_OVERFLOW) {
  461. if(_resetstkoflw()) {
  462. Err = ERROR_STACK_OVERFLOW;
  463. } else {
  464. //
  465. // Couldn't recover from stack overflow!
  466. //
  467. RaiseException(EXCEPTION_SPAPI_UNRECOVERABLE_STACK_OVERFLOW,
  468. EXCEPTION_NONCONTINUABLE,
  469. 0,
  470. NULL
  471. );
  472. //
  473. // We should never get here, but initialize Err to make code
  474. // analysis tools happy...
  475. //
  476. Err = ERROR_UNRECOVERABLE_STACK_OVERFLOW;
  477. }
  478. } else {
  479. //
  480. // Except for a couple of special cases (for backwards-compatibility),
  481. // we have to report an "unknown exception", since the function we'd
  482. // like to use (RtlNtStatusToDosErrorNoTeb) isn't available for use by
  483. // clients of sputils.
  484. //
  485. switch(ExceptionCode) {
  486. case EXCEPTION_ACCESS_VIOLATION :
  487. Err = AccessViolationError;
  488. break;
  489. case EXCEPTION_IN_PAGE_ERROR :
  490. Err = ERROR_READ_FAULT;
  491. break;
  492. default :
  493. Err = ERROR_UNKNOWN_EXCEPTION;
  494. break;
  495. }
  496. }
  497. if(Win32ErrorCode) {
  498. *Win32ErrorCode = Err;
  499. }
  500. }