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#include "pal/dbgmsg.h"
14#include "pal/thread.hpp"
15#include "../thread/procprivate.hpp"
16#include "pal/module.h"
17#include "pal/process.h"
18#include "pal/seh.hpp"
19#include "pal/signal.hpp"
20
21using namespace CorUnix;
22
23#ifdef FEATURE_PAL_SXS
24
25SET_DEFAULT_DEBUG_CHANNEL(SXS);
26
27PAL_ERROR AllocatePalThread(CPalThread **ppThread);
28
29/************************* Enter *************************/
30
31/*++
32Function:
33 PAL_Enter
34
35Abstract:
36 This function needs to be called on a thread when it enters
37 a region of code that depends on this instance of the PAL
38 in the process, and the current thread may or may not be
39 known to the PAL. This function can fail (for something else
40 than an internal error) if this is the first time that the
41 current thread entered this PAL. Note that PAL_Initialize
42 implies a call to this function.
43
44 NOTE: This function must not modify LastError.
45--*/
46PAL_ERROR
47PALAPI
48PAL_Enter(PAL_Boundary boundary)
49{
50 ENTRY_EXTERNAL("PAL_Enter(boundary=%u)\n", boundary);
51
52 PAL_ERROR palError = ERROR_SUCCESS;
53 CPalThread *pThread = GetCurrentPalThread();
54 if (pThread != NULL)
55 {
56 palError = pThread->Enter(boundary);
57 }
58 else
59 {
60 // If this assert fires, we'll have to pipe this information so that
61 // CPalThread's RunPostCreateInitializers call to SEHEnable
62 // can know what direction.
63 _ASSERT_MSG(PAL_BoundaryTop == boundary, "How are we entering a PAL "
64 "thread for the first time not from the top? (boundary=%u)", boundary);
65
66 palError = AllocatePalThread(&pThread);
67 if (NO_ERROR != palError)
68 {
69 ERROR("Unable to allocate pal thread: error %d\n", palError);
70 }
71 }
72
73 LOGEXIT("PAL_Enter returns %d\n", palError);
74 return palError;
75}
76
77/*++
78Function:
79 CreateCurrentThreadData
80
81Abstract:
82 This function is called by the InternalGetOrCreateCurrentThread inlined
83 function to create the thread data when it is null meaning the thread has
84 never been in this PAL.
85
86Warning:
87 If the allocation fails, this function asserts and exits the process.
88--*/
89extern "C" CPalThread *
90CreateCurrentThreadData()
91{
92 CPalThread *pThread = NULL;
93
94 if (PALIsThreadDataInitialized()) {
95 PAL_ERROR palError = AllocatePalThread(&pThread);
96 if (NO_ERROR != palError)
97 {
98 ASSERT("Unable to allocate pal thread: error %d - aborting\n", palError);
99 PROCAbort();
100 }
101 }
102
103 return pThread;
104}
105
106PAL_ERROR
107AllocatePalThread(CPalThread **ppThread)
108{
109 CPalThread *pThread = NULL;
110 PAL_ERROR palError;
111
112 palError = CreateThreadData(&pThread);
113 if (NO_ERROR != palError)
114 {
115 goto exit;
116 }
117
118#if !HAVE_MACH_EXCEPTIONS
119 // Ensure alternate stack for SIGSEGV handling. Our SIGSEGV handler is set to
120 // run on an alternate stack and the stack needs to be allocated per thread.
121 if (!pThread->EnsureSignalAlternateStack())
122 {
123 ERROR("Cannot allocate alternate stack for SIGSEGV handler!\n");
124 palError = ERROR_NOT_ENOUGH_MEMORY;
125 goto exit;
126 }
127#endif // !HAVE_MACH_EXCEPTIONS
128
129 HANDLE hThread;
130 palError = CreateThreadObject(pThread, pThread, &hThread);
131 if (NO_ERROR != palError)
132 {
133 pthread_setspecific(thObjKey, NULL);
134 pThread->ReleaseThreadReference();
135 goto exit;
136 }
137
138 // Like CreateInitialProcessAndThreadObjects, we do not need this
139 // thread handle, since we're not returning it to anyone who will
140 // possibly release it.
141 (void)g_pObjectManager->RevokeHandle(pThread, hThread);
142
143 PROCAddThread(pThread, pThread);
144
145exit:
146 *ppThread = pThread;
147 return palError;
148}
149
150PALIMPORT
151DWORD
152PALAPI
153PAL_EnterTop()
154{
155 return PAL_Enter(PAL_BoundaryTop);
156}
157
158
159/*++
160Function:
161 PAL_Reenter
162
163Abstract:
164 This function needs to be called on a thread when it enters
165 a region of code that depends on this instance of the PAL
166 in the process, and the current thread is already known to
167 the PAL.
168
169 NOTE: This function must not modify LastError.
170--*/
171VOID
172PALAPI
173PAL_Reenter(PAL_Boundary boundary)
174{
175 ENTRY_EXTERNAL("PAL_Reenter(boundary=%u)\n", boundary);
176
177 CPalThread *pThread = GetCurrentPalThread();
178 if (pThread == NULL)
179 {
180 ASSERT("PAL_Reenter called on a thread unknown to this PAL\n");
181 }
182
183 // We ignore the return code. This call should only fail on internal
184 // error, and we assert at the actual failure.
185 pThread->Enter(boundary);
186
187 LOGEXIT("PAL_Reenter returns\n");
188}
189
190/*++
191Function:
192 PAL_HasEntered
193
194Abstract:
195 This function can be called to determine if the thread has entered the
196 PAL through PAL_Enter or related calls.
197--*/
198BOOL
199PALAPI
200PAL_HasEntered()
201{
202 ENTRY_EXTERNAL("PAL_HasEntered()\n");
203
204 CPalThread *pThread = GetCurrentPalThread();
205 if (pThread == NULL)
206 {
207 ASSERT("PAL_Reenter called on a thread unknown to this PAL\n");
208 }
209
210 LOGEXIT("PAL_HasEntered returned\n");
211
212 return pThread->IsInPal();
213}
214
215/*++
216Function:
217 PAL_ReenterForEH
218
219Abstract:
220 This function needs to be called on a thread when it enters
221 a region of code that depends on this instance of the PAL
222 in the process, and it is unknown whether the current thread
223 is already running in the PAL. Returns TRUE if and only if
224 the thread was not running in the PAL previously.
225
226 NOTE: This function must not modify LastError.
227--*/
228BOOL
229PALAPI
230PAL_ReenterForEH()
231{
232 // Only trace if we actually reenter (otherwise, too verbose)
233 // ENTRY_EXTERNAL("PAL_ReenterForEH()\n");
234 // Thus we have to split up what ENTRY_EXTERNAL does.
235 CHECK_STACK_ALIGN;
236
237 BOOL fEntered = FALSE;
238
239 CPalThread *pThread = GetCurrentPalThread();
240 if (pThread == NULL)
241 {
242 ASSERT("PAL_ReenterForEH called on a thread unknown to this PAL\n");
243 }
244 else if (!pThread->IsInPal())
245 {
246#if _ENABLE_DEBUG_MESSAGES_
247 DBG_PRINTF(DLI_ENTRY, defdbgchan, TRUE)("PAL_ReenterForEH()\n");
248#endif
249
250 // We ignore the return code. This call should only fail on internal
251 // error, and we assert at the actual failure.
252 pThread->Enter(PAL_BoundaryEH);
253 fEntered = TRUE;
254 LOGEXIT("PAL_ReenterForEH returns TRUE\n");
255 }
256 else
257 {
258 // LOGEXIT("PAL_ReenterForEH returns FALSE\n");
259 }
260
261 return fEntered;
262}
263
264PAL_ERROR CPalThread::Enter(PAL_Boundary /* boundary */)
265{
266 if (m_fInPal)
267 {
268 WARN("Enter called on a thread that already runs in this PAL\n");
269 return NO_ERROR;
270 }
271 m_fInPal = TRUE;
272
273 return ERROR_SUCCESS;
274}
275
276
277/************************* Leave *************************/
278
279/*++
280Function:
281 PAL_Leave
282
283Abstract:
284 This function needs to be called on a thread when it leaves a region
285 of code that depends on this instance of the PAL in the process.
286
287 NOTE: This function must not modify LastError.
288--*/
289VOID
290PALAPI
291PAL_Leave(PAL_Boundary boundary)
292{
293 ENTRY("PAL_Leave(boundary=%u)\n", boundary);
294
295 CPalThread *pThread = GetCurrentPalThread();
296 // We ignore the return code. This call should only fail on internal
297 // error, and we assert at the actual failure.
298 pThread->Leave(boundary);
299
300 LOGEXIT("PAL_Leave returns\n");
301}
302
303PALIMPORT
304VOID
305PALAPI
306PAL_LeaveBottom()
307{
308 PAL_Leave(PAL_BoundaryBottom);
309}
310
311PALIMPORT
312VOID
313PALAPI
314PAL_LeaveTop()
315{
316 PAL_Leave(PAL_BoundaryTop);
317}
318
319
320PAL_ERROR CPalThread::Leave(PAL_Boundary /* boundary */)
321{
322 if (!m_fInPal)
323 {
324 WARN("Leave called on a thread that is not running in this PAL\n");
325 return ERROR_NOT_SUPPORTED;
326 }
327
328 PAL_ERROR palError = ERROR_SUCCESS;
329
330 m_fInPal = FALSE;
331
332 return palError;
333}
334
335#endif // FEATURE_PAL_SXS
336