/**************************************************************
 * 
 * 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 <string.h>
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <osl/nlsupport.h>
#ifndef _RTL_TEXTENC_H_
#include <rtl/textenc.h>
#endif

#if defined LINUX
#include <sys/prctl.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 'PTHREAD_VALUE(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.
 *
 ****************************************************************************/

/*****************************************************************************/
/*	Internal data structures and functions */
/*****************************************************************************/

#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

typedef struct osl_thread_impl_st
{
	pthread_t			m_hThread;
	sal_uInt16			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;

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 }
static void osl_thread_priority_init_Impl (void);

struct osl_thread_textencoding_st
{
	pthread_key_t    m_key;     /* key to store thread local text encoding */
	rtl_TextEncoding m_default; /* the default text encoding */
};

#define OSL_THREAD_TEXTENCODING_INITIALIZER { 0, RTL_TEXTENCODING_DONTKNOW }
static void osl_thread_textencoding_init_Impl (void);

struct osl_thread_global_st
{
	pthread_once_t                    m_once;
	struct osl_thread_priority_st     m_priority;
	struct osl_thread_textencoding_st m_textencoding;
};

static struct osl_thread_global_st g_thread =
{
	PTHREAD_ONCE_INIT,
	OSL_THREAD_PRIORITY_INITIALIZER,
	OSL_THREAD_TEXTENCODING_INITIALIZER
};

static void osl_thread_init_Impl (void);

static Thread_Impl* osl_thread_construct_Impl (void);
static void         osl_thread_destruct_Impl (Thread_Impl ** ppImpl);

static void* osl_thread_start_Impl (void * pData);
static void  osl_thread_cleanup_Impl (void * pData);

static oslThread osl_thread_create_Impl (
	oslWorkerFunction pWorker, void * pThreadData, short nFlags);

static void osl_thread_join_cleanup_Impl (void * opaque);
static void osl_thread_wait_cleanup_Impl (void * opaque);

/* @@@ see TODO @@@ */
static sal_uInt16 insertThreadId (pthread_t hThread);
static sal_uInt16 lookupThreadId (pthread_t hThread);
static void       removeThreadId (pthread_t hThread);

/*****************************************************************************/
/* osl_thread_init_Impl */
/*****************************************************************************/
static void osl_thread_init_Impl (void)
{
	osl_thread_priority_init_Impl();
	osl_thread_textencoding_init_Impl();
}

/*****************************************************************************/
/* osl_thread_join_cleanup_Impl */
/*****************************************************************************/
static void osl_thread_join_cleanup_Impl (void * opaque)
{
	pthread_t hThread = (pthread_t)(opaque);
	pthread_detach (hThread);
}

/*****************************************************************************/
/* osl_thread_wait_cleanup_Impl */
/*****************************************************************************/
static void osl_thread_wait_cleanup_Impl (void * opaque)
{
	pthread_mutex_t * pMutex = (pthread_mutex_t*)(opaque);
	pthread_mutex_unlock (pMutex);
}

/*****************************************************************************/
/* osl_thread_construct_Impl */
/*****************************************************************************/
Thread_Impl* osl_thread_construct_Impl (void)
{
	Thread_Impl* pImpl = malloc (sizeof(Thread_Impl));
	if (pImpl)
	{
		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);
}

/*****************************************************************************/
/* osl_thread_destruct_Impl */
/*****************************************************************************/
static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl)
{
	OSL_ASSERT(ppImpl);
	if (*ppImpl)
	{
		pthread_cond_destroy  (&((*ppImpl)->m_Cond));
		pthread_mutex_destroy (&((*ppImpl)->m_Lock));

		free (*ppImpl);
		(*ppImpl) = 0;
	}
}

