1 krisbash 1.1 #include <nits/base/Globals.h>
2
3 #ifdef _MSC_VER
4 #include <psapi.h>
5 #else
6 #include <stdio.h>
7 #endif
8 #include <pal/palcommon.h>
9 #include <pal/process.h>
10 #include <pal/strings.h>
11 #include <iostream>
12 #include <fstream>
13
14
15 using namespace std;
16 #if defined(_MSC_VER)
17 # pragma warning(disable : 4702)
18 #endif
19
20 struct SharedSegmentHeader
21 {
22 krisbash 1.1 union
23 {
24 ptrdiff_t status;
25 double doubleAlignment;
26 };
27 };
28
29 PAL_Char *Copy(_In_opt_z_ const PAL_Char * str)
30 {
31 if (str == NULL)
32 {
33 return NULL;
34 }
35
36 int length = (int)Tcslen(str);
37 PAL_Char *copy = new PAL_Char[length + 1];
38
39 if(!copy)
40 {
41 return NULL;
42 }
43 krisbash 1.1
44 Tcslcpy(copy, str, length + 1);
45
46 return copy;
47 }
48
49 PSTR ConvertStringToA(_In_opt_z_ const wchar_t *buf)
50 {
51 if(buf == NULL)
52 return NULL;
53
54 #ifdef _MSC_VER
55 int size = WideCharToMultiByte(CP_THREAD_ACP, 0, buf, -1, NULL, NULL, NULL, NULL);
56 #else
57 int size = wcstombs(NULL, buf, 0);
58 #endif
59 if (size == 0)
60 {
61 return NULL;
62 }
63
64 krisbash 1.1 #ifdef _MSC_VER
65 PSTR ansibuf = new char[size];
66 size = WideCharToMultiByte(CP_THREAD_ACP, 0, buf, -1, ansibuf, size, NULL, NULL);
67 #else
68 PSTR ansibuf = new char[size + 1];
69 size = wcstombs(ansibuf, buf, size + 1);
70 #endif
71 if (size == 0)
72 {
73 delete [] ansibuf;
74 return NULL;
75 }
76 #ifndef _MSC_VER
77 else
78 {
79 ansibuf[size] = '\0';
80 }
81 #endif
82
83 return ansibuf;
84 }
85 krisbash 1.1
86 PWSTR ConvertStringToW(_In_opt_z_ const char *buf)
87 {
88 if(buf == NULL)
89 return NULL;
90
91 #ifdef _MSC_VER
92 int size = MultiByteToWideChar(CP_THREAD_ACP, 0, buf, -1, NULL, NULL);
93 #else
94 int size = mbstowcs(0, buf, 0);
95 #endif
96 if (size == 0)
97 {
98 return NULL;
99 }
100
101
102
103 #ifdef _MSC_VER
104 PWSTR widebuf = new wchar_t[size];
105 size = MultiByteToWideChar(CP_THREAD_ACP, 0,
106 krisbash 1.1 buf, -1, widebuf, size);
107 #else
108 PWSTR widebuf = new wchar_t[size + 1];
109 size = mbstowcs(widebuf, buf, size);
110 #endif
111 if (size == 0)
112 {
113 delete [] widebuf;
114 return NULL;
115 }
116 #ifndef _MSC_VER
117 else
118 {
119 widebuf[size] = L'\0';
120 }
121 #endif
122
123 return widebuf;
124 }
125
126 char *ConvertPalCharToStringA(_In_opt_z_ const PAL_Char *buf)
127 krisbash 1.1 {
128 #ifdef CONFIG_ENABLE_WCHAR
129 return ConvertStringToA(buf);
130 #else
131 return Copy(buf);
132 #endif
133
134 }
135
136 PAL_Char *ConvertStringAToPalChar(_In_opt_z_ const char *buf)
137 {
138 if(buf == NULL)
139 return NULL;
140
141 #ifdef CONFIG_ENABLE_WCHAR
142 return ConvertStringToW(buf);
143 #else
144 return Copy(buf);
145 #endif
146 }
147
148 krisbash 1.1 namespace TestSystem {
149
150 Fault::Fault() : m_lock(0)
151 {
152 Reset(CallSite_NONE, 0, false);
153
154 m_mainThread = Thread_ID();;
155 m_minimumAttemptDifferentThread = 0;
156 }
157
158 void Fault::Reset(int site, int iteration, bool breakOnFault)
159 {
160 m_site = site;
161 m_iteration = iteration;
162 m_attempt = 0;
163
164 m_break = breakOnFault;
165 m_faulted = false;
166 m_filtered = false;
167 m_hit = CallSite_NONE;
168 m_line = 0;
169 krisbash 1.1 m_file[0] = '\0';
170
171 m_faultedAttempt = 0;
172 m_firstAttemptDifferentThread = 0;
173 }
174
175 Globals::Globals()
176 : m_version(SharedMemoryVersion),
177 m_runLock(0),
178 m_attachLock(0),
179 m_pipeLock(FALSE),
180 m_pipeChars(0)
181 {
182 m_pipe[0] = '\0';
183 m_debugger[0] = '\0';
184 m_binaryFilter[0] = '\0';
185 m_binaryTarget[0] = '\0';
186
187 for (int i = 0; i < InjectorListSize; i++)
188 m_injectors[i].process = 0;
189
190 krisbash 1.1 Reset();
191
192 for (int i = 0; i < ResultCount; i++)
193 {
194 m_statistics[i] = 0;
195 }
196 }
197
198 Globals::~Globals()
199 {
200 }
201
202 void Globals::Reset()
203 {
204 m_result = Skipped;
205
206 m_stopReportingIgnoredErrors = false;
207
208 m_config.traces = NitsTraceAllTests;
209 m_config.mode = NitsTestCompoundFault;
210 m_config.breakFault = false;
211 krisbash 1.1 m_config.breakAssert = false;
212 m_config.skipFlakyTests = false;
213
214 m_simAuto.Reset(CallSite_NONE, 0, false);
215 m_simManual.Reset(CallSite_NONE, 0, false);
216
217 m_faultError = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
218 m_faultEvent[0] = L'\0';
219 m_debugger[0] = L'\0';
220 m_binaryFilter[0] = '\0';
221 m_binaryTarget[0] = '\0';
222 }
223
224 } //namespace TestSystem
225
226 static void PatchBinary(
227 _In_z_ PAL_Char *binary)
228 {
229 Shlib *library = NULL;
230 Shlib *framework;
231 NitsFT *target;
232 krisbash 1.1 NitsFT *source;
233 ptrdiff_t *targetNitsPresence = NULL;
234
235 //printf("\nPatching binary %s\n", binary);
236
237 library = Shlib_Open_Injected(binary, NitsReservedCallSite());
238
239 if (library == NULL)
240 {
241 return;
242 }
243
244 target = (NitsFT *)Shlib_Sym(library, "NITS_STUB");
245 if (target == NULL)
246 {
247 Shlib_Close(library);
248 return;
249 }
250
251 targetNitsPresence = (ptrdiff_t *)Shlib_Sym(library, "NITS_PRESENCE_STUB");
252 if (targetNitsPresence == NULL)
253 krisbash 1.1 {
254 Shlib_Close(library);
255 return;
256 }
257
258 #ifdef _MSC_VER
259 framework = Shlib_Open_Injected(PAL_T("nitsdll.dll"), NitsReservedCallSite());
260 #else
261 framework = Shlib_Open_Injected(PAL_T("libnits.so"), NitsReservedCallSite());
262 #endif
263 if (framework == NULL)
264 {
265 return;
266 }
267
268 source = (NitsFT *)Shlib_Sym(framework, "NITS_IMPL");
269 if (source == NULL)
270 {
271 Shlib_Close(library);
272 return;
273 }
274 krisbash 1.1
275 #ifdef _MSC_VER
276 // changing the protection on the page to execute read write so that
277 // injector can patch the NITS stub table with the NITS API table values
278 DWORD oldProtection = 0;
279 if(VirtualProtect(target, sizeof(NitsFT), PAGE_EXECUTE_READWRITE, &oldProtection) == 0)
280 {
281 Shlib_Close(library);
282 return;
283 }
284 #endif
285
286 memcpy(target, source, sizeof(NitsFT));
287
288 Atomic_Swap(targetNitsPresence, NitsActive);
289
290 Shlib_Close(library);
291 }
292
293 static unsigned DoesBinaryMatch(
294 _In_ PAL_Char *list,
295 krisbash 1.1 _In_z_ PAL_Char *name)
296 {
297 for (;;)
298 {
299 #ifdef _MSC_VER
300 #pragma prefast(push)
301 #pragma prefast (disable: 26006)
302 #endif
303 if (*list == '\0')
304 return FALSE;
305
306 // This is to enable specifying a substring of the
307 // target string; it is useful when all your target dll have some common substring
308 if(*list == PAL_T('*'))
309 {
310 list++;
311 if(Tcsstr(name, list))
312 return TRUE;
313 }
314 else if (Tcscasecmp(list, name) == 0)
315 return TRUE;
316 krisbash 1.1
317 #ifdef _MSC_VER
318 #pragma prefast(pop)
319 #endif
320 list += Tcslen(list) + 1;
321 }
322 }
323
324 #ifdef _MSC_VER
325 typedef HMODULE LoadedModule;
326 #else
327 typedef struct _LoadedModule
328 {
329 PAL_Char *modulePath;
330 PAL_Char *moduleBaseName;
331 } LoadedModule;
332 #endif
333
334 BOOL EnumProcessModulesHelper(
335 _Out_writes_bytes_(cb) LoadedModule *lphModule,
336 _In_ DWORD cb,
337 krisbash 1.1 _Out_ LPDWORD lpcbNeeded)
338 {
339 #ifdef _MSC_VER
340 return EnumProcessModules(GetCurrentProcess(), lphModule, cb, lpcbNeeded);
341 #else
342 char buf[MAX_PATH] = "/proc/";
343 DWORD count = 0;
344 DWORD maxCount = cb / sizeof(LoadedModule);
345
346 ostringstream s;
347 string convertedString;
348 s << Process_ID();
349 convertedString = s.str();
350 Strlcpy(buf + 6, convertedString.c_str(), MAX_PATH - 6);
351 Strlcpy(buf + Strlen(buf), "/maps", 6);
352
353 ifstream file(buf);
354
355 if (!file.good())
356 {
357 Tprintf(PAL_T("ERROR: maps file could not be loaded for current process"));
358 krisbash 1.1 return FALSE;
359 }
360 while (file.good() && (count < maxCount))
361 {
362 char *startOfPath = NULL;
363 PAL_Char *nextFwdSlash = NULL;
364 PAL_Char *currentBaseName = NULL;
365 lphModule[count].modulePath = NULL;
366 lphModule[count].moduleBaseName = NULL;
367 file >> buf;
368 startOfPath = strchr(buf, '/');
369 if(startOfPath)
370 {
371 lphModule[count].modulePath = ConvertStringAToPalChar(startOfPath);
372 if(lphModule[count].modulePath == NULL)
373 {
374 Tprintf(PAL_T("ERROR: could not allocate memory while enumerating modules"));
375 return FALSE;
376 }
377
378 currentBaseName = lphModule[count].modulePath;
379 krisbash 1.1 nextFwdSlash = Tcschr(currentBaseName, PAL_T('/'));
380 while(nextFwdSlash && nextFwdSlash[1] != PAL_T('\0'))
381 {
382 currentBaseName = nextFwdSlash + 1;
383 nextFwdSlash = Tcschr(currentBaseName, PAL_T('/'));
384 }
385 lphModule[count].moduleBaseName = currentBaseName;
386
387 count++;
388 }
389 }
390
391 *lpcbNeeded = count * sizeof(LoadedModule);
392 return TRUE;
393 #endif
394 }
395
396 DWORD GetModuleBaseNameHelper(
397 _In_opt_ LoadedModule hModule,
398 _Out_writes_z_(nSize) PAL_Char *lpBaseName,
399 _In_ DWORD nSize)
400 krisbash 1.1 {
401 #ifdef _MSC_VER
402 return GetModuleBaseName(GetCurrentProcess(), hModule, lpBaseName, nSize);
403 #else
404 if(hModule.moduleBaseName == NULL)
405 return 0;
406
407 return Tcslcpy(lpBaseName, hModule.moduleBaseName, nSize);
408 #endif
409 }
410
411 void ProcessPatches(TestSystem::Globals *globals)
412 {
413 if (globals->m_runLock == 0)
414 return;
415
416 unsigned isMatch = 0;
417 /* Sweep through the list of loaded modules. */
418 LoadedModule modules[200];
419 DWORD size = 0;
420 unsigned count = 0;
421 krisbash 1.1 unsigned i;
422 PAL_Char name[MAX_PATH];
423
424 if (EnumProcessModulesHelper(modules, sizeof(modules), &size) == FALSE)
425 {
426 return;
427 }
428
429 count = size / sizeof(LoadedModule);
430
431 if (globals->m_binaryFilter[0] == '\0')
432 {
433 /* There is no filter. Match target binaries in any process. */
434 isMatch = 1;
435 }
436 else for (i = 0; i < count; i++)
437 {
438 if(GetModuleBaseNameHelper(modules[i], name, MAX_PATH) == 0)
439 continue;
440
441 if (DoesBinaryMatch(globals->m_binaryFilter, name))
442 krisbash 1.1 {
443 /* Found one of the filter modules. */
444 isMatch = 1;
445 break;
446 }
447 }
448
449 if (isMatch == 0)
450 goto Cleanup;
451
452 for (i = 0; i < count; i++)
453 {
454 if(GetModuleBaseNameHelper(modules[i], name, MAX_PATH) == 0)
455 continue;
456
457 if (DoesBinaryMatch(globals->m_binaryTarget, name))
458 {
459 /* Found a target module. Trap this! */
460 PatchBinary(name);
461 }
462 }
463 krisbash 1.1
464 Cleanup:
465 #ifndef _MSC_VER
466 if(count != 0)
467 {
468 for(i = 0; i < count; i++)
469 {
470 if(modules[i].modulePath != NULL)
471 {
472 delete [] (modules[i].modulePath);
473 }
474 }
475 }
476 #endif
477 return;
478 }
479
480 static TestSystem::Globals g_tempGlobals;
481
482 static volatile ptrdiff_t g_signalSemaphoreDeleted = 0;
483 static NamedSem g_signalSemaphore;
484 krisbash 1.1 static PAL_Boolean g_signalSemaphoreInitialized = PAL_FALSE;
485 static NamedSem g_waitSemaphore;
486 static PAL_Boolean g_waitSemaphoreInitialized = PAL_FALSE;
487 static NamedSem g_lockSemaphore;
488 static PAL_Boolean g_lockSemaphoreInitialized = PAL_FALSE;
489 static TestSystem::Globals *g_globals = NULL;
490 static TestSystem::InjectorTarget *g_injectorTarget = NULL;
491 static Shmem g_mapping;
492 volatile ptrdiff_t *g_status = NULL;
493 static volatile ptrdiff_t s_injectorStopping;
494
495 PAL_Boolean TryMarkingSignalSemaphoreForDeletion()
496 {
497 return (Atomic_CompareAndSwap(&g_signalSemaphoreDeleted, 0, 1) == 0);
498 }
499
500 void MarkSignalSemaphoreAlive()
501 {
502 Atomic_Swap(&g_signalSemaphoreDeleted, 0);
503 }
504
505 krisbash 1.1 unsigned long InjectorSetup()
506 {
507 void *start;
508 size_t bytes = sizeof(TestSystem::Globals) + sizeof(SharedSegmentHeader);
509 PAL_Char nameSignal[128] = PAL_T(CONFIG_SEMNAMELOCALPREFIX) PAL_T("NitsInjectorIn_");
510 PAL_Char nameWait[128] = PAL_T(CONFIG_SEMNAMELOCALPREFIX) PAL_T("NitsInjectorOut_");
511 PAL_Char conversionBuf[64] = PAL_T("");
512 const PAL_Char *convertedStr = NULL;
513 size_t convertedSize = 0;
514 // the lock semaphore is kept non-windows only since the issue with the injector is not observed on windows
515 PAL_Char nameLock[128] = PAL_T(CONFIG_SEMNAMELOCALPREFIX) PAL_T("NitsInjectorLock_");
516
517 const PAL_Char globalMappingName[] = PAL_T(CONFIG_SHMNAMEGLOBALPREFIX) PAL_T("NitsGlobalData");
518
519 s_injectorStopping = 0;
520
521 MarkSignalSemaphoreAlive();
522
523 if(Shmem_Open(&g_mapping, globalMappingName, SHMEM_ACCESS_READWRITE, SHMEM_USER_ACCESS_ALLOW_ALL, bytes) != NO_ERROR)
524 {
525 #ifdef _MSC_VER
526 krisbash 1.1 return GetLastError();
527 #else
528 return ERROR_OUTOFMEMORY;
529 #endif
530 }
531
532 start = Shmem_Map(&g_mapping, SHMEM_ACCESS_READWRITE, 0, bytes);
533 if (start == NULL)
534 {
535 #ifdef _MSC_VER
536 return GetLastError();
537 #else
538 return ERROR_OUTOFMEMORY;
539 #endif
540 }
541
542 /* The shared memory is mapped. Make sure the contents are initialized. */
543 g_status = (ptrdiff_t *)start;
544 g_globals = (TestSystem::Globals *)((char*)g_status + sizeof(SharedSegmentHeader));
545 if (Atomic_CompareAndSwap(g_status, TestSystem::Unloaded, TestSystem::Loading) == TestSystem::Unloaded)
546 {
547 krisbash 1.1 memcpy(g_globals, &g_tempGlobals, sizeof(TestSystem::Globals));
548 *g_status = TestSystem::Loaded;
549 }
550
551 while (*g_status != TestSystem::Loaded)
552 {
553 Sleep_Milliseconds(10);
554 }
555
556 /* The shared memory space is now initialized. */
557 /* Set up the semaphores used to trigger patching. */
558
559 if (g_globals->m_version != TestSystem::SharedMemoryVersion)
560 {
561 g_globals = NULL;
562 return 0;
563 }
564
565 TcsFromUInt64(conversionBuf, Process_ID(), &convertedStr, &convertedSize);
566 Tcscat(nameSignal, 128, convertedStr);
567 Tcscat(nameWait, 128, convertedStr);
568 krisbash 1.1
569 /* The semaphre signalled by the product to us. */
570 g_signalSemaphoreInitialized = (0 == NamedSem_Open_Injected(&g_signalSemaphore, SEM_USER_ACCESS_ALLOW_ALL, 0, nameSignal, NAMEDSEM_FLAG_CREATE, NitsReservedCallSite()));
571
572 /* The product waits on this semaphre to continue execution. */
573 g_waitSemaphoreInitialized = (0 == NamedSem_Open_Injected(&g_waitSemaphore, SEM_USER_ACCESS_ALLOW_ALL, 0, nameWait, NAMEDSEM_FLAG_CREATE, NitsReservedCallSite()));
574
575 Tcscat(nameLock, 128, convertedStr);
576
577 /* The product or unittest framework use this as a locking mechanism to access the wait and signal semaphore */
578 g_lockSemaphoreInitialized = (0 == NamedSem_Open_Injected(&g_lockSemaphore, SEM_USER_ACCESS_ALLOW_ALL, 1, nameLock, NAMEDSEM_FLAG_CREATE, NitsReservedCallSite()));
579
580 /* Register the handles with the harness. */
581 for (int i = 0; i < TestSystem::InjectorListSize; i++)
582 {
583 TestSystem::InjectorTarget *target = g_globals->m_injectors + i;
584
585 if (target->process != 0)
586 continue;
587
588 if (Atomic_CompareAndSwap(&target->process, 0, Process_ID()) != 0)
589 krisbash 1.1 continue;
590 #ifdef _MSC_VER
591 target->signalSemaphore = g_signalSemaphore.handle;
592 target->waitSemaphore = g_waitSemaphore.handle;
593 target->lockSemaphore = g_lockSemaphore.handle;
594 #endif
595 g_injectorTarget = target;
596 break;
597 }
598
599 return 0;
600 }
601
602 void CloseSemaphoreIfRequired(PAL_Boolean *initialized, NamedSem *semaphore)
603 {
604 if(*initialized)
605 {
606 NamedSem_Close(semaphore);
607 NamedSem_Destroy(semaphore);
608 *initialized = PAL_FALSE;
609 }
610 krisbash 1.1 }
611
612 void Unload()
613 {
614 if(g_injectorTarget != NULL)
615 {
616 /* Unregister with the harness. */
617 Atomic_CompareAndSwap(&(g_injectorTarget->process), Process_ID(), 0);
618 }
619
620 if(g_status)
621 {
622 g_injectorTarget = NULL;
623 Shmem_Unmap(&g_mapping, const_cast<ptrdiff_t *>(g_status), sizeof(TestSystem::Globals) + sizeof(SharedSegmentHeader));
624 Shmem_Close(&g_mapping);
625 }
626
627 CloseSemaphoreIfRequired(&g_waitSemaphoreInitialized, &g_waitSemaphore);
628 CloseSemaphoreIfRequired(&g_lockSemaphoreInitialized, &g_lockSemaphore);
629 // either we or the NitsStopInjectorFunc will close signal semaphore
630 // the function is to see who wins the race
631 krisbash 1.1 if(TryMarkingSignalSemaphoreForDeletion())
632 {
633 CloseSemaphoreIfRequired(&g_signalSemaphoreInitialized, &g_signalSemaphore);
634 }
635 //printf("\nUnloading injector\n");
636 }
637
638
639 NITS_EXTERN_C PAL_Uint32 THREAD_API InjectorProc(_In_ void * param)
640 {
641 PAL_UNUSED(param);
642
643 if (g_globals == NULL || g_injectorTarget == NULL)
644 {
645 //printf("\nUnloading injector since globals is null\n");
646 Unload();
647 return 0;
648 }
649
650 for(;;)
651 {
652 krisbash 1.1 int waitResult = NamedSem_TimedWait(&g_signalSemaphore, 10000);
653
654 if (g_globals->m_unload ||
655 g_globals->m_version != TestSystem::SharedMemoryVersion ||
656 g_injectorTarget->process != (long)Process_ID() ||
657 s_injectorStopping)
658 {
659 NamedSem_Post(&g_waitSemaphore, 1);
660 Unload();
661 //printf("\nUnloading injector since globals told it to\n");
662 return 0;
663 }
664
665 // if the wait timed out, no need to patch; just go back to wait
666 if(waitResult != 0)
667 {
668 continue;
669 }
670
671 ProcessPatches(g_globals);
672
673 krisbash 1.1 NamedSem_Post(&g_waitSemaphore, 1);
674 }
675 }
676
677 PAL_BEGIN_EXTERNC
678
679 static volatile ptrdiff_t s_injectorRefs = 0;
680 static Thread s_injectorThread;
681
682 #define INJECTOR_STOPPED 0
683 #define INJECTOR_STARTING 1
684 #define INJECTOR_STOPPING 2
685 #define INJECTOR_RUNNING 3
686
687 NITS_DLLEXPORT int NITS_CALL NitsStartInjector(void)
688 {
689 int result;
690 ptrdiff_t oldRefs;
691 ptrdiff_t newRefs;
692 ptrdiff_t swapRefs;
693
694 krisbash 1.1 for (;;)
695 {
696 oldRefs = PAL_PREFETCH(&s_injectorRefs);
697
698 if (oldRefs >= INJECTOR_RUNNING)
699 newRefs = oldRefs + 1;
700 else if (oldRefs == INJECTOR_STOPPED)
701 newRefs = INJECTOR_STARTING;
702 else
703 {
704 // this is a rare code path which occurs if two product binaries at the same time try to load the injector
705 // and in that case one of them wins the race to set the s_injectorRefs to INJECTOR_STARTING
706 // this is very quick transient state till the other thread sets the refs to INJECTOR_RUNNING which is few statements below in this function
707 // not doing this leads to this thread not leaving the cpu for anyone else and slows down the process and the machine
708 Sleep_Milliseconds(10);
709 continue;
710 }
711
712 swapRefs = Atomic_CompareAndSwap(&s_injectorRefs, oldRefs, newRefs);
713 if (swapRefs == oldRefs)
714 break;
715 krisbash 1.1 }
716
717 if (newRefs != INJECTOR_STARTING)
718 return 0;
719
720 result = InjectorSetup();
721 if (result)
722 goto Fail;
723
724 result = Thread_CreateJoinable_Injected(&s_injectorThread, InjectorProc, NULL, NULL, NitsReservedCallSite());
725 if (result)
726 goto Fail;
727
728 s_injectorRefs = INJECTOR_RUNNING;
729 return 0;
730
731 Fail:
732 s_injectorRefs = INJECTOR_STOPPED;
733 return result;
734 }
735
736 krisbash 1.1 NITS_DLLEXPORT void NITS_CALL NitsStopInjector(void)
737 {
738 ptrdiff_t oldRefs;
739 ptrdiff_t newRefs;
740 ptrdiff_t swapRefs;
741
742 for (;;)
743 {
744 oldRefs = PAL_PREFETCH(&s_injectorRefs);
745
746 if (oldRefs > INJECTOR_RUNNING)
747 newRefs = oldRefs - 1;
748 else if (oldRefs == INJECTOR_RUNNING)
749 newRefs = INJECTOR_STOPPING;
750 else if (oldRefs == INJECTOR_STOPPED)
751 abort();
752 else
753 continue;
754
755 swapRefs = Atomic_CompareAndSwap(&s_injectorRefs, oldRefs, newRefs);
756 if (swapRefs == oldRefs)
757 krisbash 1.1 break;
758 }
759
760 if (newRefs != INJECTOR_STOPPING)
761 return;
762
763 s_injectorStopping = 1;
764
765 // if injector has already called Unload and ended InjectorProc
766 // in that case we dont need to post signalsemaphore
767 // in other case we will post it since that will make the thread end quicker
768 PAL_Boolean markedForDeletion = TryMarkingSignalSemaphoreForDeletion();
769 if(markedForDeletion)
770 {
771 NamedSem_Post(&g_signalSemaphore, 1);
772 }
773
774 PAL_Uint32 result = 0;
775
776 Thread_Join(&s_injectorThread, &result);
777
778 krisbash 1.1 Thread_Destroy(&s_injectorThread);
779
780 if(markedForDeletion)
781 {
782 CloseSemaphoreIfRequired(&g_signalSemaphoreInitialized, &g_signalSemaphore);
783 }
784
785 s_injectorRefs = INJECTOR_STOPPED;
786 }
787
788 PAL_END_EXTERNC
789
|