Source code of Windows XP (NT5)
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.

469 lines
11 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. pEntry->hEvent = NULL;
  141. }
  142. //
  143. // don't need event any more, the fact we've been signalled indicates we've now got the lock
  144. //
  145. CloseHandle(pEntry->hEvent);
  146. if(res != WAIT_OBJECT_0) {
  147. MYASSERT(res == WAIT_OBJECT_0);
  148. return FALSE;
  149. }
  150. return TRUE;
  151. }
  152. static
  153. VOID
  154. _ReleaseInitMutex(
  155. IN LinkWaitList *pEntry
  156. )
  157. /*++
  158. Routine Description:
  159. release process mutex previously acquired by _AcquireInitMutex
  160. thread must own the mutex
  161. no resources required for this action.
  162. This call may only be done once for each _AcquireInitMutex
  163. Arguments:
  164. pEntry - holding mutex information. This structure
  165. must have been initialized by _AcquireInitMutex.
  166. Global:pWaitHead - atomic linked list of mutex requests
  167. Return Value:
  168. none.
  169. --*/
  170. {
  171. LinkWaitList *pHead;
  172. LinkWaitList *pWalk;
  173. LinkWaitList *pPrev;
  174. MYASSERT(!pEntry->pNext);
  175. pHead = InterlockedCompareExchangePointer(&pWaitHead,NULL,pEntry);
  176. if(pHead == pEntry) {
  177. //
  178. // we were at head of list as well as at tail of list
  179. // list has now been reset to NULL
  180. // and may even already contain an entry due to a pLock call
  181. return;
  182. }
  183. if(!pEntry->pPrev) {
  184. //
  185. // we need to walk down list from pHead to pEntry
  186. // at the same time, remember back links
  187. // so we don't need to do this every time
  188. // note, we will never get here if pHead==pEntry
  189. //
  190. MYASSERT(pHead);
  191. MYASSERT(!pHead->pPrev);
  192. for(pWalk = pHead;pWalk != pEntry;pWalk = pWalk->pNext) {
  193. MYASSERT(pWalk->pNext);
  194. MYASSERT(!pWalk->pNext->pPrev);
  195. pWalk->pNext->pPrev = pWalk;
  196. }
  197. }
  198. pPrev = pEntry->pPrev;
  199. pPrev->pNext = NULL; // aids debugging, even in free build.
  200. SetEvent(pPrev->hEvent);
  201. return;
  202. }
  203. #else
  204. //
  205. // ANSI functions *MUST* work on Win95
  206. // to support install of Whistler
  207. // InterlockedCompareExchange(Pointer)
  208. // is not supported
  209. // so we'll use something simple/functional instead
  210. // that uses the supported InterlockedExchange
  211. //
  212. static LONG SimpleCritSec = FALSE;
  213. typedef PVOID LinkWaitList;
  214. static
  215. BOOL
  216. _AcquireInitMutex(
  217. OUT LinkWaitList *pEntry
  218. )
  219. {
  220. while(InterlockedExchange(&SimpleCritSec,TRUE) == TRUE) {
  221. //
  222. // release our timeslice
  223. // we should rarely be spinning here
  224. // starvation can occur in some circumstances
  225. // if initializing threads are of different priorities
  226. //
  227. Sleep(0);
  228. }
  229. return TRUE;
  230. }
  231. static
  232. VOID
  233. _ReleaseInitMutex(
  234. IN LinkWaitList *pEntry
  235. )
  236. {
  237. if(InterlockedExchange(&SimpleCritSec,FALSE) == FALSE) {
  238. MYASSERT(0 && SimpleCritSec);
  239. }
  240. }
  241. #endif
  242. BOOL
  243. pSetupInitializeUtils(
  244. VOID
  245. )
  246. /*++
  247. Routine Description:
  248. Initialize this library
  249. balance each successful call to this function with
  250. equal number of calls to pSetupUninitializeUtils
  251. Arguments:
  252. none
  253. Return Value:
  254. TRUE if init succeeded, FALSE otherwise
  255. --*/
  256. {
  257. LinkWaitList Lock;
  258. if(!_AcquireInitMutex(&Lock)) {
  259. #if COUNTING
  260. InterlockedIncrement(&pSpFailCount);
  261. #endif
  262. return FALSE;
  263. }
  264. #if COUNTING
  265. InterlockedIncrement(&pSpInitCount);
  266. #endif
  267. RefCount++;
  268. if(RefCount==1) {
  269. pSpDoneInit = TRUE;
  270. SucceededInit = _pSpUtilsMemoryInitialize();
  271. if(!SucceededInit) {
  272. pSpFailedInit = TRUE;
  273. _pSpUtilsMemoryUninitialize();
  274. }
  275. }
  276. _ReleaseInitMutex(&Lock);
  277. return SucceededInit;
  278. }
  279. BOOL
  280. pSetupUninitializeUtils(
  281. VOID
  282. )
  283. /*++
  284. Routine Description:
  285. Uninitialize this library
  286. This should be called for each successful call to
  287. pSetupInitializeUtils
  288. Arguments:
  289. none
  290. Return Value:
  291. TRUE if cleanup succeeded, FALSE otherwise
  292. --*/
  293. {
  294. LinkWaitList Lock;
  295. #if COUNTING
  296. InterlockedIncrement(&pSpUninitCount);
  297. #endif
  298. if(!SucceededInit) {
  299. return FALSE;
  300. }
  301. if(!_AcquireInitMutex(&Lock)) {
  302. return FALSE;
  303. }
  304. RefCount--;
  305. if(RefCount == 0) {
  306. _pSpUtilsMemoryUninitialize();
  307. SucceededInit = FALSE;
  308. }
  309. _ReleaseInitMutex(&Lock);
  310. return TRUE;
  311. }
  312. VOID
  313. _pSpUtilsAssertFail(
  314. IN PSTR FileName,
  315. IN UINT LineNumber,
  316. IN PSTR Condition
  317. )
  318. {
  319. int i;
  320. CHAR Name[MAX_PATH];
  321. PCHAR p;
  322. LPSTR Msg;
  323. DWORD msglen;
  324. DWORD sz;
  325. //
  326. // obtain module name
  327. //
  328. sz = GetModuleFileNameA(NULL,Name,MAX_PATH);
  329. if((sz == 0) || (sz > MAX_PATH)) {
  330. strcpy(Name,"?");
  331. }
  332. if(p = strrchr(Name,'\\')) {
  333. p++;
  334. } else {
  335. p = Name;
  336. }
  337. msglen = strlen(p)+strlen(FileName)+strlen(Condition)+128;
  338. //
  339. // assert might be out of memory condition
  340. // stack alloc is more likely to succeed than memory alloc
  341. //
  342. try {
  343. Msg = (LPSTR)_alloca(msglen);
  344. wsprintfA(
  345. Msg,
  346. "SPUTILS: Assertion failure at line %u in file %s!%s: %s\r\n",
  347. LineNumber,
  348. p,
  349. FileName,
  350. Condition
  351. );
  352. } except (EXCEPTION_EXECUTE_HANDLER) {
  353. Msg = "SpUTILS ASSERT!!!! (out of stack)\r\n";
  354. }
  355. OutputDebugStringA(Msg);
  356. DebugBreak();
  357. }
  358. VOID
  359. pSetupDebugPrintEx(
  360. DWORD Level,
  361. PCTSTR format,
  362. ... OPTIONAL
  363. )
  364. /*++
  365. Routine Description:
  366. Send a formatted string to the debugger.
  367. Note that this is expected to work cross-platform, but use preferred debugger
  368. Arguments:
  369. format - standard printf format string.
  370. Return Value:
  371. NONE.
  372. --*/
  373. {
  374. TCHAR buf[1026]; // bigger than max size
  375. va_list arglist;
  376. if (!fInitDebug) {
  377. pfnDbgPrintEx = (PFNDbgPrintEx)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "DbgPrintEx");
  378. fInitDebug = TRUE;
  379. }
  380. va_start(arglist, format);
  381. wvsprintf(buf, format, arglist);
  382. if (pfnDbgPrintEx) {
  383. #ifdef UNICODE
  384. (*pfnDbgPrintEx)(DPFLTR_SETUP_ID, Level, "%ls",buf);
  385. #else
  386. (*pfnDbgPrintEx)(DPFLTR_SETUP_ID, Level, "%s",buf);
  387. #endif
  388. } else {
  389. OutputDebugString(buf);
  390. }
  391. }