/*****************************************************************************/
/* osl_thread_cleanup_Impl */
/*****************************************************************************/
static void osl_thread_cleanup_Impl (void* pData)
{
    pthread_t thread;
    int attached;
    int destroyed;
	Thread_Impl* pImpl= (Thread_Impl*)pData;

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

/*****************************************************************************/
/* osl_thread_start_Impl */
/*****************************************************************************/
static void* osl_thread_start_Impl (void* pData)
{
	int terminate;
	Thread_Impl* pImpl= (Thread_Impl*)pData;

	OSL_ASSERT(pImpl);

    pthread_mutex_lock (&(pImpl->m_Lock));

	/* install cleanup handler */
    pthread_cleanup_push (osl_thread_cleanup_Impl, pData);

	/* 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_cleanup_push (osl_thread_wait_cleanup_Impl, &(pImpl->m_Lock));
		pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
		pthread_cleanup_pop (0);
    }

	/* check for SUSPENDED to TERMINATE state change */
	terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);

    pthread_mutex_unlock (&(pImpl->m_Lock));

	if (!terminate)
	{
		/* call worker function */
		pImpl->m_WorkerFunction(pImpl->m_pData);
	}

	/* call cleanup handler and leave */
	pthread_cleanup_pop (1);
	return (0);
}

/*****************************************************************************/
/* osl_thread_create_Impl */
/*****************************************************************************/
static oslThread osl_thread_create_Impl (
	oslWorkerFunction pWorker,
	void*             pThreadData,
	short             nFlags)
{
	Thread_Impl* pImpl;
    int nRet=0;

	pImpl = osl_thread_construct_Impl();
    if (!pImpl)
        return (0); /* ENOMEM */

	pImpl->m_WorkerFunction = pWorker;
	pImpl->m_pData = pThreadData;
	pImpl->m_Flags = nFlags | THREADIMPL_FLAGS_STARTUP;

    pthread_mutex_lock (&(pImpl->m_Lock));

	if ((nRet = pthread_create (
		&(pImpl->m_hThread),
		PTHREAD_ATTR_DEFAULT,
		osl_thread_start_Impl,
		(void*)(pImpl))) != 0)
	{
	    OSL_TRACE("osl_thread_create_Impl(): errno: %d, %s\n",
			      nRet, strerror(nRet));

		pthread_mutex_unlock (&(pImpl->m_Lock));
		osl_thread_destruct_Impl (&pImpl);

		return (0);
	}

	/* wait for change from STARTUP to ACTIVE state */
	while (pImpl->m_Flags & THREADIMPL_FLAGS_STARTUP)
	{
		/* wait until STARTUP flag is cleared */
		pthread_cleanup_push (osl_thread_wait_cleanup_Impl, &(pImpl->m_Lock));
        pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
		pthread_cleanup_pop (0);
    }

    pthread_mutex_unlock (&(pImpl->m_Lock));

	return ((oslThread)(pImpl));
}

/*****************************************************************************/
/* osl_createThread */
/*****************************************************************************/
oslThread osl_createThread (
	oslWorkerFunction pWorker,
	void *            pThreadData)
{
    return osl_thread_create_Impl (
		pWorker,
		pThreadData,
		THREADIMPL_FLAGS_ATTACHED);
}

/*****************************************************************************/
/* osl_createSuspendedThread */
/*****************************************************************************/
oslThread osl_createSuspendedThread (
	oslWorkerFunction pWorker,
	void *            pThreadData)
{
    return osl_thread_create_Impl (
		pWorker,
		pThreadData,
		THREADIMPL_FLAGS_ATTACHED |
		THREADIMPL_FLAGS_SUSPENDED );
}

/*****************************************************************************/
/* osl_destroyThread */
/*****************************************************************************/
void SAL_CALL osl_destroyThread(oslThread Thread)
{
    if (Thread != NULL) {
        Thread_Impl * impl = (Thread_Impl *) Thread;
        int 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);
        }
    }
}

/*****************************************************************************/
/* osl_resumeThread */
/*****************************************************************************/
void SAL_CALL osl_resumeThread(oslThread Thread)
{
	Thread_Impl* pImpl= (Thread_Impl*)Thread;

	OSL_ASSERT(pImpl);
	if (!pImpl)
		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));
}

/*****************************************************************************/
/* osl_suspendThread */
/*****************************************************************************/
void SAL_CALL osl_suspendThread(oslThread Thread)
{
    Thread_Impl* pImpl= (Thread_Impl*)Thread;

	OSL_ASSERT(pImpl);
	if (!pImpl)
		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_cleanup_push (osl_thread_wait_cleanup_Impl, &(pImpl->m_Lock));
			pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
			pthread_cleanup_pop (0);
        }
    }

	pthread_mutex_unlock (&(pImpl->m_Lock));
}

