
/*
 *@@sourcefile threads.c:
 *      contains helper functions for creating, destroying, and
 *      synchronizing threads.
 *
 *      Function prefixes (new with V0.81):
 *      --  thr*        Thread helper functions
 *
 *      This file is new with V0.81 and contains all the thread
 *      functions that used to be in helpers.c.
 *
 *@@include #define INCL_DOSPROCESS
 *@@include #include <os2.h>
 *@@include #include "threads.h"
 */

/*
 *      Copyright (C) 1997-99 Ulrich M”ller.
 *      This file is part of the XFolder source package.
 *      XFolder is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XFolder main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

#define INCL_DOSPROCESS
#define INCL_DOSERRORS
#include <os2.h>

#include <stdlib.h>

#include "threads.h"

/*
 *@@ thrCreate:
 *      this function creates a THREADINFO structure in *ppti;
 *      you must pass the thread function in pfn, which will
 *      then be executed. The thread will be passed a pointer
 *      to the new THREADINFO structure as its thread parameter.
 *      The ulData field in that structure is set to ulData
 *      here. Use whatever you like.
 *      Note: As opposed to previous versions, V0.84 now expects
 *      *pfn to have _Optlink calling convention, because now
 *      _beginthread() is used instead of DosCreateThread().
 */

BOOL thrCreate(PTHREADINFO *ppti,   // out: where to create THREADINFO
               PTHREADFUNC pfn,     // in: _Optlink thread function
               ULONG ulData)        // in: user data to be stored in THREADINFO
{
    BOOL            rc = FALSE;
    PTHREADINFO     pti = NULL;

    if (ppti)
    {
        if (*ppti == NULL)
            pti = (PTHREADINFO)malloc(sizeof(THREADINFO));
        else
            if ((*ppti)->tid == NULLHANDLE)
                pti = *ppti;

        if (pti) {
            // we arrive here if *ppti was NULL or (*ppti->tid == NULLHANDLE),
            // i.e. the thread is not already running. We use
            // DosCreateThread and not _beginthread because we
            // do not use the compiler's default exception handlers,
            // but our own ones for each thread we create, so there.
            // _beginthread is contained both in the VAC++ and EMX
            // C libraries with this syntax.
            pti->tid = _beginthread(
                            pfn,
                            0,      // unused compatibility param
                            65536,  // stack size
                            pti);    // parameter passed to thread
            pti->cb = sizeof(THREADINFO);
            pti->ulData = ulData;
            pti->fExit = FALSE;

            pti->hab = NULLHANDLE;
            pti->fExitComplete = FALSE;
            pti->ulResult = 0;
            pti->ulFuncInfo = 0;
            rc = TRUE;
        }
        *ppti = pti;
    }
    return (rc);
}

/*
 *@@ thrClose:
 *      this functions sets the "fExit" flag in
 *      THREADINFO to TRUE;
 *      the thread should monitor this flag
 *      periodically and then terminate itself.
 */

BOOL thrClose(PTHREADINFO pti)
{
    if (pti) {
        pti->fExit = TRUE;
        return (TRUE);
    }
    return (FALSE);
}

/*
 *@@ thrGoodbye:
 *      every thread should call this function just before
 *      it terminates itself so that the other thread funcs
 *      can react properly. This updates the THREADINFO
 *      structure, but does _not_ call _endthread(). So you
 *      should either call this function before calling
 *      _endthread() or as the last function call in the
 *      thread function itself before it exits.
 */

VOID thrGoodbye(PTHREADINFO pti)
{
    if (pti) {
        pti->fExitComplete = TRUE;
        pti->tid = NULLHANDLE;
    }
}

/*
 *@@ thrWait:
 *      this function waits for a thread to end by calling
 *      DosWaitThread. Note that this blocks the calling
 *      thread, so only use this function when you're sure
 *      the thread will actually terminate.
 */

BOOL thrWait(PTHREADINFO pti)
{
    if (pti) {
        DosWaitThread(&(pti->tid), DCWW_WAIT);
        pti->tid = NULLHANDLE;
        return (TRUE);
    }
    return (FALSE);
}

/*
 *@@ thrFree:
 *      this is a combination of thrClose and
 *      thrWait; the THREADINFO block is then freed
 *      and *ppti set to NULL.
 */

BOOL thrFree(PTHREADINFO *ppti)
{
    if (ppti)
        if (*ppti) {
            if ((*ppti)->tid) {
                thrClose(*ppti);
                thrWait(*ppti);
            }
            free(*ppti);
            *ppti = NULL;
            return (TRUE);
        }

    return (FALSE);
}

/*
 *@@ thrKill:
 *      just like thrFree, but the thread is
 *      brutally killed, using DosKillThread.
 */

BOOL thrKill(PTHREADINFO *ppti)
{
    if (ppti)
        if (*ppti) {
            if ((*ppti)->tid) {
                DosResumeThread((*ppti)->tid);
                    // this returns an error if the thread
                    // is not suspended, but otherwise the
                    // system might hang
                DosKillThread((*ppti)->tid);
            }
            free(*ppti);
            *ppti = NULL;
            return (TRUE);
        }
    return (FALSE);
}

/*
 *@@ thrQueryID:
 *      returns thread ID.
 */

TID thrQueryID(PTHREADINFO pti)
{
    if (pti)
        if (!(pti->fExitComplete))
            return (pti->tid);

    return NULLHANDLE;
}

/*
 *@@ thrQueryPriority:
 *      returns the priority of the calling thread.
 *      The low byte of the low word is a hexadecimal value
 *      representing a rank (value 0 to 31) within a priority class.
 *      Class values, found in the high byte of the low word, are
 *      as follows:
 *          0x01  idle
 *          0x02  regular
 *          0x03  time-critical
 *          0x04  server
 */

ULONG thrQueryPriority(VOID)
{
    PTIB    ptib;
    PPIB    ppib;
    if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
        if (ptib)
            if (ptib->tib_ptib2)
                return (ptib->tib_ptib2->tib2_ulpri);
    return (0);
}


