4663c56edf
Change-Id: I7b8b020bdbcd5b4db4cb478cc5fe1225f19ae0cf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161268 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
1042 lines
28 KiB
C++
1042 lines
28 KiB
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 <sal/config.h>
|
|
|
|
#include <cassert>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <unistd.h>
|
|
|
|
#include "system.hxx"
|
|
#include "unixerrnostring.hxx"
|
|
#include <thread_internal.hxx>
|
|
|
|
#include <string.h>
|
|
#if defined(OPENBSD)
|
|
#include <sched.h>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#if __FreeBSD_version <= 1201517
|
|
#include <pthread_np.h>
|
|
#define pthread_setname_np pthread_set_name_np
|
|
#endif
|
|
#endif
|
|
#include <config_options.h>
|
|
#include <o3tl/safeint.hxx>
|
|
#include <osl/thread.h>
|
|
#include <osl/nlsupport.h>
|
|
#include <rtl/textenc.h>
|
|
#include <sal/log.hxx>
|
|
#include <sal/macros.h>
|
|
#ifdef ANDROID
|
|
#include <jni.h>
|
|
#include <android/log.h>
|
|
#include <osl/detail/android-bootstrap.h>
|
|
#endif
|
|
|
|
#if defined LINUX && ! defined __FreeBSD_kernel__
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* @@@ TODO @@@
|
|
*
|
|
* (1) 'osl_thread_priority_init_Impl()'
|
|
* - insane assumption that initializing caller is main thread
|
|
* - use _POSIX_THREAD_PRIORITY_SCHEDULING, not NO_PTHREAD_PRIORITY (?)
|
|
* - POSIX doesn't require defined prio's for SCHED_OTHER (!)
|
|
* - use SCHED_RR instead of SCHED_OTHER for defined behaviour (?)
|
|
* (2) 'oslThreadIdentifier' and '{insert|remove|lookup}ThreadId()'
|
|
* - cannot reliably be applied to 'alien' threads;
|
|
* - memory leak for 'alien' thread 'HashEntry's;
|
|
* - use 'reinterpret_cast<unsigned long>(pthread_t)' as identifier
|
|
* instead (?)
|
|
* - if yes, change 'oslThreadIdentifier' to 'intptr_t' or similar
|
|
* (3) 'oslSigAlarmHandler()' (#71232#)
|
|
* - [Under Solaris we get SIGALRM in e.g. pthread_join which terminates
|
|
* the process. So we initialize our signal handling module and do
|
|
* register a SIGALRM Handler which catches and ignores it]
|
|
* - should this still happen, 'signal.c' needs to be fixed instead.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#define THREADIMPL_FLAGS_TERMINATE 0x00001
|
|
#define THREADIMPL_FLAGS_STARTUP 0x00002
|
|
#define THREADIMPL_FLAGS_SUSPENDED 0x00004
|
|
#define THREADIMPL_FLAGS_ACTIVE 0x00008
|
|
#define THREADIMPL_FLAGS_ATTACHED 0x00010
|
|
#define THREADIMPL_FLAGS_DESTROYED 0x00020
|
|
|
|
namespace {
|
|
|
|
typedef struct osl_thread_impl_st
|
|
{
|
|
pthread_t m_hThread;
|
|
oslThreadIdentifier m_Ident; /* @@@ see TODO @@@ */
|
|
short m_Flags;
|
|
oslWorkerFunction m_WorkerFunction;
|
|
void* m_pData;
|
|
pthread_mutex_t m_Lock;
|
|
pthread_cond_t m_Cond;
|
|
} Thread_Impl;
|
|
|
|
#if !defined NO_PTHREAD_PRIORITY
|
|
struct osl_thread_priority_st
|
|
{
|
|
int m_Highest;
|
|
int m_Above_Normal;
|
|
int m_Normal;
|
|
int m_Below_Normal;
|
|
int m_Lowest;
|
|
};
|
|
#define OSL_THREAD_PRIORITY_INITIALIZER { 127, 96, 64, 32, 0 }
|
|
#endif
|
|
|
|
}
|
|
|
|
#if !defined NO_PTHREAD_PRIORITY
|
|
|
|
namespace {
|
|
|
|
struct osl_thread_global_st
|
|
{
|
|
pthread_once_t m_once;
|
|
struct osl_thread_priority_st m_priority;
|
|
};
|
|
|
|
}
|
|
|
|
static struct osl_thread_global_st g_thread =
|
|
{
|
|
PTHREAD_ONCE_INIT,
|
|
OSL_THREAD_PRIORITY_INITIALIZER
|
|
};
|
|
|
|
#endif // !defined NO_PTHREAD_PRIORITY
|
|
|
|
static Thread_Impl* osl_thread_construct_Impl();
|
|
static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl);
|
|
|
|
static void* osl_thread_start_Impl (void * pData);
|
|
static void osl_thread_cleanup_Impl (Thread_Impl * pImpl);
|
|
|
|
static oslThread osl_thread_create_Impl (
|
|
oslWorkerFunction pWorker, void * pThreadData, short nFlags);
|
|
|
|
/* @@@ see TODO @@@ */
|
|
static oslThreadIdentifier insertThreadId (pthread_t hThread);
|
|
static oslThreadIdentifier lookupThreadId (pthread_t hThread);
|
|
static void removeThreadId (pthread_t hThread);
|
|
|
|
Thread_Impl* osl_thread_construct_Impl()
|
|
{
|
|
Thread_Impl* pImpl = new Thread_Impl;
|
|
memset (pImpl, 0, sizeof(Thread_Impl));
|
|
|
|
pthread_mutex_init (&(pImpl->m_Lock), PTHREAD_MUTEXATTR_DEFAULT);
|
|
pthread_cond_init (&(pImpl->m_Cond), PTHREAD_CONDATTR_DEFAULT);
|
|
return pImpl;
|
|
}
|
|
|
|
static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl)
|
|
{
|
|
assert(ppImpl);
|
|
if (*ppImpl)
|
|
{
|
|
pthread_cond_destroy (&((*ppImpl)->m_Cond));
|
|
pthread_mutex_destroy (&((*ppImpl)->m_Lock));
|
|
|
|
delete *ppImpl;
|
|
(*ppImpl) = nullptr;
|
|
}
|
|
}
|
|
|
|
static void osl_thread_cleanup_Impl (Thread_Impl * pImpl)
|
|
{
|
|
pthread_t thread;
|
|
bool attached;
|
|
bool destroyed;
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
thread = pImpl->m_hThread;
|
|
attached = (pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) != 0;
|
|
destroyed = (pImpl->m_Flags & THREADIMPL_FLAGS_DESTROYED) != 0;
|
|
pImpl->m_Flags &= ~(THREADIMPL_FLAGS_ACTIVE | THREADIMPL_FLAGS_ATTACHED);
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
|
|
/* release oslThreadIdentifier @@@ see TODO @@@ */
|
|
removeThreadId (thread);
|
|
|
|
if (attached)
|
|
{
|
|
pthread_detach (thread);
|
|
}
|
|
|
|
if (destroyed)
|
|
{
|
|
osl_thread_destruct_Impl (&pImpl);
|
|
}
|
|
}
|
|
|
|
static void* osl_thread_start_Impl (void* pData)
|
|
{
|
|
bool terminate;
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(pData);
|
|
|
|
assert(pImpl);
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
/* request oslThreadIdentifier @@@ see TODO @@@ */
|
|
pImpl->m_Ident = insertThreadId (pImpl->m_hThread);
|
|
|
|
/* signal change from STARTUP to ACTIVE state */
|
|
pImpl->m_Flags &= ~THREADIMPL_FLAGS_STARTUP;
|
|
pImpl->m_Flags |= THREADIMPL_FLAGS_ACTIVE;
|
|
pthread_cond_signal (&(pImpl->m_Cond));
|
|
|
|
/* Check if thread is started in SUSPENDED state */
|
|
while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
|
|
{
|
|
/* wait until SUSPENDED flag is cleared */
|
|
pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
|
|
}
|
|
|
|
/* check for SUSPENDED to TERMINATE state change */
|
|
terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
|
|
if (!terminate)
|
|
{
|
|
#ifdef ANDROID
|
|
JNIEnv* env = 0;
|
|
int res = (*lo_get_javavm()).AttachCurrentThread(&env, NULL);
|
|
__android_log_print(ANDROID_LOG_INFO, "LibreOffice", "New sal thread started and attached res=%d", res);
|
|
#endif
|
|
/* call worker function */
|
|
pImpl->m_WorkerFunction(pImpl->m_pData);
|
|
|
|
#ifdef ANDROID
|
|
res = (*lo_get_javavm()).DetachCurrentThread();
|
|
__android_log_print(ANDROID_LOG_INFO, "LibreOffice", "Detached finished sal thread res=%d", res);
|
|
#endif
|
|
}
|
|
|
|
osl_thread_cleanup_Impl (pImpl);
|
|
return nullptr;
|
|
}
|
|
|
|
static oslThread osl_thread_create_Impl (
|
|
oslWorkerFunction pWorker,
|
|
void* pThreadData,
|
|
short nFlags)
|
|
{
|
|
Thread_Impl* pImpl;
|
|
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
|
|
pthread_attr_t attr;
|
|
size_t stacksize;
|
|
#endif
|
|
int nRet=0;
|
|
|
|
pImpl = osl_thread_construct_Impl();
|
|
if (!pImpl)
|
|
return nullptr; /* ENOMEM */
|
|
|
|
pImpl->m_WorkerFunction = pWorker;
|
|
pImpl->m_pData = pThreadData;
|
|
pImpl->m_Flags = nFlags | THREADIMPL_FLAGS_STARTUP;
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
|
|
if (pthread_attr_init(&attr) != 0)
|
|
return nullptr;
|
|
|
|
#if defined OPENBSD
|
|
stacksize = 262144;
|
|
#elif !ENABLE_RUNTIME_OPTIMIZATIONS
|
|
stacksize = 12 * 1024 * 1024; // 8MB is not enough for ASAN on x86-64
|
|
#else
|
|
stacksize = 1 * 1024 * 1024; // macOS default for non-main threads (512kB) is not enough...
|
|
#endif
|
|
if (pthread_attr_setstacksize(&attr, stacksize) != 0) {
|
|
pthread_attr_destroy(&attr);
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
if ((nRet = pthread_create (
|
|
&(pImpl->m_hThread),
|
|
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
|
|
&attr,
|
|
#else
|
|
PTHREAD_ATTR_DEFAULT,
|
|
#endif
|
|
osl_thread_start_Impl,
|
|
static_cast<void*>(pImpl))) != 0)
|
|
{
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"pthread_create failed: " << UnixErrnoString(nRet));
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
osl_thread_destruct_Impl (&pImpl);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
|
|
pthread_attr_destroy(&attr);
|
|
#endif
|
|
|
|
/* wait for change from STARTUP to ACTIVE state */
|
|
while (pImpl->m_Flags & THREADIMPL_FLAGS_STARTUP)
|
|
{
|
|
/* wait until STARTUP flag is cleared */
|
|
pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
|
|
}
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
|
|
return static_cast<oslThread>(pImpl);
|
|
}
|
|
|
|
oslThread osl_createThread (
|
|
oslWorkerFunction pWorker,
|
|
void * pThreadData)
|
|
{
|
|
return osl_thread_create_Impl (
|
|
pWorker,
|
|
pThreadData,
|
|
THREADIMPL_FLAGS_ATTACHED);
|
|
}
|
|
|
|
oslThread osl_createSuspendedThread (
|
|
oslWorkerFunction pWorker,
|
|
void * pThreadData)
|
|
{
|
|
return osl_thread_create_Impl (
|
|
pWorker,
|
|
pThreadData,
|
|
THREADIMPL_FLAGS_ATTACHED |
|
|
THREADIMPL_FLAGS_SUSPENDED );
|
|
}
|
|
|
|
void SAL_CALL osl_destroyThread(oslThread Thread)
|
|
{
|
|
if (Thread != nullptr) {
|
|
Thread_Impl * impl = static_cast<Thread_Impl *>(Thread);
|
|
bool active;
|
|
pthread_mutex_lock(&impl->m_Lock);
|
|
active = (impl->m_Flags & THREADIMPL_FLAGS_ACTIVE) != 0;
|
|
impl->m_Flags |= THREADIMPL_FLAGS_DESTROYED;
|
|
pthread_mutex_unlock(&impl->m_Lock);
|
|
if (!active) {
|
|
osl_thread_destruct_Impl(&impl);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAL_CALL osl_resumeThread(oslThread Thread)
|
|
{
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_resumeThread(nullptr) call");
|
|
return; /* EINVAL */
|
|
}
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
|
|
{
|
|
/* clear SUSPENDED flag */
|
|
pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
|
|
pthread_cond_signal (&(pImpl->m_Cond));
|
|
}
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
}
|
|
|
|
void SAL_CALL osl_suspendThread(oslThread Thread)
|
|
{
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_suspendThread(nullptr) call");
|
|
return; /* EINVAL */
|
|
}
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
pImpl->m_Flags |= THREADIMPL_FLAGS_SUSPENDED;
|
|
|
|
if (pthread_equal (pthread_self(), pImpl->m_hThread))
|
|
{
|
|
/* self suspend */
|
|
while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
|
|
{
|
|
/* wait until SUSPENDED flag is cleared */
|
|
pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
}
|
|
|
|
sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
|
|
{
|
|
bool active;
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
return false;
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
active = ((pImpl->m_Flags & THREADIMPL_FLAGS_ACTIVE) > 0);
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
|
|
return active;
|
|
}
|
|
|
|
void SAL_CALL osl_joinWithThread(oslThread Thread)
|
|
{
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
return;
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
pthread_t const thread = pImpl->m_hThread;
|
|
bool const attached = ((pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) > 0);
|
|
|
|
/* check this only if *this* thread is still attached - if it's not,
|
|
then it could have terminated and another newly created thread could
|
|
have recycled the same id as m_hThread! */
|
|
if (attached && pthread_equal(pthread_self(), pImpl->m_hThread))
|
|
{
|
|
assert(false); /* Win32 implementation would deadlock here! */
|
|
/* self join */
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
return; /* EDEADLK */
|
|
}
|
|
|
|
pImpl->m_Flags &= ~THREADIMPL_FLAGS_ATTACHED;
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
|
|
if (attached)
|
|
{
|
|
pthread_join (thread, nullptr);
|
|
}
|
|
}
|
|
|
|
void SAL_CALL osl_terminateThread(oslThread Thread)
|
|
{
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_terminateThread(nullptr) call");
|
|
return; /* EINVAL */
|
|
}
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
|
|
{
|
|
/* clear SUSPENDED flag */
|
|
pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
|
|
pthread_cond_signal (&(pImpl->m_Cond));
|
|
}
|
|
|
|
pImpl->m_Flags |= THREADIMPL_FLAGS_TERMINATE;
|
|
|
|
pthread_mutex_unlock (&(pImpl->m_Lock));
|
|
}
|
|
|
|
sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
|
|
{
|
|
bool terminate;
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_scheduleThread(nullptr) call");
|
|
return false; /* EINVAL */
|
|
}
|
|
|
|
if (!(pthread_equal (pthread_self(), pImpl->m_hThread)))
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_scheduleThread(non-self) call");
|
|
return false; /* EINVAL */
|
|
}
|
|
|
|
pthread_mutex_lock (&(pImpl->m_Lock));
|
|
|
|
while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
|
|
{
|
|
/* wait until SUSPENDED flag is cleared */
|
|
pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
|
|
}
|
|
|
|
terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
|
|
|
|
pthread_mutex_unlock(&(pImpl->m_Lock));
|
|
|
|
return !terminate;
|
|
}
|
|
|
|
void SAL_CALL osl_waitThread(const TimeValue* pDelay)
|
|
{
|
|
if (pDelay)
|
|
{
|
|
struct timespec delay;
|
|
|
|
SET_TIMESPEC(delay, pDelay->Seconds, pDelay->Nanosec);
|
|
|
|
SLEEP_TIMESPEC(delay);
|
|
}
|
|
}
|
|
|
|
/** Yields thread
|
|
|
|
@attention Note that POSIX scheduling @em really requires threads to call this
|
|
function, since a thread only reschedules to other thread, when
|
|
it blocks (sleep, blocking I/O) OR calls sched_yield().
|
|
*/
|
|
void SAL_CALL osl_yieldThread()
|
|
{
|
|
sched_yield();
|
|
}
|
|
|
|
void SAL_CALL osl_setThreadName(char const * name)
|
|
{
|
|
assert( name );
|
|
#if defined LINUX && ! defined __FreeBSD_kernel__
|
|
const int LINUX_THREAD_NAME_MAXLEN = 15;
|
|
if ( strlen( name ) > LINUX_THREAD_NAME_MAXLEN )
|
|
SAL_INFO( "sal.osl", "osl_setThreadName truncated thread name to "
|
|
<< LINUX_THREAD_NAME_MAXLEN << " chars from name '"
|
|
<< name << "'" );
|
|
char shortname[ LINUX_THREAD_NAME_MAXLEN + 1 ];
|
|
shortname[ LINUX_THREAD_NAME_MAXLEN ] = '\0';
|
|
strncpy( shortname, name, LINUX_THREAD_NAME_MAXLEN );
|
|
int err = pthread_setname_np( pthread_self(), shortname );
|
|
if ( 0 != err )
|
|
SAL_WARN("sal.osl", "pthread_setname_np failed with errno " << err);
|
|
#elif defined __FreeBSD__
|
|
pthread_setname_np( pthread_self(), name );
|
|
#elif defined MACOSX || defined IOS
|
|
pthread_setname_np( name );
|
|
#else
|
|
(void) name;
|
|
#endif
|
|
}
|
|
|
|
/* osl_getThreadIdentifier @@@ see TODO @@@ */
|
|
|
|
namespace {
|
|
|
|
struct HashEntry
|
|
{
|
|
pthread_t Handle;
|
|
oslThreadIdentifier Ident;
|
|
HashEntry * Next;
|
|
};
|
|
|
|
}
|
|
|
|
static HashEntry* HashTable[31];
|
|
const int HashSize = SAL_N_ELEMENTS(HashTable);
|
|
|
|
static std::mutex HashLock;
|
|
|
|
#if ! ((defined LINUX && !defined __FreeBSD_kernel__) || defined MACOSX || defined IOS)
|
|
static oslThreadIdentifier LastIdent = 0;
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
std::size_t HASHID(pthread_t x)
|
|
{ return std::hash<pthread_t>()(x) % HashSize; }
|
|
|
|
}
|
|
|
|
static oslThreadIdentifier lookupThreadId (pthread_t hThread)
|
|
{
|
|
HashEntry *pEntry;
|
|
|
|
std::unique_lock aGuard(HashLock);
|
|
|
|
pEntry = HashTable[HASHID(hThread)];
|
|
while (pEntry != nullptr)
|
|
{
|
|
if (pthread_equal(pEntry->Handle, hThread))
|
|
{
|
|
return pEntry->Ident;
|
|
}
|
|
pEntry = pEntry->Next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static oslThreadIdentifier insertThreadId (pthread_t hThread)
|
|
{
|
|
HashEntry *pEntry, *pInsert = nullptr;
|
|
|
|
std::unique_lock aGuard(HashLock);
|
|
|
|
pEntry = HashTable[HASHID(hThread)];
|
|
|
|
while (pEntry != nullptr)
|
|
{
|
|
if (pthread_equal(pEntry->Handle, hThread))
|
|
break;
|
|
|
|
pInsert = pEntry;
|
|
pEntry = pEntry->Next;
|
|
}
|
|
|
|
if (pEntry == nullptr)
|
|
{
|
|
pEntry = static_cast<HashEntry*>(calloc(1, sizeof(HashEntry)));
|
|
|
|
pEntry->Handle = hThread;
|
|
|
|
#if defined LINUX && ! defined __FreeBSD_kernel__
|
|
#if defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
|
|
// gettid returns a pid_t, which POSIX defines to be a signed integer type; assume that all
|
|
// valid pid_t values on Linux are positive (zero is filtered out in the generic code
|
|
// below):
|
|
pid_t const tid = gettid();
|
|
assert(tid >= 0);
|
|
#else
|
|
long const tid = syscall(SYS_gettid);
|
|
if (tid < 0 || o3tl::make_unsigned(tid) > std::numeric_limits<sal_uInt32>::max()) {
|
|
std::abort();
|
|
}
|
|
#endif
|
|
pEntry->Ident = tid;
|
|
#elif defined MACOSX || defined IOS
|
|
// currently the value of pthread_threadid_np is the same then
|
|
// syscall(SYS_thread_selfid), which returns an int as the TID.
|
|
// may change, as the syscall interface was deprecated.
|
|
uint64_t mac_tid;
|
|
pthread_threadid_np(nullptr, &mac_tid);
|
|
if (mac_tid > SAL_MAX_UINT32)
|
|
std::abort();
|
|
pEntry->Ident = mac_tid;
|
|
#else
|
|
++LastIdent;
|
|
if (0 == LastIdent)
|
|
LastIdent = 1;
|
|
pEntry->Ident = LastIdent;
|
|
#endif
|
|
if (0 == pEntry->Ident)
|
|
std::abort();
|
|
|
|
if (pInsert)
|
|
pInsert->Next = pEntry;
|
|
else
|
|
HashTable[HASHID(hThread)] = pEntry;
|
|
}
|
|
|
|
return pEntry->Ident;
|
|
}
|
|
|
|
static void removeThreadId (pthread_t hThread)
|
|
{
|
|
HashEntry *pEntry, *pRemove = nullptr;
|
|
|
|
std::unique_lock aGuard(HashLock);
|
|
|
|
pEntry = HashTable[HASHID(hThread)];
|
|
while (pEntry != nullptr)
|
|
{
|
|
if (pthread_equal(pEntry->Handle, hThread))
|
|
break;
|
|
|
|
pRemove = pEntry;
|
|
pEntry = pEntry->Next;
|
|
}
|
|
|
|
if (pEntry != nullptr)
|
|
{
|
|
if (pRemove)
|
|
pRemove->Next = pEntry->Next;
|
|
else
|
|
HashTable[HASHID(hThread)] = pEntry->Next;
|
|
|
|
free(pEntry);
|
|
}
|
|
}
|
|
|
|
oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
|
|
{
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
oslThreadIdentifier Ident;
|
|
|
|
if (pImpl)
|
|
Ident = pImpl->m_Ident;
|
|
else
|
|
{
|
|
/* current thread */
|
|
pthread_t current = pthread_self();
|
|
|
|
Ident = lookupThreadId (current);
|
|
if (Ident == 0)
|
|
/* @@@ see TODO: alien pthread_self() @@@ */
|
|
Ident = insertThreadId (current);
|
|
}
|
|
|
|
return Ident;
|
|
}
|
|
|
|
#ifndef NO_PTHREAD_PRIORITY
|
|
/*****************************************************************************
|
|
@@@ see TODO @@@
|
|
osl_thread_priority_init_Impl
|
|
|
|
set the base-priority of the main-thread to
|
|
oslThreadPriorityNormal (64) since 0 (lowest) is
|
|
the system default. This behaviour collides with
|
|
our enum-priority definition (highest..normal..lowest).
|
|
A normaluser will expect the main-thread of an app.
|
|
to have the "normal" priority.
|
|
|
|
*****************************************************************************/
|
|
static void osl_thread_priority_init_Impl()
|
|
{
|
|
struct sched_param param;
|
|
int policy=0;
|
|
int nRet=0;
|
|
|
|
/* @@@ see TODO: calling thread may not be main thread @@@ */
|
|
|
|
if ((nRet = pthread_getschedparam(pthread_self(), &policy, ¶m)) != 0)
|
|
{
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"pthread_getschedparam failed: " << UnixErrnoString(nRet));
|
|
return;
|
|
}
|
|
|
|
#if defined (__sun)
|
|
if ( policy >= _SCHED_NEXT)
|
|
{
|
|
/* mfe: pthread_getschedparam on Solaris has a possible Bug */
|
|
/* one gets 959917873 as the policy */
|
|
/* so set the policy to a default one */
|
|
policy=SCHED_OTHER;
|
|
}
|
|
#endif /* __sun */
|
|
|
|
if ((nRet = sched_get_priority_min(policy) ) != -1)
|
|
{
|
|
SAL_INFO(
|
|
"sal.osl", "Min Prioriy for policy " << policy << " == " << nRet);
|
|
g_thread.m_priority.m_Lowest=nRet;
|
|
}
|
|
else
|
|
{
|
|
int e = errno;
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"sched_get_priority_min failed: " << UnixErrnoString(e));
|
|
}
|
|
|
|
if ((nRet = sched_get_priority_max(policy) ) != -1)
|
|
{
|
|
SAL_INFO(
|
|
"sal.osl", "Max Prioriy for policy " << policy << " == " << nRet);
|
|
g_thread.m_priority.m_Highest=nRet;
|
|
}
|
|
else
|
|
{
|
|
int e = errno;
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"sched_get_priority_max failed: " << UnixErrnoString(e));
|
|
}
|
|
|
|
g_thread.m_priority.m_Normal =
|
|
(g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Highest) / 2;
|
|
g_thread.m_priority.m_Below_Normal =
|
|
(g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Normal) / 2;
|
|
g_thread.m_priority.m_Above_Normal =
|
|
(g_thread.m_priority.m_Normal + g_thread.m_priority.m_Highest) / 2;
|
|
|
|
/* @@@ set prio of calling (not main) thread (?) @@@ */
|
|
|
|
param.sched_priority= g_thread.m_priority.m_Normal;
|
|
|
|
if ((nRet = pthread_setschedparam(pthread_self(), policy, ¶m)) != 0)
|
|
{
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"pthread_setschedparam failed: " << UnixErrnoString(nRet));
|
|
SAL_INFO(
|
|
"sal.osl",
|
|
"Thread ID " << pthread_self() << ", Policy " << policy
|
|
<< ", Priority " << param.sched_priority);
|
|
}
|
|
|
|
}
|
|
#endif /* NO_PTHREAD_PRIORITY */
|
|
|
|
/**
|
|
Impl-Notes: contrary to solaris-docu, which claims
|
|
valid priority-levels from 0 .. INT_MAX, only the
|
|
range 0..127 is accepted. (0 lowest, 127 highest)
|
|
*/
|
|
void SAL_CALL osl_setThreadPriority (
|
|
oslThread Thread,
|
|
oslThreadPriority Priority)
|
|
{
|
|
#ifndef NO_PTHREAD_PRIORITY
|
|
|
|
struct sched_param Param;
|
|
int policy;
|
|
int nRet;
|
|
|
|
#endif /* NO_PTHREAD_PRIORITY */
|
|
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_setThreadPriority(nullptr, ...) call");
|
|
return; /* EINVAL */
|
|
}
|
|
|
|
#ifdef NO_PTHREAD_PRIORITY
|
|
(void) Priority; /* unused */
|
|
#else /* NO_PTHREAD_PRIORITY */
|
|
|
|
if (pthread_getschedparam(pImpl->m_hThread, &policy, &Param) != 0)
|
|
return; /* ESRCH */
|
|
|
|
#if defined (__sun)
|
|
if ( policy >= _SCHED_NEXT)
|
|
{
|
|
/* mfe: pthread_getschedparam on Solaris has a possible Bug */
|
|
/* one gets 959917873 as the policy */
|
|
/* so set the policy to a default one */
|
|
policy=SCHED_OTHER;
|
|
}
|
|
#endif /* __sun */
|
|
|
|
pthread_once (&(g_thread.m_once), osl_thread_priority_init_Impl);
|
|
|
|
switch(Priority)
|
|
{
|
|
case osl_Thread_PriorityHighest:
|
|
Param.sched_priority= g_thread.m_priority.m_Highest;
|
|
break;
|
|
|
|
case osl_Thread_PriorityAboveNormal:
|
|
Param.sched_priority= g_thread.m_priority.m_Above_Normal;
|
|
break;
|
|
|
|
case osl_Thread_PriorityNormal:
|
|
Param.sched_priority= g_thread.m_priority.m_Normal;
|
|
break;
|
|
|
|
case osl_Thread_PriorityBelowNormal:
|
|
Param.sched_priority= g_thread.m_priority.m_Below_Normal;
|
|
break;
|
|
|
|
case osl_Thread_PriorityLowest:
|
|
Param.sched_priority= g_thread.m_priority.m_Lowest;
|
|
break;
|
|
|
|
case osl_Thread_PriorityUnknown:
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"invalid osl_setThreadPriority(..., osl_Thread_PriorityUnknown)"
|
|
" call");
|
|
return;
|
|
|
|
default:
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"invalid osl_setThreadPriority(..., " << Priority << ") call");
|
|
return;
|
|
}
|
|
|
|
if ((nRet = pthread_setschedparam(pImpl->m_hThread, policy, &Param)) != 0)
|
|
{
|
|
SAL_WARN(
|
|
"sal.osl",
|
|
"pthread_setschedparam failed: " << UnixErrnoString(nRet));
|
|
}
|
|
|
|
#endif /* NO_PTHREAD_PRIORITY */
|
|
}
|
|
|
|
oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
|
|
{
|
|
#ifndef NO_PTHREAD_PRIORITY
|
|
|
|
struct sched_param Param;
|
|
int Policy;
|
|
|
|
#endif /* NO_PTHREAD_PRIORITY */
|
|
|
|
oslThreadPriority Priority = osl_Thread_PriorityNormal;
|
|
Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
|
|
|
|
if (!pImpl)
|
|
{
|
|
SAL_WARN("sal.osl", "invalid osl_getThreadPriority(nullptr) call");
|
|
return osl_Thread_PriorityUnknown; /* EINVAL */
|
|
}
|
|
|
|
#ifndef NO_PTHREAD_PRIORITY
|
|
|
|
if (pthread_getschedparam(pImpl->m_hThread, &Policy, &Param) != 0)
|
|
return osl_Thread_PriorityUnknown; /* ESRCH */
|
|
|
|
pthread_once (&(g_thread.m_once), osl_thread_priority_init_Impl);
|
|
|
|
/* map pthread priority to enum */
|
|
if (Param.sched_priority==g_thread.m_priority.m_Highest)
|
|
{
|
|
/* 127 - highest */
|
|
Priority= osl_Thread_PriorityHighest;
|
|
}
|
|
else if (Param.sched_priority > g_thread.m_priority.m_Normal)
|
|
{
|
|
/* 65..126 - above normal */
|
|
Priority= osl_Thread_PriorityAboveNormal;
|
|
}
|
|
else if (Param.sched_priority == g_thread.m_priority.m_Normal)
|
|
{
|
|
/* normal */
|
|
Priority= osl_Thread_PriorityNormal;
|
|
}
|
|
else if (Param.sched_priority > g_thread.m_priority.m_Lowest)
|
|
{
|
|
/* 63..1 -below normal */
|
|
Priority= osl_Thread_PriorityBelowNormal;
|
|
}
|
|
else if (Param.sched_priority == g_thread.m_priority.m_Lowest)
|
|
{
|
|
/* 0 - lowest */
|
|
Priority= osl_Thread_PriorityLowest;
|
|
}
|
|
else
|
|
{
|
|
/* unknown */
|
|
Priority= osl_Thread_PriorityUnknown;
|
|
}
|
|
|
|
#endif /* NO_PTHREAD_PRIORITY */
|
|
|
|
return Priority;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct wrapper_pthread_key
|
|
{
|
|
pthread_key_t m_key;
|
|
oslThreadKeyCallbackFunction pfnCallback;
|
|
};
|
|
|
|
}
|
|
|
|
oslThreadKey SAL_CALL osl_createThreadKey( oslThreadKeyCallbackFunction pCallback )
|
|
{
|
|
wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(malloc(sizeof(wrapper_pthread_key)));
|
|
|
|
if (pKey)
|
|
{
|
|
pKey->pfnCallback = pCallback;
|
|
|
|
if (pthread_key_create(&(pKey->m_key), pKey->pfnCallback) != 0)
|
|
{
|
|
free(pKey);
|
|
pKey = nullptr;
|
|
}
|
|
}
|
|
|
|
return static_cast<oslThreadKey>(pKey);
|
|
}
|
|
|
|
void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
|
|
{
|
|
wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
|
|
if (pKey)
|
|
{
|
|
pthread_key_delete(pKey->m_key);
|
|
free(pKey);
|
|
}
|
|
}
|
|
|
|
void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
|
|
{
|
|
wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
|
|
return pKey ? pthread_getspecific(pKey->m_key) : nullptr;
|
|
}
|
|
|
|
sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
|
|
{
|
|
bool bRet;
|
|
void *pOldData = nullptr;
|
|
wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
|
|
if (!pKey)
|
|
return false;
|
|
|
|
if (pKey->pfnCallback)
|
|
pOldData = pthread_getspecific(pKey->m_key);
|
|
|
|
bRet = (pthread_setspecific(pKey->m_key, pData) == 0);
|
|
|
|
if (bRet && pKey->pfnCallback && pOldData)
|
|
pKey->pfnCallback(pOldData);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
rtl_TextEncoding getThreadTextEncodingForInitialization()
|
|
{
|
|
/* determine default text encoding */
|
|
rtl_TextEncoding defaultEncoding = osl_getTextEncodingFromLocale(nullptr);
|
|
// Tools string functions call abort() on an unknown encoding so ASCII is a
|
|
// meaningful fallback:
|
|
if ( RTL_TEXTENCODING_DONTKNOW == defaultEncoding )
|
|
{
|
|
SAL_WARN("sal.osl", "RTL_TEXTENCODING_DONTKNOW -> _ASCII_US");
|
|
defaultEncoding = RTL_TEXTENCODING_ASCII_US;
|
|
}
|
|
|
|
return defaultEncoding;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|