/*****************************************************************************/
/* osl_isThreadRunning */
/*****************************************************************************/
sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
{
	sal_Bool active;
	Thread_Impl* pImpl= (Thread_Impl*)Thread;

	if (!pImpl)
		return sal_False;

	pthread_mutex_lock (&(pImpl->m_Lock));
	active = ((pImpl->m_Flags & THREADIMPL_FLAGS_ACTIVE) > 0);
	pthread_mutex_unlock (&(pImpl->m_Lock));

	return (active);
}

/*****************************************************************************/
/* osl_joinWithThread */
/*****************************************************************************/
void SAL_CALL osl_joinWithThread(oslThread Thread)
{
    pthread_t thread;
	int attached;
	Thread_Impl* pImpl= (Thread_Impl*)Thread;

	if (!pImpl)
		return;

	pthread_mutex_lock (&(pImpl->m_Lock));

	if (pthread_equal (pthread_self(), pImpl->m_hThread))
	{
		/* self join */
		pthread_mutex_unlock (&(pImpl->m_Lock));
		return; /* EDEADLK */
	}

    thread = pImpl->m_hThread;
	attached = ((pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) > 0);
	pImpl->m_Flags &= ~THREADIMPL_FLAGS_ATTACHED;

	pthread_mutex_unlock (&(pImpl->m_Lock));

	if (attached)
	{
		/* install cleanup handler to ensure consistent flags and state */
		pthread_cleanup_push (
			osl_thread_join_cleanup_Impl, (void*)thread);

		/* join */
		pthread_join (thread, NULL);

		/* remove cleanup handler */
		pthread_cleanup_pop (0);
	}
}

/*****************************************************************************/
/* osl_terminateThread */
/*****************************************************************************/
void SAL_CALL osl_terminateThread(oslThread Thread)
{
	Thread_Impl* pImpl= (Thread_Impl*)Thread;

	OSL_ASSERT(pImpl);
	if (!pImpl)
		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));
}

/*****************************************************************************/
/* osl_scheduleThread */
/*****************************************************************************/
sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
{
	int terminate;
	Thread_Impl* pImpl= (Thread_Impl*)Thread;

	OSL_ASSERT(pImpl);
	if (!pImpl)
		return sal_False; /* EINVAL */

	OSL_ASSERT(pthread_equal (pthread_self(), pImpl->m_hThread));
	if (!(pthread_equal (pthread_self(), pImpl->m_hThread)))
		return sal_False; /* EINVAL */

	pthread_testcancel();
	pthread_mutex_lock (&(pImpl->m_Lock));

	while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
    {
		/* wait until SUSPENDED flag is cleared */
		pthread_cleanup_push (osl_thread_wait_cleanup_Impl, &(pImpl->m_Lock));
		pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
		pthread_cleanup_pop (0);
    }

	terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);

	pthread_mutex_unlock(&(pImpl->m_Lock));
	pthread_testcancel();

	return (terminate == 0);
}

/*****************************************************************************/
/* osl_waitThread */
/*****************************************************************************/
void SAL_CALL osl_waitThread(const TimeValue* pDelay)
{
	if (pDelay)
	{
		struct timespec delay;

		SET_TIMESPEC(delay, pDelay->Seconds, pDelay->Nanosec);

		SLEEP_TIMESPEC(delay);
	}
}

/*****************************************************************************/
/* osl_yieldThread */
/*
	Note that POSIX scheduling _really_ requires threads to call this
	functions, 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) {
#if defined LINUX
    if (prctl(PR_SET_NAME, (unsigned long) name, 0, 0, 0) != 0) {
        OSL_TRACE(
            "%s prctl(PR_SET_NAME) failed with errno %d", OSL_LOG_PREFIX,
            errno);
    }
#else
    (void) name;
#endif
}

/*****************************************************************************/
/* osl_getThreadIdentifier @@@ see TODO @@@ */
/*****************************************************************************/

#define HASHID(x) ((unsigned int)PTHREAD_VALUE(x) % HashSize)

typedef struct _HashEntry
{
	pthread_t         Handle;
	sal_uInt16	      Ident;
	struct _HashEntry *Next;
} HashEntry;

static HashEntry* HashTable[31];
static int HashSize = sizeof(HashTable) / sizeof(HashTable[0]);

static pthread_mutex_t HashLock = PTHREAD_MUTEX_INITIALIZER;

static sal_uInt16 LastIdent = 0;

static sal_uInt16 lookupThreadId (pthread_t hThread)
{
	HashEntry *pEntry;

	pthread_mutex_lock(&HashLock);

		pEntry = HashTable[HASHID(hThread)];
		while (pEntry != NULL)
		{
			if (pthread_equal(pEntry->Handle, hThread))
			{
				pthread_mutex_unlock(&HashLock);
				return (pEntry->Ident);
			}
			pEntry = pEntry->Next;
		}

	pthread_mutex_unlock(&HashLock);

	return (0);
}

static sal_uInt16 insertThreadId (pthread_t hThread)
{
	HashEntry *pEntry, *pInsert = NULL;

	pthread_mutex_lock(&HashLock);

	pEntry = HashTable[HASHID(hThread)];

	while (pEntry != NULL)
	{
		if (pthread_equal(pEntry->Handle, hThread))
			break;

		pInsert = pEntry;
		pEntry = pEntry->Next;
	}

	if (pEntry == NULL)
	{
		pEntry = (HashEntry*) calloc(sizeof(HashEntry), 1);

		pEntry->Handle = hThread;

		++ LastIdent;

        if ( LastIdent == 0 )
            LastIdent = 1;

        pEntry->Ident  = LastIdent;

		if (pInsert)
			pInsert->Next = pEntry;
		else
			HashTable[HASHID(hThread)] = pEntry;
	}

	pthread_mutex_unlock(&HashLock);

	return (pEntry->Ident);
}

static void removeThreadId (pthread_t hThread)
{
	HashEntry *pEntry, *pRemove = NULL;

	pthread_mutex_lock(&HashLock);

	pEntry = HashTable[HASHID(hThread)];
	while (pEntry != NULL)
	{
		if (pthread_equal(pEntry->Handle, hThread))
			break;

		pRemove = pEntry;
		pEntry = pEntry->Next;
	}

	if (pEntry != NULL)
	{
		if (pRemove)
			pRemove->Next = pEntry->Next;
		else
			HashTable[HASHID(hThread)] = pEntry->Next;

		free(pEntry);
	}

	pthread_mutex_unlock(&HashLock);
}

oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
{
	Thread_Impl* pImpl= (Thread_Impl*)Thread;
	sal_uInt16   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 ((oslThreadIdentifier)(Ident));
}

/*****************************************************************************
    @@@ 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 (void)
{
#ifndef NO_PTHREAD_PRIORITY
    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)
    {
        OSL_TRACE("failed to get priority of thread [%s]\n",strerror(nRet));
        return;
    }

#if defined (SOLARIS)
    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 /* SOLARIS */

    if ((nRet = sched_get_priority_min(policy) ) != -1)
    {
        OSL_TRACE("Min Prioriy for policy '%i' == '%i'\n",policy,nRet);
        g_thread.m_priority.m_Lowest=nRet;
    }
#if OSL_DEBUG_LEVEL > 1
    else
    {
        fprintf(stderr,"failed to get min sched param [%s]\n",strerror(errno));
    }
#endif /* OSL_DEBUG_LEVEL */

    if ((nRet = sched_get_priority_max(policy) ) != -1)
    {
        OSL_TRACE("Max Prioriy for policy '%i' == '%i'\n",policy,nRet);
        g_thread.m_priority.m_Highest=nRet;
    }
#if OSL_DEBUG_LEVEL > 1
    else
    {
        fprintf(stderr,"failed to get max sched param [%s]\n",strerror(errno));
    }
#endif /* OSL_DEBUG_LEVEL */

    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)
    {
        OSL_TRACE("failed to change base priority of thread [%s]\n",strerror(nRet));
        OSL_TRACE("Thread ID '%i', Policy '%i', Priority '%i'\n",pthread_self(),policy,param.sched_priority);
    }

#endif /* NO_PTHREAD_PRIORITY */
}

