/* -*- 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, &param)) != 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, &param)) != 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: */

V522 There might be dereferencing of a potential null pointer 'pEntry'. Check lines: 629, 627.