Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sal / osl / w32 / thread.cxx
blobf6b3109adfb9b30d66ab2c331f34e494db235eb5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include "system.h"
21 #include "thread.hxx"
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>
29 #include <osl/time.h>
30 #include <osl/interlck.h>
31 #include <rtl/tencinfo.h>
32 #include <sal/log.hxx>
33 #include <systools/win32/comtools.hxx>
35 #include <errno.h>
36 #include <mutex>
38 namespace {
40 /**
41 Thread-data structure hidden behind oslThread:
43 typedef struct
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;
49 void* m_pData;
51 } osl_TThreadImpl;
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);
69 return 0;
72 static oslThread oslCreateThread(oslWorkerFunction pWorker,
73 void* pThreadData,
74 sal_uInt32 nFlags)
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 )
85 return 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()));
104 /* create failed */
105 free(pThreadImpl);
106 return nullptr;
109 return pThreadImpl;
112 oslThread SAL_CALL osl_createThread(oslWorkerFunction pWorker,
113 void* pThreadData)
115 return oslCreateThread(pWorker, pThreadData, 0);
118 oslThread SAL_CALL osl_createSuspendedThread(oslWorkerFunction pWorker,
119 void* pThreadData)
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);
130 else
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 */
141 return;
144 /* !!!! _exitthreadex does _not_ call CloseHandle !!! */
145 CloseHandle( pThreadImpl->m_hThread );
147 /* free memory */
148 free(Thread);
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)
172 int winPriority;
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.
182 switch(Priority) {
184 case osl_Thread_PriorityHighest:
185 winPriority= THREAD_PRIORITY_HIGHEST;
186 break;
188 case osl_Thread_PriorityAboveNormal:
189 winPriority= THREAD_PRIORITY_ABOVE_NORMAL;
190 break;
192 case osl_Thread_PriorityNormal:
193 winPriority= THREAD_PRIORITY_NORMAL;
194 break;
196 case osl_Thread_PriorityBelowNormal:
197 winPriority= THREAD_PRIORITY_BELOW_NORMAL;
198 break;
200 case osl_Thread_PriorityLowest:
201 winPriority= THREAD_PRIORITY_LOWEST;
202 break;
204 case osl_Thread_PriorityUnknown:
205 OSL_ASSERT(FALSE); /* only fools try this...*/
207 /* let release-version behave friendly */
208 return;
210 default:
211 OSL_ASSERT(FALSE); /* enum expanded, but forgotten here...*/
213 /* let release-version behave friendly */
214 return;
217 SetThreadPriority(pThreadImpl->m_hThread, winPriority);
220 oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
222 int winPriority;
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;
233 winPriority=
234 GetThreadPriority(pThreadImpl->m_hThread);
236 if(winPriority == THREAD_PRIORITY_ERROR_RETURN)
238 return osl_Thread_PriorityUnknown;
241 /* map WIN32 priority to enum */
242 switch(winPriority)
244 case THREAD_PRIORITY_TIME_CRITICAL:
245 case THREAD_PRIORITY_HIGHEST:
246 Priority= osl_Thread_PriorityHighest;
247 break;
249 case THREAD_PRIORITY_ABOVE_NORMAL:
250 Priority= osl_Thread_PriorityAboveNormal;
251 break;
253 case THREAD_PRIORITY_NORMAL:
254 Priority= osl_Thread_PriorityNormal;
255 break;
257 case THREAD_PRIORITY_BELOW_NORMAL:
258 Priority= osl_Thread_PriorityBelowNormal;
259 break;
261 case THREAD_PRIORITY_IDLE:
262 case THREAD_PRIORITY_LOWEST:
263 Priority= osl_Thread_PriorityLowest;
264 break;
266 default:
267 OSL_ASSERT(FALSE); /* WIN32 API changed, incorporate new prio-level! */
269 /* release-version behaves friendly */
270 Priority= osl_Thread_PriorityUnknown;
273 return Priority;
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)
283 return false;
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 */
297 return;
300 WaitForSingleObject(pThreadImpl->m_hThread, INFINITE);
303 void SAL_CALL osl_waitThread(const TimeValue* pDelay)
305 if (pDelay)
307 DWORD millisecs = pDelay->Seconds * 1000L + pDelay->Nanosec / 1000000L;
309 Sleep(millisecs);
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 */
321 return;
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);
331 osl_yieldThread();
333 /* invalid arguments?*/
334 if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
336 /* assume thread is not running */
337 return false;
340 return 0 == pThreadImpl->m_nTerminationRequested;
343 void SAL_CALL osl_yieldThread(void)
345 Sleep(0);
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);
361 free(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)
370 struct {
371 DWORD dwType = 0x1000;
372 LPCSTR szName;
373 DWORD dwThreadID = DWORD(-1);
374 DWORD dwFlags = 0;
375 } info;
376 #pragma pack(pop)
377 info.szName = name;
378 __try {
379 RaiseException(
380 0x406D1388, 0, sizeof info / sizeof (ULONG_PTR),
381 reinterpret_cast<ULONG_PTR *>(&info));
382 } __except (EXCEPTION_EXECUTE_HANDLER) {}
384 impSetThreadDescription(name);
387 namespace {
389 typedef struct TLS_
391 DWORD dwIndex;
392 oslThreadKeyCallbackFunction pfnCallback;
393 struct TLS_ *pNext, *pPrev;
394 } TLS, *PTLS;
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 )
407 if ( 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 )
423 if ( pTls )
425 std::lock_guard aGuard(getThreadKeyListMutex());
426 if ( pTls->pPrev )
427 pTls->pPrev->pNext = pTls->pNext;
428 else
430 OSL_ASSERT( pTls == g_pThreadKeyList );
431 g_pThreadKeyList = pTls->pNext;
434 if ( pTls->pNext )
435 pTls->pNext->pPrev = pTls->pPrev;
439 void osl_callThreadKeyCallbackOnThreadDetach(void)
441 PTLS pTls;
443 std::lock_guard aGuard(getThreadKeyListMutex());
444 pTls = g_pThreadKeyList;
445 while ( pTls )
447 if ( pTls->pfnCallback )
449 void *pValue = TlsGetValue( pTls->dwIndex );
451 if ( pValue )
452 pTls->pfnCallback( pValue );
455 pTls = pTls->pNext;
459 oslThreadKey SAL_CALL osl_createThreadKey(oslThreadKeyCallbackFunction pCallback)
461 PTLS pTls = static_cast<PTLS>(malloc( sizeof(TLS) ));
463 if ( pTls )
465 pTls->pfnCallback = pCallback;
466 if ( DWORD(-1) == (pTls->dwIndex = TlsAlloc()) )
468 free( pTls );
469 pTls = nullptr;
471 else
472 AddKeyToList( pTls );
475 return pTls;
478 void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
480 if (Key != nullptr)
482 PTLS pTls = static_cast<PTLS>(Key);
484 RemoveKeyFromList( pTls );
485 TlsFree( pTls->dwIndex );
486 free( pTls );
490 void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
492 if (Key != nullptr)
494 PTLS pTls = static_cast<PTLS>(Key);
496 return TlsGetValue( pTls->dwIndex );
499 return nullptr;
502 sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
504 if (Key != nullptr)
506 PTLS pTls = static_cast<PTLS>(Key);
507 void* pOldData = nullptr;
508 bool fSuccess;
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 );
518 return fSuccess;
521 return false;
524 rtl_TextEncoding getThreadTextEncodingForInitialization()
526 return rtl_getTextEncodingFromWindowsCodePage(GetACP());
529 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */