1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | // ==++== |
6 | // |
7 | |
8 | // |
9 | // |
10 | |
11 | // |
12 | // ==--== |
13 | |
14 | #include "stdafx.h" |
15 | |
16 | #include "mscoree.h" |
17 | #include "clrinternal.h" |
18 | #include "hostimpl.h" |
19 | #include "predeftlsslot.h" |
20 | #include "unsafe.h" |
21 | |
22 | // to avoid to include clrhost.h in this file |
23 | #ifdef FAILPOINTS_ENABLED |
24 | extern int RFS_HashStack(); |
25 | #endif |
26 | |
27 | static DWORD TlsIndex = TLS_OUT_OF_INDEXES; |
28 | static PTLS_CALLBACK_FUNCTION Callbacks[MAX_PREDEFINED_TLS_SLOT]; |
29 | |
30 | #ifdef SELF_NO_HOST |
31 | HANDLE (*g_fnGetExecutableHeapHandle)(); |
32 | #endif |
33 | |
34 | extern LPVOID* (*__ClrFlsGetBlock)(); |
35 | |
36 | // |
37 | // FLS getter to avoid unnecessary indirection via execution engine. |
38 | // |
39 | LPVOID* ClrFlsGetBlockDirect() |
40 | { |
41 | return (LPVOID*)UnsafeTlsGetValue(TlsIndex); |
42 | } |
43 | |
44 | // |
45 | // utility functions for tls functionality |
46 | // |
47 | static void **CheckThreadState(DWORD slot, BOOL force = TRUE) |
48 | { |
49 | // Treat as a runtime assertion, since the invariant spans many DLLs. |
50 | _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); |
51 | |
52 | // Ensure we have a TLS Index |
53 | if (TlsIndex == TLS_OUT_OF_INDEXES) |
54 | { |
55 | DWORD tmp = UnsafeTlsAlloc(); |
56 | |
57 | if (InterlockedCompareExchange((LONG*)&TlsIndex, tmp, TLS_OUT_OF_INDEXES) != (LONG) TLS_OUT_OF_INDEXES) |
58 | { |
59 | // We lost the race with another thread. |
60 | UnsafeTlsFree(tmp); |
61 | } |
62 | |
63 | // Switch to faster TLS getter now that the TLS slot is initialized |
64 | __ClrFlsGetBlock = ClrFlsGetBlockDirect; |
65 | } |
66 | |
67 | _ASSERTE(TlsIndex != TLS_OUT_OF_INDEXES); |
68 | |
69 | void **pTlsData = (void **)TlsGetValue(TlsIndex); |
70 | |
71 | if (pTlsData == 0 && force) { |
72 | |
73 | // !!! Contract uses our TLS support. Contract may be used before our host support is set up. |
74 | // !!! To better support contract, we call into OS for memory allocation. |
75 | pTlsData = (void**) ::HeapAlloc(GetProcessHeap(),0,MAX_PREDEFINED_TLS_SLOT*sizeof(void*)); |
76 | |
77 | |
78 | if (pTlsData == NULL) |
79 | { |
80 | // workaround! We don't want exceptions being thrown during ClrInitDebugState. Just return NULL out of TlsSetValue. |
81 | // ClrInitDebugState will do a confirming FlsGet to see if the value stuck. |
82 | |
83 | // If this is for the stack probe, and we failed to allocate memory for it, we won't |
84 | // put in a guard page. |
85 | if (slot == TlsIdx_ClrDebugState || slot == TlsIdx_StackProbe) |
86 | { |
87 | return NULL; |
88 | } |
89 | RaiseException(STATUS_NO_MEMORY, 0, 0, NULL); |
90 | } |
91 | for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) |
92 | pTlsData[i] = 0; |
93 | UnsafeTlsSetValue(TlsIndex, pTlsData); |
94 | } |
95 | |
96 | return pTlsData; |
97 | } // CheckThreadState |
98 | |
99 | // This function should only be called during process detatch for |
100 | // mscoree.dll. |
101 | VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex() |
102 | { |
103 | if (TlsIndex != TLS_OUT_OF_INDEXES) |
104 | if (UnsafeTlsFree(TlsIndex)) |
105 | TlsIndex = TLS_OUT_OF_INDEXES; |
106 | } // TLS_FreeMasterSlotIndex |
107 | |
108 | |
109 | |
110 | HRESULT STDMETHODCALLTYPE UtilExecutionEngine::QueryInterface(REFIID id, void **pInterface) |
111 | { |
112 | if (!pInterface) |
113 | return E_POINTER; |
114 | |
115 | *pInterface = NULL; |
116 | |
117 | if (id == IID_IExecutionEngine) |
118 | *pInterface = (IExecutionEngine *)this; |
119 | else if (id == IID_IEEMemoryManager) |
120 | *pInterface = (IEEMemoryManager *)this; |
121 | else if (id == IID_IUnknown) |
122 | *pInterface = (IUnknown *)(IExecutionEngine *)this; |
123 | else |
124 | return E_NOINTERFACE; |
125 | |
126 | AddRef(); |
127 | return S_OK; |
128 | } // UtilExecutionEngine::QueryInterface |
129 | |
130 | // |
131 | // lifetime of this object is that of the app it lives in so no point in AddRef/Release |
132 | // |
133 | ULONG STDMETHODCALLTYPE UtilExecutionEngine::AddRef() |
134 | { |
135 | return 1; |
136 | } |
137 | |
138 | ULONG STDMETHODCALLTYPE UtilExecutionEngine::Release() |
139 | { |
140 | return 1; |
141 | } |
142 | |
143 | VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) |
144 | { |
145 | CheckThreadState(slot); |
146 | |
147 | // They can toggle between a callback and no callback. But anything else looks like |
148 | // confusion on their part. |
149 | // |
150 | // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But |
151 | // all the callbacks are equally good.) |
152 | _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0); |
153 | Callbacks[slot] = callback; |
154 | } |
155 | |
156 | LPVOID* STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetDataBlock() |
157 | { |
158 | if (TlsIndex == TLS_OUT_OF_INDEXES) |
159 | return NULL; |
160 | |
161 | return (LPVOID *)UnsafeTlsGetValue(TlsIndex); |
162 | } |
163 | |
164 | LPVOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetValue(DWORD slot) |
165 | { |
166 | void **pTlsData = CheckThreadState(slot, FALSE); |
167 | if (pTlsData) |
168 | return pTlsData[slot]; |
169 | else |
170 | return NULL; |
171 | } |
172 | |
173 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue) |
174 | { |
175 | void **pTlsData = CheckThreadState(slot, FALSE); |
176 | if (pTlsData) |
177 | { |
178 | *pValue = pTlsData[slot]; |
179 | return TRUE; |
180 | } |
181 | return FALSE; |
182 | } |
183 | |
184 | VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData) |
185 | { |
186 | void **pTlsData = CheckThreadState(slot); |
187 | if (pTlsData) // Yes, CheckThreadState(slot, TRUE) can return NULL now. |
188 | { |
189 | pTlsData[slot] = pData; |
190 | } |
191 | } |
192 | |
193 | VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_ThreadDetaching() |
194 | { |
195 | void **pTlsData = CheckThreadState(0, FALSE); |
196 | if (pTlsData) |
197 | { |
198 | for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) |
199 | { |
200 | // If we have some data and a callback, issue it. |
201 | if (Callbacks[i] != 0 && pTlsData[i] != 0) |
202 | (*Callbacks[i])(pTlsData[i]); |
203 | } |
204 | ::HeapFree (GetProcessHeap(),0,pTlsData); |
205 | |
206 | } |
207 | } |
208 | |
209 | CRITSEC_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags) |
210 | { |
211 | CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); |
212 | UnsafeInitializeCriticalSection(cs); |
213 | return (CRITSEC_COOKIE)cs; |
214 | } |
215 | |
216 | void STDMETHODCALLTYPE UtilExecutionEngine::DestroyLock(CRITSEC_COOKIE lock) |
217 | { |
218 | _ASSERTE(lock); |
219 | UnsafeDeleteCriticalSection((CRITICAL_SECTION*)lock); |
220 | free(lock); |
221 | } |
222 | |
223 | void STDMETHODCALLTYPE UtilExecutionEngine::AcquireLock(CRITSEC_COOKIE lock) |
224 | { |
225 | _ASSERTE(lock); |
226 | UnsafeEnterCriticalSection((CRITICAL_SECTION*)lock); |
227 | } |
228 | |
229 | void STDMETHODCALLTYPE UtilExecutionEngine::ReleaseLock(CRITSEC_COOKIE lock) |
230 | { |
231 | _ASSERTE(lock); |
232 | UnsafeLeaveCriticalSection((CRITICAL_SECTION*)lock); |
233 | } |
234 | |
235 | EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateAutoEvent(BOOL bInitialState) |
236 | { |
237 | HANDLE handle = UnsafeCreateEvent(NULL, FALSE, bInitialState, NULL); |
238 | _ASSERTE(handle); |
239 | return (EVENT_COOKIE)handle; |
240 | } |
241 | |
242 | EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateManualEvent(BOOL bInitialState) |
243 | { |
244 | HANDLE handle = UnsafeCreateEvent(NULL, TRUE, bInitialState, NULL); |
245 | _ASSERTE(handle); |
246 | return (EVENT_COOKIE)handle; |
247 | } |
248 | |
249 | void STDMETHODCALLTYPE UtilExecutionEngine::CloseEvent(EVENT_COOKIE event) |
250 | { |
251 | _ASSERTE(event); |
252 | CloseHandle((HANDLE)event); |
253 | } |
254 | |
255 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrSetEvent(EVENT_COOKIE event) |
256 | { |
257 | _ASSERTE(event); |
258 | return UnsafeSetEvent((HANDLE)event); |
259 | } |
260 | |
261 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrResetEvent(EVENT_COOKIE event) |
262 | { |
263 | _ASSERTE(event); |
264 | return UnsafeResetEvent((HANDLE)event); |
265 | } |
266 | |
267 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable) |
268 | { |
269 | _ASSERTE(event); |
270 | return WaitForSingleObjectEx((HANDLE)event, dwMilliseconds, bAlertable); |
271 | } |
272 | |
273 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds) |
274 | { |
275 | _ASSERTE(handle); |
276 | return WaitForSingleObjectEx(handle, dwMilliseconds, FALSE); |
277 | } |
278 | |
279 | SEMAPHORE_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax) |
280 | { |
281 | HANDLE handle = UnsafeCreateSemaphore(NULL, (LONG)dwInitial, (LONG)dwMax, NULL); |
282 | _ASSERTE(handle); |
283 | return (SEMAPHORE_COOKIE)handle; |
284 | } |
285 | |
286 | void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) |
287 | { |
288 | _ASSERTE(semaphore); |
289 | CloseHandle((HANDLE)semaphore); |
290 | } |
291 | |
292 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable) |
293 | { |
294 | _ASSERTE(semaphore); |
295 | return WaitForSingleObjectEx((HANDLE)semaphore, dwMilliseconds, bAlertable); |
296 | } |
297 | |
298 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount) |
299 | { |
300 | _ASSERTE(semaphore); |
301 | return UnsafeReleaseSemaphore((HANDLE)semaphore, lReleaseCount, lpPreviousCount); |
302 | } |
303 | |
304 | MUTEX_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, |
305 | BOOL bInitialOwner, |
306 | LPCTSTR lpName) |
307 | { |
308 | return (MUTEX_COOKIE)WszCreateMutex(lpMutexAttributes,bInitialOwner,lpName); |
309 | } |
310 | |
311 | void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseMutex(MUTEX_COOKIE mutex) |
312 | { |
313 | _ASSERTE(mutex); |
314 | CloseHandle((HANDLE)mutex); |
315 | } |
316 | |
317 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseMutex(MUTEX_COOKIE mutex) |
318 | { |
319 | _ASSERTE(mutex); |
320 | return ReleaseMutex((HANDLE)mutex); |
321 | } |
322 | |
323 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForMutex(MUTEX_COOKIE mutex, |
324 | DWORD dwMilliseconds, |
325 | BOOL bAlertable) |
326 | { |
327 | _ASSERTE(mutex); |
328 | return WaitForSingleObjectEx ((HANDLE)mutex, dwMilliseconds, bAlertable); |
329 | } |
330 | |
331 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) |
332 | { |
333 | return SleepEx (dwMilliseconds, bAlertable); |
334 | } |
335 | |
336 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrAllocationDisallowed() |
337 | { |
338 | return FALSE; |
339 | } |
340 | |
341 | LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) |
342 | { |
343 | #ifdef FAILPOINTS_ENABLED |
344 | if (RFS_HashStack ()) |
345 | return NULL; |
346 | #endif |
347 | return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); |
348 | } |
349 | |
350 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) |
351 | { |
352 | return VirtualFree(lpAddress, dwSize, dwFreeType); |
353 | } |
354 | |
355 | SIZE_T STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) |
356 | { |
357 | return VirtualQuery(lpAddress, lpBuffer, dwLength); |
358 | } |
359 | |
360 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) |
361 | { |
362 | return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); |
363 | } |
364 | |
365 | HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessHeap() |
366 | { |
367 | return GetProcessHeap(); |
368 | } |
369 | |
370 | HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessExecutableHeap() |
371 | { |
372 | #ifndef CROSSGEN_COMPILE |
373 | _ASSERTE(g_fnGetExecutableHeapHandle); |
374 | return (g_fnGetExecutableHeapHandle != NULL) ? g_fnGetExecutableHeapHandle() : NULL; |
375 | #else |
376 | return GetProcessHeap(); |
377 | #endif |
378 | } |
379 | |
380 | HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) |
381 | { |
382 | #ifdef FEATURE_PAL |
383 | return NULL; |
384 | #else |
385 | return HeapCreate(flOptions, dwInitialSize, dwMaximumSize); |
386 | #endif |
387 | } |
388 | |
389 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapDestroy(HANDLE hHeap) |
390 | { |
391 | #ifdef FEATURE_PAL |
392 | return FALSE; |
393 | #else |
394 | return HeapDestroy(hHeap); |
395 | #endif |
396 | } |
397 | |
398 | LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) |
399 | { |
400 | #ifdef FAILPOINTS_ENABLED |
401 | if (RFS_HashStack ()) |
402 | return NULL; |
403 | #endif |
404 | return HeapAlloc(hHeap, dwFlags, dwBytes); |
405 | } |
406 | |
407 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) |
408 | { |
409 | return HeapFree(hHeap, dwFlags, lpMem); |
410 | } |
411 | |
412 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) |
413 | { |
414 | #ifdef FEATURE_PAL |
415 | return FALSE; |
416 | #else |
417 | return HeapValidate(hHeap, dwFlags, lpMem); |
418 | #endif |
419 | } |
420 | |
421 | |
422 | //------------------------------------------------------------------------------ |
423 | // Helper function to get an exception from outside the exception. In |
424 | // the CLR, it may be from the Thread object. Non-CLR users have no thread object, |
425 | // and it will do nothing. |
426 | |
427 | void UtilExecutionEngine::GetLastThrownObjectExceptionFromThread(void **ppvException) |
428 | { |
429 | // Declare class so we can declare Exception** |
430 | class Exception; |
431 | |
432 | // Cast to our real type. |
433 | Exception **ppException = reinterpret_cast<Exception**>(ppvException); |
434 | |
435 | *ppException = NULL; |
436 | } // UtilExecutionEngine::GetLastThrownObjectExceptionFromThread |
437 | |
438 | |