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.

382 lines
8.8 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. mrsw.c
  5. Abstract:
  6. This module implements a multiple reader single write synchronization
  7. method.
  8. Author:
  9. Dave Hastings (daveh) creation-date 26-Jul-1995
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <windows.h>
  16. #include "wx86.h"
  17. #include "wx86nt.h"
  18. #include "cpuassrt.h"
  19. #include "config.h"
  20. #include "mrsw.h"
  21. #include "cpumain.h"
  22. #include "atomic.h"
  23. ASSERTNAME;
  24. MRSWOBJECT MrswEP; // Entrypoint MRSW synchronization object
  25. MRSWOBJECT MrswTC; // Translation cache MRSW synchronization object
  26. MRSWOBJECT MrswIndirTable; // Indirect Control Transfer Table synchronization object
  27. BOOL
  28. MrswInitializeObject(
  29. PMRSWOBJECT Mrsw
  30. )
  31. /*++
  32. Routine Description:
  33. This routine initializes the fields of Mrsw to their default values,
  34. and creates the events.
  35. Arguments:
  36. Mrsw -- Supplies a pointer to an MRSWOBJECT to initialize
  37. Return Value:
  38. TRUE on success, FALSE on failure.
  39. --*/
  40. {
  41. NTSTATUS Status;
  42. //
  43. // Initialize the counters
  44. //
  45. ZeroMemory(Mrsw, sizeof(MRSWOBJECT));
  46. //
  47. // Create the ReaderEvent and WriterEvent
  48. //
  49. Status = NtCreateEvent(&Mrsw->ReaderEvent,
  50. EVENT_ALL_ACCESS,
  51. NULL, // POBJECT_ATTRIBUTES
  52. NotificationEvent, // ManualReset
  53. FALSE); // InitialState
  54. if (!NT_SUCCESS(Status)) {
  55. return FALSE;
  56. }
  57. Status = NtCreateEvent(&Mrsw->WriterEvent,
  58. EVENT_ALL_ACCESS,
  59. NULL, // POBJECT_ATTRIBUTES
  60. SynchronizationEvent, // AutoReset
  61. FALSE); // InitialState
  62. if (!NT_SUCCESS(Status)) {
  63. NtClose(Mrsw->ReaderEvent);
  64. return FALSE;
  65. }
  66. return TRUE;
  67. }
  68. VOID
  69. PossibleMrswTimeout(
  70. PMRSWOBJECT Mrsw
  71. )
  72. /*++
  73. Routine Description:
  74. This function is called whenever an Mrsw function times out. It prompts
  75. the user, and if the user chooses Retry, the Mrsw function re-waits.
  76. If the user chooses Cancel, the CPU will attempt to launch NTSD and break
  77. into the debugger.
  78. Arguments:
  79. Mrsw -- Supplies the Mrsw which may have a deadlock
  80. Return Value:
  81. --*/
  82. {
  83. NTSTATUS Status;
  84. ULONG ErrorResponse;
  85. LOGPRINT((ERRORLOG, "WX86CPU: Possible deadlock in Mrsw %x\n", Mrsw));
  86. Status = NtRaiseHardError(
  87. STATUS_POSSIBLE_DEADLOCK | 0x10000000,
  88. 0,
  89. 0,
  90. NULL,
  91. OptionRetryCancel,
  92. &ErrorResponse);
  93. if (!NT_SUCCESS(Status) || ErrorResponse == ResponseCancel) {
  94. DbgBreakPoint();
  95. }
  96. }
  97. VOID
  98. MrswWriterEnter(
  99. PMRSWOBJECT Mrsw
  100. )
  101. /*++
  102. Routine Description:
  103. This function causes the caller to enter the Mrsw as the (single) writer.
  104. Arguments:
  105. Mrsw -- Supplies the Mrsw to enter
  106. Return Value:
  107. --*/
  108. {
  109. DWORD dwCounters;
  110. MRSWCOUNTERS Counters;
  111. NTSTATUS r;
  112. //
  113. // reset the reader event so that any readers that find the
  114. // WriterCount > 0 will actually wait. We have to do that now,
  115. // because if we wait, the reader might wait on the event before we
  116. // got it reset.
  117. //
  118. r= NtClearEvent(Mrsw->ReaderEvent);
  119. if (!NT_SUCCESS(r)) {
  120. #if DBG
  121. LOGPRINT((ERRORLOG, "WX86CPU: Got status %x from NtClearEvent\n", r));
  122. #endif
  123. RtlRaiseStatus(r);
  124. }
  125. //
  126. // Get the counters and increment the writer count
  127. // This is done atomically
  128. //
  129. dwCounters = MrswFetchAndIncrementWriter((DWORD *)&(Mrsw->Counters));
  130. Counters = *(PMRSWCOUNTERS)&dwCounters;
  131. CPUASSERTMSG(Counters.WriterCount != 0, "WriterCount overflowed");
  132. //
  133. // If there is a writer or a reader already, wait for them to finish
  134. //
  135. if ( (Counters.WriterCount > 1) || (Counters.ReaderCount) ) {
  136. NTSTATUS r;
  137. // Ensure We are not about to wait on ourselves.
  138. CPUASSERTMSG(Mrsw->WriterThreadId != ProxyGetCurrentThreadId(),
  139. "MrswWriterEnter() called twice by the same thread");
  140. for (;;) {
  141. r = NtWaitForSingleObject(
  142. Mrsw->WriterEvent,
  143. FALSE,
  144. &MrswTimeout
  145. );
  146. if (r == STATUS_TIMEOUT) {
  147. PossibleMrswTimeout(Mrsw);
  148. } else if (NT_SUCCESS(r)) {
  149. break;
  150. } else {
  151. #if DBG
  152. LOGPRINT((ERRORLOG, "WX86CPU: Got status %x from NtWaitForCriticalSection\n", r));
  153. #endif
  154. RtlRaiseStatus(r);
  155. }
  156. }
  157. }
  158. #if DBG
  159. CPUASSERTMSG(Mrsw->WriterThreadId == 0, "Another writer still is active.");
  160. Mrsw->WriterThreadId = ProxyGetCurrentThreadId();
  161. #endif
  162. }
  163. VOID
  164. MrswWriterExit(
  165. PMRSWOBJECT Mrsw
  166. )
  167. /*++
  168. Routine Description:
  169. This function causes the caller to exit the Mrsw. It will restart the
  170. next writer if there is one, or the readers if there are any
  171. Arguments:
  172. Mrsw -- Supplies the Mrsw to exit
  173. Return Value:
  174. --*/
  175. {
  176. DWORD dwCounters;
  177. MRSWCOUNTERS Counters;
  178. // Ensure we are the active writer
  179. CPUASSERTMSG(Mrsw->WriterThreadId == ProxyGetCurrentThreadId(),
  180. "MrswWriterExit: current thread is not the writer");
  181. //
  182. // Decrement the count of writers
  183. //
  184. #if DBG
  185. //
  186. // Set the thread id to 0 first, so if another writer comes along,
  187. // we don't zero out its thread id.
  188. //
  189. Mrsw->WriterThreadId = 0;
  190. #endif
  191. dwCounters = MrswFetchAndDecrementWriter((DWORD *)&(Mrsw->Counters));
  192. Counters = *(PMRSWCOUNTERS)&dwCounters;
  193. CPUASSERTMSG(Counters.WriterCount != 0xffff, "Writer underflow");
  194. //
  195. // Start a waiting writer if there is one. If there is no writer
  196. // start the waiting readers
  197. //
  198. if (Counters.WriterCount) {
  199. NtSetEvent(Mrsw->WriterEvent, NULL);
  200. } else {
  201. NtSetEvent(Mrsw->ReaderEvent, NULL);
  202. }
  203. }
  204. VOID
  205. MrswReaderEnter(
  206. PMRSWOBJECT Mrsw
  207. )
  208. /*++
  209. Routine Description:
  210. This function causes the caller to enter the Mrsw as a reader.
  211. Arguments:
  212. Mrsw -- Supplies the Mrsw to enter
  213. Return Value:
  214. --*/
  215. {
  216. DWORD dwCounters;
  217. MRSWCOUNTERS Counters;
  218. for (;;) {
  219. //
  220. // Increment the count of readers. If a writer is active, DO NOT
  221. // increment the read count. In that case, we must block until the
  222. // writer is done, then try again.
  223. //
  224. dwCounters = MrswFetchAndIncrementReader((DWORD *)&(Mrsw->Counters));
  225. Counters = *(PMRSWCOUNTERS)&dwCounters;
  226. CPUASSERTMSG(Counters.WriterCount || Counters.ReaderCount != 0,
  227. "Reader underflow");
  228. if (Counters.WriterCount) {
  229. NTSTATUS r;
  230. // Ensure we are not about to wait on ourselves.
  231. CPUASSERTMSG(Mrsw->WriterThreadId != ProxyGetCurrentThreadId(),
  232. "MRSWReaderEnter(): Thread already has write lock");
  233. //
  234. // There is a writer, wait for it to finish
  235. //
  236. for (;;) {
  237. r = NtWaitForSingleObject(
  238. Mrsw->ReaderEvent,
  239. FALSE,
  240. &MrswTimeout
  241. );
  242. if (r == STATUS_TIMEOUT) {
  243. PossibleMrswTimeout(Mrsw);
  244. } else if (NT_SUCCESS(r)) {
  245. break;
  246. } else {
  247. #if DBG
  248. LOGPRINT((ERRORLOG, "WX86CPU: Got status %x from NtWaitForCriticalSection\n", r));
  249. #endif
  250. RtlRaiseStatus(r);
  251. }
  252. }
  253. } else {
  254. //
  255. // No writer, so MrswFetchAndIncrementReader() incremented the
  256. // reader count - OK to exit out of the loop.
  257. //
  258. break;
  259. }
  260. }
  261. }
  262. VOID
  263. MrswReaderExit(
  264. PMRSWOBJECT Mrsw
  265. )
  266. /*++
  267. Routine Description:
  268. This function causes the caller to exit the Mrsw. If this was the last
  269. reader, it will restart the a writer if there is one.
  270. Arguments:
  271. Mrsw -- Supplies the Mrsw to exit
  272. Return Value:
  273. --*/
  274. {
  275. DWORD dwCounters;
  276. MRSWCOUNTERS Counters;
  277. //
  278. // Decrement the count of active readers
  279. //
  280. dwCounters = MrswFetchAndDecrementReader((DWORD *)&(Mrsw->Counters));
  281. Counters = *(PMRSWCOUNTERS)&dwCounters;
  282. CPUASSERTMSG(Counters.ReaderCount != 0xffff, "Reader underflow");
  283. if (Counters.WriterCount) {
  284. if (Counters.ReaderCount == 0) {
  285. //
  286. // This thread is the last reader, and there is a writer
  287. // waiting. Start the writer.
  288. //
  289. NtSetEvent(Mrsw->WriterEvent, NULL);
  290. }
  291. } else {
  292. //
  293. // There are no waiting readers and no writers, so do nothing.
  294. //
  295. }
  296. }