/*****************************************************************************/
/* osl_setThreadPriority */
/*
	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= (Thread_Impl*)Thread;

	OSL_ASSERT(pImpl);
	if (!pImpl)
		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 (SOLARIS)
    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 /* SOLARIS */

	pthread_once (&(g_thread.m_once), osl_thread_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:
            OSL_ASSERT(sal_False);		/* only fools try this...*/

            /* let release-version behave friendly */
            return;

        default:
            /* enum expanded, but forgotten here...*/
            OSL_ENSURE(sal_False,"osl_setThreadPriority : unknown priority\n");

            /* let release-version behave friendly */
            return;
    }

    if ((nRet = pthread_setschedparam(pImpl->m_hThread, policy, &Param)) != 0)
    {
        OSL_TRACE("failed to change thread priority [%s]\n",strerror(nRet));
    }

#endif /* NO_PTHREAD_PRIORITY */
}

/*****************************************************************************/
/* osl_getThreadPriority */
/*****************************************************************************/
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= (Thread_Impl*)Thread;

	OSL_ASSERT(pImpl);
	if (!pImpl)
		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_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;
}

/*****************************************************************************/
/* osl_createThreadKey */
/*****************************************************************************/
oslThreadKey SAL_CALL osl_createThreadKey( oslThreadKeyCallbackFunction pCallback )
{
	pthread_key_t key;

	if (pthread_key_create(&key, pCallback) != 0)
		key = 0;

	return ((oslThreadKey)key);
}

/*****************************************************************************/
/* osl_destroyThreadKey */
/*****************************************************************************/
void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
{
	pthread_key_delete((pthread_key_t)Key);
}

/*****************************************************************************/
/* osl_getThreadKeyData */
/*****************************************************************************/
void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
{
	return (pthread_getspecific((pthread_key_t)Key));
}

/*****************************************************************************/
/* osl_setThreadKeyData */
/*****************************************************************************/
sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
{
	return (pthread_setspecific((pthread_key_t)Key, pData) == 0);
}

/*****************************************************************************/
/* Thread Local Text Encoding */
/*****************************************************************************/
static void osl_thread_textencoding_init_Impl (void)
{
	rtl_TextEncoding defaultEncoding;
	const char *     pszEncoding;

	/* create thread specific data key */
	pthread_key_create (&(g_thread.m_textencoding.m_key), NULL);

	/* determine default text encoding */
	pszEncoding = getenv ("SOLAR_USER_RTL_TEXTENCODING");
	if (pszEncoding)
		defaultEncoding = atoi(pszEncoding);
	else
		defaultEncoding = osl_getTextEncodingFromLocale(NULL);

	OSL_ASSERT(defaultEncoding != RTL_TEXTENCODING_DONTKNOW);

	/*
	Tools string functions call abort() on an unknown encoding so ASCII
	is a meaningfull fallback regardless wether the assertion makes sense.
	*/

	if ( RTL_TEXTENCODING_DONTKNOW == defaultEncoding )
		defaultEncoding = RTL_TEXTENCODING_ASCII_US;

	g_thread.m_textencoding.m_default = defaultEncoding;
}

/*****************************************************************************/
/* osl_getThreadTextEncoding */
/*****************************************************************************/
rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding()
{
    rtl_TextEncoding threadEncoding;

	pthread_once (&(g_thread.m_once), osl_thread_init_Impl);

    /* check for thread specific encoding, use default if not set */
	threadEncoding = SAL_INT_CAST(
        rtl_TextEncoding,
        (sal_uIntPtr) pthread_getspecific(g_thread.m_textencoding.m_key));
	if (0 == threadEncoding)
		threadEncoding = g_thread.m_textencoding.m_default;

    return threadEncoding;
}

/*****************************************************************************/
/* osl_setThreadTextEncoding */
/*****************************************************************************/
rtl_TextEncoding osl_setThreadTextEncoding(rtl_TextEncoding Encoding)
{
    rtl_TextEncoding oldThreadEncoding = osl_getThreadTextEncoding();

    /* save encoding in thread local storage */
    pthread_setspecific (
        g_thread.m_textencoding.m_key,
        (void*) SAL_INT_CAST(sal_uIntPtr, Encoding));

    return oldThreadEncoding;
}
