    /* -*- 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 <osl/diagnose.h>
    #include <osl/thread.h>
    #include <rtl/alloc.h>
    #include <osl/time.h>
    #include <osl/interlck.h>
    #include <rtl/tencinfo.h>
    #include <errno.h>

    /**
        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 unsigned __stdcall oslWorkerWrapperFunction(void* pData);
    static oslThread oslCreateThread(oslWorkerFunction pWorker, void* pThreadData, sal_uInt32 nFlags);

    static unsigned __stdcall oslWorkerWrapperFunction(void* pData)
    {
        osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(pData);

        /* Initialize COM - Multi Threaded Apartment (MTA) for all threads */
        CoInitializeEx(nullptr, COINIT_MULTITHREADED); /* spawned by oslCreateThread */

        /* call worker-function with data */

        pThreadImpl->m_WorkerFunction(pThreadImpl->m_pData);

        CoUninitialize();

        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)
        {
            switch (errno)
            {
                case EAGAIN:
                    fprintf(stderr, "_beginthreadex errno EAGAIN\n");
                break;
                case EINVAL:
                    fprintf(stderr, "_beginthreadex errno EINVAL\n");
                break;
                case EACCES:
                    fprintf(stderr, "_beginthreadex errno EACCES\n");
                break;
                case ENOMEM:
                    fprintf(stderr, "_beginthreadex undocumented errno ENOMEM - this means not enough VM for stack\n");
                break;
                default:
                    fprintf(stderr, "_beginthreadex unexpected errno %d\n", errno);
                break;
            }

            /* 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);

        OSL_ASSERT(pThreadImpl);        /* valid ptr? */

        ResumeThread(pThreadImpl->m_hThread);
    }

    void SAL_CALL osl_suspendThread(oslThread Thread)
    {
        osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);

        OSL_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);

        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);
    }

    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);
    }

    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),
                reinterpret_cast<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 = nullptr;
    CRITICAL_SECTION    g_ThreadKeyListCS;

    static void AddKeyToList( PTLS pTls )
    {
        if ( pTls )
        {
            EnterCriticalSection( &g_ThreadKeyListCS );

            pTls->pNext = g_pThreadKeyList;
            pTls->pPrev = nullptr;

            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 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 );
    }

    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 != FALSE;
        }

        return false;
    }

    DWORD   g_dwTLSTextEncodingIndex = DWORD(-1);

    rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding(void)
    {
        DWORD_PTR           dwEncoding;
        rtl_TextEncoding    _encoding;
        BOOL                gotACP;

        if ( DWORD(-1) == g_dwTLSTextEncodingIndex )
            g_dwTLSTextEncodingIndex = TlsAlloc();

        dwEncoding = reinterpret_cast<DWORD_PTR>(TlsGetValue( g_dwTLSTextEncodingIndex ));
        _encoding = LOWORD(dwEncoding);
        gotACP = HIWORD(dwEncoding);

        if ( !gotACP )
        {
            _encoding = rtl_getTextEncodingFromWindowsCodePage( GetACP() );
            TlsSetValue( g_dwTLSTextEncodingIndex, reinterpret_cast<LPVOID>(static_cast<DWORD_PTR>(MAKELONG( _encoding, TRUE ))) );
        }

        return _encoding;
    }

    rtl_TextEncoding SAL_CALL osl_setThreadTextEncoding( rtl_TextEncoding Encoding )
    {
        rtl_TextEncoding oldEncoding = osl_getThreadTextEncoding();

        TlsSetValue( g_dwTLSTextEncodingIndex, reinterpret_cast<LPVOID>(static_cast<DWORD_PTR>(MAKELONG( Encoding, TRUE))) );

        return oldEncoding;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V720 It is advised to utilize the 'SuspendThread' function only when developing a debugger (see documentation for details).