blob: 75ba55a24a822f5901541d734b481f32d82c28a5 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "system.h"
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <rtl/alloc.h>
#include <osl/time.h>
#include <osl/interlck.h>
#include <rtl/tencinfo.h>
/*
Thread-data structure hidden behind oslThread:
*/
typedef struct _osl_TThreadImpl
{
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;
#define THREADIMPL_FLAGS_TERMINATE 0x0001
static unsigned __stdcall oslWorkerWrapperFunction(void* pData);
static oslThread oslCreateThread(oslWorkerFunction pWorker, void* pThreadData, sal_uInt32 nFlags);
/*****************************************************************************/
/* oslWorkerWrapperFunction */
/*****************************************************************************/
static unsigned __stdcall oslWorkerWrapperFunction(void* pData)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)pData;
/* Initialize COM */
CoInitializeEx(NULL, COINIT_MULTITHREADED);
/* call worker-function with data */
pThreadImpl->m_WorkerFunction(pThreadImpl->m_pData);
CoUninitialize();
return (0);
}
/*****************************************************************************/
/* oslCreateThread */
/*****************************************************************************/
static oslThread oslCreateThread(oslWorkerFunction pWorker,
void* pThreadData,
sal_uInt32 nFlags)
{
osl_TThreadImpl* pThreadImpl;
/* alloc mem. for our internal data structure */
pThreadImpl= malloc(sizeof(osl_TThreadImpl));
OSL_ASSERT(pThreadImpl);
if ( pThreadImpl == 0 )
{
return 0;
}
pThreadImpl->m_WorkerFunction= pWorker;
pThreadImpl->m_pData= pThreadData;
pThreadImpl->m_nTerminationRequested= 0;
pThreadImpl->m_hThread=
(HANDLE)_beginthreadex(NULL, /* 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 == 0)
{
/* create failed */
free(pThreadImpl);
return 0;
}
return (oslThread)pThreadImpl;
}
/*****************************************************************************/
/* osl_createThread */
/*****************************************************************************/
oslThread SAL_CALL osl_createThread(oslWorkerFunction pWorker,
void* pThreadData)
{
return oslCreateThread(pWorker, pThreadData, 0);
}
/*****************************************************************************/
/* osl_createSuspendedThread */
/*****************************************************************************/
oslThread SAL_CALL osl_createSuspendedThread(oslWorkerFunction pWorker,
void* pThreadData)
{
return oslCreateThread(pWorker, pThreadData, CREATE_SUSPENDED);
}
/*****************************************************************************/
/* osl_getThreadIdentifier */
/*****************************************************************************/
oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
if (pThreadImpl != NULL)
return ((oslThreadIdentifier)pThreadImpl->m_ThreadId);
else
return ((oslThreadIdentifier)GetCurrentThreadId());
}
/*****************************************************************************/
/* osl_destroyThread */
/*****************************************************************************/
void SAL_CALL osl_destroyThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
if (Thread == 0) /* valid ptr? */
{
/* thread already destroyed or not created */
return;
}
/* !!!! _exitthreadex does _not_ call CloseHandle !!! */
CloseHandle( pThreadImpl->m_hThread );
/* free memory */
free(Thread);
}
/*****************************************************************************/
/* osl_resumeThread */
/*****************************************************************************/
void SAL_CALL osl_resumeThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
OSL_ASSERT(pThreadImpl); /* valid ptr? */
ResumeThread(pThreadImpl->m_hThread);
}
/*****************************************************************************/
/* osl_suspendThread */
/*****************************************************************************/
void SAL_CALL osl_suspendThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
OSL_ASSERT(pThreadImpl); /* valid ptr? */
SuspendThread(pThreadImpl->m_hThread);
}
/*****************************************************************************/
/* osl_setThreadPriority */
/*****************************************************************************/
void SAL_CALL osl_setThreadPriority(oslThread Thread,
oslThreadPriority Priority)
{
int winPriority;
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
OSL_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);
}
/*****************************************************************************/
/* osl_getThreadPriority */
/*****************************************************************************/
oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
{
int winPriority;
oslThreadPriority Priority;
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
/* invalid arguments ?*/
if(pThreadImpl==0 || pThreadImpl->m_hThread==0)
{
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;
}
/*****************************************************************************/
/* osl_isThreadRunning */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
/* invalid arguments ?*/
if(pThreadImpl==0 || pThreadImpl->m_hThread==0)
{
return sal_False;
}
return (sal_Bool)(WaitForSingleObject(pThreadImpl->m_hThread, 0) != WAIT_OBJECT_0);
}
/*****************************************************************************/
/* osl_joinWithThread */
/*****************************************************************************/
void SAL_CALL osl_joinWithThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
/* invalid arguments?*/
if(pThreadImpl==0 || pThreadImpl->m_hThread==0)
{
/* assume thread is not running */
return;
}
WaitForSingleObject(pThreadImpl->m_hThread, INFINITE);
}
/*****************************************************************************/
/* osl_waitThread */
/*****************************************************************************/
void SAL_CALL osl_waitThread(const TimeValue* pDelay)
{
if (pDelay)
{
DWORD millisecs = pDelay->Seconds * 1000L + pDelay->Nanosec / 1000000L;
Sleep(millisecs);
}
}
/*****************************************************************************/
/* osl_terminateThread */
/*****************************************************************************/
void SAL_CALL osl_terminateThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
/* invalid arguments?*/
if (pThreadImpl==0 || pThreadImpl->m_hThread==0)
{
/* assume thread is not running */
return;
}
osl_incrementInterlockedCount(&(pThreadImpl->m_nTerminationRequested));
}
/*****************************************************************************/
/* osl_scheduleThread */
/*****************************************************************************/
sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
{
osl_TThreadImpl* pThreadImpl= (osl_TThreadImpl*)Thread;
osl_yieldThread();
/* invalid arguments?*/
if (pThreadImpl==0 || pThreadImpl->m_hThread==0)
{
/* assume thread is not running */
return sal_False;
}
return (sal_Bool)(0 == pThreadImpl->m_nTerminationRequested);
}
/*****************************************************************************/
/* osl_yieldThread */
/*****************************************************************************/
void SAL_CALL osl_yieldThread(void)
{
Sleep(0);
}
void SAL_CALL osl_setThreadName(char const * name) {
#ifdef _MSC_VER
/* See <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>: */
#pragma pack(push, 8)
struct {
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
} info;
#pragma pack(pop)
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = (DWORD) -1;
info.dwFlags = 0;
__try {
RaiseException(
0x406D1388, 0, sizeof info / sizeof (ULONG_PTR),
(ULONG_PTR *) &info);
} __except (EXCEPTION_EXECUTE_HANDLER) {}
#else
(void) name;
#endif
}
typedef struct _TLS
{
DWORD dwIndex;
oslThreadKeyCallbackFunction pfnCallback;
struct _TLS *pNext, *pPrev;
} TLS, *PTLS;
static PTLS g_pThreadKeyList = NULL;
CRITICAL_SECTION g_ThreadKeyListCS;
static void AddKeyToList( PTLS pTls )
{
if ( pTls )
{
EnterCriticalSection( &g_ThreadKeyListCS );
pTls->pNext = g_pThreadKeyList;
pTls->pPrev = 0;
if ( g_pThreadKeyList )
g_pThreadKeyList->pPrev = pTls;
g_pThreadKeyList = pTls;
LeaveCriticalSection( &g_ThreadKeyListCS );
}
}
static void RemoveKeyFromList( PTLS pTls )
{
if ( pTls )
{
EnterCriticalSection( &g_ThreadKeyListCS );
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;
LeaveCriticalSection( &g_ThreadKeyListCS );
}
}
void SAL_CALL _osl_callThreadKeyCallbackOnThreadDetach(void)
{
PTLS pTls;
EnterCriticalSection( &g_ThreadKeyListCS );
pTls = g_pThreadKeyList;
while ( pTls )
{
if ( pTls->pfnCallback )
{
void *pValue = TlsGetValue( pTls->dwIndex );
if ( pValue )
pTls->pfnCallback( pValue );
}
pTls = pTls->pNext;
}
LeaveCriticalSection( &g_ThreadKeyListCS );
}
/*****************************************************************************/
/* osl_createThreadKey */
/*****************************************************************************/
oslThreadKey SAL_CALL osl_createThreadKey(oslThreadKeyCallbackFunction pCallback)
{
PTLS pTls = rtl_allocateMemory( sizeof(TLS) );
if ( pTls )
{
pTls->pfnCallback = pCallback;
if ( (DWORD)-1 == (pTls->dwIndex = TlsAlloc()) )
{
rtl_freeMemory( pTls );
pTls = 0;
}
else
AddKeyToList( pTls );
}
return ((oslThreadKey)pTls);
}
/*****************************************************************************/
/* osl_destroyThreadKey */
/*****************************************************************************/
void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
{
if (Key != 0)
{
PTLS pTls = (PTLS)Key;
RemoveKeyFromList( pTls );
TlsFree( pTls->dwIndex );
rtl_freeMemory( pTls );
}
}
/*****************************************************************************/
/* osl_getThreadKeyData */
/*****************************************************************************/
void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
{
if (Key != 0)
{
PTLS pTls = (PTLS)Key;
return (TlsGetValue( pTls->dwIndex ));
}
return (NULL);
}
/*****************************************************************************/
/* osl_setThreadKeyData */
/*****************************************************************************/
sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
{
if (Key != 0)
{
PTLS pTls = (PTLS)Key;
void* pOldData = NULL;
BOOL fSuccess;
if ( pTls->pfnCallback )
pOldData = TlsGetValue( pTls->dwIndex );
fSuccess = TlsSetValue( pTls->dwIndex, pData );
if ( fSuccess && pTls->pfnCallback && pOldData )
pTls->pfnCallback( pOldData );
return (sal_Bool)(fSuccess != FALSE);
}
return (sal_False);
}
/*****************************************************************************/
/* osl_getThreadTextEncoding */
/*****************************************************************************/
DWORD g_dwTLSTextEncodingIndex = (DWORD)-1;
rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding(void)
{
DWORD dwEncoding;
rtl_TextEncoding _encoding;
BOOL gotACP;
if ( (DWORD)-1 == g_dwTLSTextEncodingIndex )
g_dwTLSTextEncodingIndex = TlsAlloc();
dwEncoding = (DWORD)TlsGetValue( g_dwTLSTextEncodingIndex );
_encoding = LOWORD(dwEncoding);
gotACP = HIWORD(dwEncoding);
if ( !gotACP )
{
char *pszEncoding;
if ( NULL != (pszEncoding = getenv( "SOLAR_USER_RTL_TEXTENCODING" )) )
_encoding = (rtl_TextEncoding)atoi(pszEncoding);
else
_encoding = rtl_getTextEncodingFromWindowsCodePage( GetACP() );
TlsSetValue( g_dwTLSTextEncodingIndex, (LPVOID)MAKELONG( _encoding, TRUE ) );
}
return _encoding;
}
/*****************************************************************************/
/* osl_getThreadTextEncoding */
/*****************************************************************************/
rtl_TextEncoding SAL_CALL osl_setThreadTextEncoding( rtl_TextEncoding Encoding )
{
rtl_TextEncoding oldEncoding = osl_getThreadTextEncoding();
TlsSetValue( g_dwTLSTextEncodingIndex, (LPVOID)MAKELONG( Encoding, TRUE) );
return oldEncoding;
}