Quelle thread.cxx
Sprache: C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include "system.h"
#include "thread.hxx"
#include <thread_internal.hxx>
#include <comphelper/windowserrorstring.hxx>
#include <osl/diagnose.h>
#include <osl/mutex.hxx>
#include <osl/thread.h>
#include <rtl/alloc.h>
#include <osl/time.h>
#include <osl/interlck.h>
#include <rtl/tencinfo.h>
#include <sal/log.hxx>
#include <systools/win32/comtools.hxx>
#include <errno.h>
#include <mutex>
namespace {
/**
Thread-data structure hidden behind oslThread:
*/
typedef struct
{
HANDLE m_hThread;
/* OS-handle used for all thread-functions */
unsigned m_ThreadId;
/* identifier for this thread */
sal_Int32 m_nTerminationRequested;
oslWorkerFunction m_WorkerFunction;
void * m_pData;
} osl_TThreadImpl;
}
static oslThread oslCreateThread(oslWorkerFunction pWorker,
void * pThreadData, sal_uIn
t32 nFlags);
static unsigned __stdcall oslWorkerWrapperFunction(void * pData)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(pData);
/* Initialize COM - Multi Threaded Apartment (MTA) for all threads */
sal::systools::CoInitializeGuard aGuard(COINIT_MULTITHREADED, false ,
sal::systools::CoInitializeGuard::WhenFailed::NoThrow);
/* call worker-function with data */
pThreadImpl->m_WorkerFunction(pThreadImpl->m_pData);
return 0;
}
static oslThread oslCreateThread(oslWorkerFunction pWorker,
void * pThreadData,
sal_uInt32 nFlags)
{
osl_TThreadImpl* pThreadImpl;
/* alloc mem. for our internal data structure */
pThreadImpl= static_cast <osl_TThreadImpl *>(malloc(sizeof (osl_TThreadImpl)));
OSL_ASSERT(pThreadImpl);
if ( pThreadImpl == nullptr )
{
return nullptr;
}
pThreadImpl->m_WorkerFunction= pWorker;
pThreadImpl->m_pData= pThreadData;
pThreadImpl->m_nTerminationRequested= 0;
pThreadImpl->m_hThread= reinterpret_cast <HANDLE>(_beginthreadex(
nullptr, /* no security */
0, /* default stack-size */
oslWorkerWrapperFunction, /* worker-function */
pThreadImpl, /* provide worker-function with data */
nFlags, /* start thread immediately or suspended */
&pThreadImpl->m_ThreadId));
if (pThreadImpl->m_hThread == nullptr)
{
SAL_WARN("sal.osl" , "CreateThread failed:" << comphelper::WindowsErrorString(GetLastError()));
/* create failed */
free(pThreadImpl);
return nullptr;
}
return pThreadImpl;
}
oslThread SAL_CALL osl_createThread(oslWorkerFunction pWorker,
void * pThreadData)
{
return oslCreateThread(pWorker, pThreadData, 0);
}
oslThread SAL_CALL osl_createSuspendedThread(oslWorkerFunction pWorker,
void * pThreadData)
{
return oslCreateThread(pWorker, pThreadData, CREATE_SUSPENDED);
}
oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
if (pThreadImpl != nullptr)
return static_cast <oslThreadIdentifier>(pThreadImpl->m_ThreadId);
else
return static_cast <oslThreadIdentifier>(GetCurrentThreadId());
}
void SAL_CALL osl_destroyThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
if (Thread == nullptr) /* valid ptr? */
{
/* thread already destroyed or not created */
return ;
}
/* !!!! _exitthreadex does _not_ call CloseHandle !!! */
CloseHandle( pThreadImpl->m_hThread );
/* free memory */
free(Thread);
}
void SAL_CALL osl_resumeThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
assert(pThreadImpl); /* valid ptr? */
ResumeThread(pThreadImpl->m_hThread);
}
void SAL_CALL osl_suspendThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
assert(pThreadImpl); /* valid ptr? */
SuspendThread(pThreadImpl->m_hThread);
}
void SAL_CALL osl_setThreadPriority(oslThread Thread,
oslThreadPriority Priority)
{
int winPriority;
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
assert(pThreadImpl); /* valid ptr? */
/* map enum to WIN32 levels
it would be faster and more elegant to preset
the enums, but that would require an #ifdef in
the exported header, which is not desired.
*/
switch (Priority) {
case osl_Thread_PriorityHighest:
winPriority= THREAD_PRIORITY_HIGHEST;
break ;
case osl_Thread_PriorityAboveNormal:
winPriority= THREAD_PRIORITY_ABOVE_NORMAL;
break ;
case osl_Thread_PriorityNormal:
winPriority= THREAD_PRIORITY_NORMAL;
break ;
case osl_Thread_PriorityBelowNormal:
winPriority= THREAD_PRIORITY_BELOW_NORMAL;
break ;
case osl_Thread_PriorityLowest:
winPriority= THREAD_PRIORITY_LOWEST;
break ;
case osl_Thread_PriorityUnknown:
OSL_ASSERT(FALSE ); /* only fools try this...*/
/* let release-version behave friendly */
return ;
default :
OSL_ASSERT(FALSE ); /* enum expanded, but forgotten here...*/
/* let release-version behave friendly */
return ;
}
SetThreadPriority(pThreadImpl->m_hThread, winPriority);
}
oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
{
int winPriority;
oslThreadPriority Priority;
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
/* invalid arguments ?*/
if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
{
return osl_Thread_PriorityUnknown;
}
winPriority=
GetThreadPriority(pThreadImpl->m_hThread);
if (winPriority == THREAD_PRIORITY_ERROR_RETURN)
{
return osl_Thread_PriorityUnknown;
}
/* map WIN32 priority to enum */
switch (winPriority)
{
case THREAD_PRIORITY_TIME_CRITICAL:
case THREAD_PRIORITY_HIGHEST:
Priority= osl_Thread_PriorityHighest;
break ;
case THREAD_PRIORITY_ABOVE_NORMAL:
Priority= osl_Thread_PriorityAboveNormal;
break ;
case THREAD_PRIORITY_NORMAL:
Priority= osl_Thread_PriorityNormal;
break ;
case THREAD_PRIORITY_BELOW_NORMAL:
Priority= osl_Thread_PriorityBelowNormal;
break ;
case THREAD_PRIORITY_IDLE:
case THREAD_PRIORITY_LOWEST:
Priority= osl_Thread_PriorityLowest;
break ;
default :
OSL_ASSERT(FALSE ); /* WIN32 API changed, incorporate new prio-level! */
/* release-version behaves friendly */
Priority= osl_Thread_PriorityUnknown;
}
return Priority;
}
sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
/* invalid arguments ?*/
if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
{
return false ;
}
return WaitForSingleObject(pThreadImpl->m_hThread, 0) != WAIT_OBJECT_0;
}
void SAL_CALL osl_joinWithThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
/* invalid arguments?*/
if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
{
/* assume thread is not running */
return ;
}
WaitForSingleObject(pThreadImpl->m_hThread, INFINITE);
}
void SAL_CALL osl_waitThread(const TimeValue* pDelay)
{
if (pDelay)
{
DWORD millisecs = pDelay->Seconds * 1000L + pDelay->Nanosec / 1000000L;
Sleep(millisecs);
}
}
void SAL_CALL osl_terminateThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
/* invalid arguments?*/
if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
{
/* assume thread is not running */
return ;
}
osl_atomic_increment(&(pThreadImpl->m_nTerminationRequested));
}
sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= static_cast <osl_TThreadImpl*>(Thread);
osl_yieldThread();
/* invalid arguments?*/
if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
{
/* assume thread is not running */
return false ;
}
return 0 == pThreadImpl->m_nTerminationRequested;
}
void SAL_CALL osl_yieldThread(void )
{
Sleep(0);
}
static void impSetThreadDescription(char const * name) {
// SetThreadDescription is only available since Windows 10 version 1607
typedef HRESULT(WINAPI * TSetThreadDescription)(HANDLE, PCWSTR);
static const auto pSetThreadDescription = reinterpret_cast <TSetThreadDescription>(
GetProcAddress(GetModuleHandleA("KernelBase.dll" ), "SetThreadDescription" ));
if (pSetThreadDescription)
{
if (const int nReqCCh = MultiByteToWideChar(CP_ACP, 0, name, -1, nullptr, 0))
{
if (PWSTR wStr = static_cast <PWSTR>(malloc(nReqCCh * sizeof (WCHAR))))
{
if (MultiByteToWideChar(CP_ACP, 0, name, -1, wStr, nReqCCh))
pSetThreadDescription(GetCurrentThread(), wStr);
free(wStr);
}
}
}
}
void SAL_CALL osl_setThreadName(char const * name) {
/* See < https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code >: */
#pragma pack (push, 8)
struct {
DWORD dwType = 0x1000;
LPCSTR szName;
DWORD dwThreadID = DWORD(-1);
DWORD dwFlags = 0;
} info;
#pragma pack (pop)
info.szName = name;
__try {
RaiseException(
0x406D1388, 0, sizeof info / sizeof (ULONG_PTR),
reinterpret_cast <ULONG_PTR *>(&info));
} __except (EXCEPTION_EXECUTE_HANDLER) {}
impSetThreadDescription(name);
}
namespace {
typedef struct TLS_
{
DWORD dwIndex;
oslThreadKeyCallbackFunction pfnCallback;
struct TLS_ *pNext, *pPrev;
} TLS, *PTLS;
PTLS g_pThreadKeyList = nullptr;
std::mutex& getThreadKeyListMutex()
{
static std::mutex g_ThreadKeyListMutex;
return g_ThreadKeyListMutex;
}
}
static void AddKeyToList( PTLS pTls )
{
if ( pTls )
{
std::lock_guard aGuard(getThreadKeyListMutex());
pTls->pNext = g_pThreadKeyList;
pTls->pPrev = nullptr;
if ( g_pThreadKeyList )
g_pThreadKeyList->pPrev = pTls;
g_pThreadKeyList = pTls;
}
}
static void RemoveKeyFromList( PTLS pTls )
{
if ( pTls )
{
std::lock_guard aGuard(getThreadKeyListMutex());
if ( pTls->pPrev )
pTls->pPrev->pNext = pTls->pNext;
else
{
OSL_ASSERT( pTls == g_pThreadKeyList );
g_pThreadKeyList = pTls->pNext;
}
if ( pTls->pNext )
pTls->pNext->pPrev = pTls->pPrev;
}
}
void osl_callThreadKeyCallbackOnThreadDetach(void )
{
PTLS pTls;
std::lock_guard aGuard(getThreadKeyListMutex());
pTls = g_pThreadKeyList;
while ( pTls )
{
if ( pTls->pfnCallback )
{
void *pValue = TlsGetValue( pTls->dwIndex );
if ( pValue )
pTls->pfnCallback( pValue );
}
pTls = pTls->pNext;
}
}
oslThreadKey SAL_CALL osl_createThreadKey(oslThreadKeyCallbackFunction pCallback)
{
PTLS pTls = static_cast <PTLS>(malloc( sizeof (TLS) ));
if ( pTls )
{
pTls->pfnCallback = pCallback;
if ( DWORD(-1) == (pTls->dwIndex = TlsAlloc()) )
{
free( pTls );
pTls = nullptr;
}
else
AddKeyToList( pTls );
}
return pTls;
}
void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
{
if (Key != nullptr)
{
PTLS pTls = static_cast <PTLS>(Key);
RemoveKeyFromList( pTls );
TlsFree( pTls->dwIndex );
free( pTls );
}
}
void * SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
{
if (Key != nullptr)
{
PTLS pTls = static_cast <PTLS>(Key);
return TlsGetValue( pTls->dwIndex );
}
return nullptr;
}
sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
{
if (Key != nullptr)
{
PTLS pTls = static_cast <PTLS>(Key);
void * pOldData = nullptr;
bool fSuccess;
if ( pTls->pfnCallback )
pOldData = TlsGetValue( pTls->dwIndex );
fSuccess = TlsSetValue( pTls->dwIndex, pData );
if ( fSuccess && pTls->pfnCallback && pOldData )
pTls->pfnCallback( pOldData );
return fSuccess;
}
return false ;
}
rtl_TextEncoding getThreadTextEncodingForInitialization()
{
return rtl_getTextEncodingFromWindowsCodePage(GetACP());
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=93 H=92 G=92
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland
2026-04-04