1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <thread_internal.hxx>
24 #include <comphelper/windowserrorstring.hxx>
25 #include <osl/diagnose.h>
26 #include <osl/mutex.hxx>
27 #include <osl/thread.h>
28 #include <rtl/alloc.h>
30 #include <osl/interlck.h>
31 #include <rtl/tencinfo.h>
32 #include <sal/log.hxx>
33 #include <systools/win32/comtools.hxx>
41 Thread-data structure hidden behind oslThread:
45 HANDLE m_hThread
; /* OS-handle used for all thread-functions */
46 DWORD m_ThreadId
; /* identifier for this thread */
47 sal_Int32 m_nTerminationRequested
;
48 oslWorkerFunction m_WorkerFunction
;
55 static oslThread
oslCreateThread(oslWorkerFunction pWorker
, void* pThreadData
, sal_uInt32 nFlags
);
57 static DWORD WINAPI
oslWorkerWrapperFunction(_In_ LPVOID pData
)
59 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(pData
);
61 /* Initialize COM - Multi Threaded Apartment (MTA) for all threads */
62 sal::systools::CoInitializeGuard
aGuard(COINIT_MULTITHREADED
, false,
63 sal::systools::CoInitializeGuard::WhenFailed::NoThrow
);
65 /* call worker-function with data */
67 pThreadImpl
->m_WorkerFunction(pThreadImpl
->m_pData
);
72 static oslThread
oslCreateThread(oslWorkerFunction pWorker
,
76 osl_TThreadImpl
* pThreadImpl
;
78 /* alloc mem. for our internal data structure */
79 pThreadImpl
= static_cast<osl_TThreadImpl
*>(malloc(sizeof(osl_TThreadImpl
)));
81 OSL_ASSERT(pThreadImpl
);
83 if ( pThreadImpl
== nullptr )
88 pThreadImpl
->m_WorkerFunction
= pWorker
;
89 pThreadImpl
->m_pData
= pThreadData
;
90 pThreadImpl
->m_nTerminationRequested
= 0;
92 pThreadImpl
->m_hThread
= CreateThread(
93 nullptr, /* no security */
94 0, /* default stack-size */
95 oslWorkerWrapperFunction
, /* worker-function */
96 pThreadImpl
, /* provide worker-function with data */
97 nFlags
, /* start thread immediately or suspended */
98 &pThreadImpl
->m_ThreadId
);
100 if(pThreadImpl
->m_hThread
== nullptr)
102 SAL_WARN("sal.osl", "CreateThread failed:" << WindowsErrorString(GetLastError()));
112 oslThread SAL_CALL
osl_createThread(oslWorkerFunction pWorker
,
115 return oslCreateThread(pWorker
, pThreadData
, 0);
118 oslThread SAL_CALL
osl_createSuspendedThread(oslWorkerFunction pWorker
,
121 return oslCreateThread(pWorker
, pThreadData
, CREATE_SUSPENDED
);
124 oslThreadIdentifier SAL_CALL
osl_getThreadIdentifier(oslThread Thread
)
126 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
128 if (pThreadImpl
!= nullptr)
129 return static_cast<oslThreadIdentifier
>(pThreadImpl
->m_ThreadId
);
131 return static_cast<oslThreadIdentifier
>(GetCurrentThreadId());
134 void SAL_CALL
osl_destroyThread(oslThread Thread
)
136 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
138 if (Thread
== nullptr) /* valid ptr? */
140 /* thread already destroyed or not created */
144 /* !!!! _exitthreadex does _not_ call CloseHandle !!! */
145 CloseHandle( pThreadImpl
->m_hThread
);
151 void SAL_CALL
osl_resumeThread(oslThread Thread
)
153 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
155 OSL_ASSERT(pThreadImpl
); /* valid ptr? */
157 ResumeThread(pThreadImpl
->m_hThread
);
160 void SAL_CALL
osl_suspendThread(oslThread Thread
)
162 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
164 OSL_ASSERT(pThreadImpl
); /* valid ptr? */
166 SuspendThread(pThreadImpl
->m_hThread
);
169 void SAL_CALL
osl_setThreadPriority(oslThread Thread
,
170 oslThreadPriority Priority
)
173 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
175 OSL_ASSERT(pThreadImpl
); /* valid ptr? */
177 /* map enum to WIN32 levels
178 it would be faster and more elegant to preset
179 the enums, but that would require an #ifdef in
180 the exported header, which is not desired.
184 case osl_Thread_PriorityHighest
:
185 winPriority
= THREAD_PRIORITY_HIGHEST
;
188 case osl_Thread_PriorityAboveNormal
:
189 winPriority
= THREAD_PRIORITY_ABOVE_NORMAL
;
192 case osl_Thread_PriorityNormal
:
193 winPriority
= THREAD_PRIORITY_NORMAL
;
196 case osl_Thread_PriorityBelowNormal
:
197 winPriority
= THREAD_PRIORITY_BELOW_NORMAL
;
200 case osl_Thread_PriorityLowest
:
201 winPriority
= THREAD_PRIORITY_LOWEST
;
204 case osl_Thread_PriorityUnknown
:
205 OSL_ASSERT(FALSE
); /* only fools try this...*/
207 /* let release-version behave friendly */
211 OSL_ASSERT(FALSE
); /* enum expanded, but forgotten here...*/
213 /* let release-version behave friendly */
217 SetThreadPriority(pThreadImpl
->m_hThread
, winPriority
);
220 oslThreadPriority SAL_CALL
osl_getThreadPriority(const oslThread Thread
)
223 oslThreadPriority Priority
;
225 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
227 /* invalid arguments ?*/
228 if(pThreadImpl
==nullptr || pThreadImpl
->m_hThread
==nullptr)
230 return osl_Thread_PriorityUnknown
;
234 GetThreadPriority(pThreadImpl
->m_hThread
);
236 if(winPriority
== THREAD_PRIORITY_ERROR_RETURN
)
238 return osl_Thread_PriorityUnknown
;
241 /* map WIN32 priority to enum */
244 case THREAD_PRIORITY_TIME_CRITICAL
:
245 case THREAD_PRIORITY_HIGHEST
:
246 Priority
= osl_Thread_PriorityHighest
;
249 case THREAD_PRIORITY_ABOVE_NORMAL
:
250 Priority
= osl_Thread_PriorityAboveNormal
;
253 case THREAD_PRIORITY_NORMAL
:
254 Priority
= osl_Thread_PriorityNormal
;
257 case THREAD_PRIORITY_BELOW_NORMAL
:
258 Priority
= osl_Thread_PriorityBelowNormal
;
261 case THREAD_PRIORITY_IDLE
:
262 case THREAD_PRIORITY_LOWEST
:
263 Priority
= osl_Thread_PriorityLowest
;
267 OSL_ASSERT(FALSE
); /* WIN32 API changed, incorporate new prio-level! */
269 /* release-version behaves friendly */
270 Priority
= osl_Thread_PriorityUnknown
;
276 sal_Bool SAL_CALL
osl_isThreadRunning(const oslThread Thread
)
278 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
280 /* invalid arguments ?*/
281 if(pThreadImpl
==nullptr || pThreadImpl
->m_hThread
==nullptr)
286 return WaitForSingleObject(pThreadImpl
->m_hThread
, 0) != WAIT_OBJECT_0
;
289 void SAL_CALL
osl_joinWithThread(oslThread Thread
)
291 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
293 /* invalid arguments?*/
294 if(pThreadImpl
==nullptr || pThreadImpl
->m_hThread
==nullptr)
296 /* assume thread is not running */
300 WaitForSingleObject(pThreadImpl
->m_hThread
, INFINITE
);
303 void SAL_CALL
osl_waitThread(const TimeValue
* pDelay
)
307 DWORD millisecs
= pDelay
->Seconds
* 1000L + pDelay
->Nanosec
/ 1000000L;
313 void SAL_CALL
osl_terminateThread(oslThread Thread
)
315 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
317 /* invalid arguments?*/
318 if (pThreadImpl
==nullptr || pThreadImpl
->m_hThread
==nullptr)
320 /* assume thread is not running */
324 osl_atomic_increment(&(pThreadImpl
->m_nTerminationRequested
));
327 sal_Bool SAL_CALL
osl_scheduleThread(oslThread Thread
)
329 osl_TThreadImpl
* pThreadImpl
= static_cast<osl_TThreadImpl
*>(Thread
);
333 /* invalid arguments?*/
334 if (pThreadImpl
==nullptr || pThreadImpl
->m_hThread
==nullptr)
336 /* assume thread is not running */
340 return 0 == pThreadImpl
->m_nTerminationRequested
;
343 void SAL_CALL
osl_yieldThread(void)
348 static void impSetThreadDescription(char const * name
) {
349 // SetThreadDescription is only available since Windows 10 version 1607
350 typedef HRESULT(WINAPI
* TSetThreadDescription
)(HANDLE
, PCWSTR
);
351 static const auto pSetThreadDescription
= reinterpret_cast<TSetThreadDescription
>(
352 GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
353 if (pSetThreadDescription
)
355 if (const int nReqCCh
= MultiByteToWideChar(CP_ACP
, 0, name
, -1, nullptr, 0))
357 if (PWSTR wStr
= static_cast<PWSTR
>(malloc(nReqCCh
* sizeof(WCHAR
))))
359 if (MultiByteToWideChar(CP_ACP
, 0, name
, -1, wStr
, nReqCCh
))
360 pSetThreadDescription(GetCurrentThread(), wStr
);
367 void SAL_CALL
osl_setThreadName(char const * name
) {
368 /* See < https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code >: */
369 #pragma pack(push, 8)
371 DWORD dwType
= 0x1000;
373 DWORD dwThreadID
= DWORD(-1);
380 0x406D1388, 0, sizeof info
/ sizeof (ULONG_PTR
),
381 reinterpret_cast<ULONG_PTR
*>(&info
));
382 } __except (EXCEPTION_EXECUTE_HANDLER
) {}
384 impSetThreadDescription(name
);
392 oslThreadKeyCallbackFunction pfnCallback
;
393 struct TLS_
*pNext
, *pPrev
;
396 PTLS g_pThreadKeyList
= nullptr;
397 std::mutex
& getThreadKeyListMutex()
399 static std::mutex g_ThreadKeyListMutex
;
400 return g_ThreadKeyListMutex
;
405 static void AddKeyToList( PTLS pTls
)
409 std::lock_guard
aGuard(getThreadKeyListMutex());
411 pTls
->pNext
= g_pThreadKeyList
;
412 pTls
->pPrev
= nullptr;
414 if ( g_pThreadKeyList
)
415 g_pThreadKeyList
->pPrev
= pTls
;
417 g_pThreadKeyList
= pTls
;
421 static void RemoveKeyFromList( PTLS pTls
)
425 std::lock_guard
aGuard(getThreadKeyListMutex());
427 pTls
->pPrev
->pNext
= pTls
->pNext
;
430 OSL_ASSERT( pTls
== g_pThreadKeyList
);
431 g_pThreadKeyList
= pTls
->pNext
;
435 pTls
->pNext
->pPrev
= pTls
->pPrev
;
439 void osl_callThreadKeyCallbackOnThreadDetach(void)
443 std::lock_guard
aGuard(getThreadKeyListMutex());
444 pTls
= g_pThreadKeyList
;
447 if ( pTls
->pfnCallback
)
449 void *pValue
= TlsGetValue( pTls
->dwIndex
);
452 pTls
->pfnCallback( pValue
);
459 oslThreadKey SAL_CALL
osl_createThreadKey(oslThreadKeyCallbackFunction pCallback
)
461 PTLS pTls
= static_cast<PTLS
>(malloc( sizeof(TLS
) ));
465 pTls
->pfnCallback
= pCallback
;
466 if ( DWORD(-1) == (pTls
->dwIndex
= TlsAlloc()) )
472 AddKeyToList( pTls
);
478 void SAL_CALL
osl_destroyThreadKey(oslThreadKey Key
)
482 PTLS pTls
= static_cast<PTLS
>(Key
);
484 RemoveKeyFromList( pTls
);
485 TlsFree( pTls
->dwIndex
);
490 void* SAL_CALL
osl_getThreadKeyData(oslThreadKey Key
)
494 PTLS pTls
= static_cast<PTLS
>(Key
);
496 return TlsGetValue( pTls
->dwIndex
);
502 sal_Bool SAL_CALL
osl_setThreadKeyData(oslThreadKey Key
, void *pData
)
506 PTLS pTls
= static_cast<PTLS
>(Key
);
507 void* pOldData
= nullptr;
510 if ( pTls
->pfnCallback
)
511 pOldData
= TlsGetValue( pTls
->dwIndex
);
513 fSuccess
= TlsSetValue( pTls
->dwIndex
, pData
);
515 if ( fSuccess
&& pTls
->pfnCallback
&& pOldData
)
516 pTls
->pfnCallback( pOldData
);
524 rtl_TextEncoding
getThreadTextEncodingForInitialization()
526 return rtl_getTextEncodingFromWindowsCodePage(GetACP());
529 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */