/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */

static const char* const vthread_linux_cxx_Version =
    "$Id: vthread-linux.cxx,v 1.9.4.1 2003/01/31 23:58:11 bko Exp $";

#ifndef __vxworks

#include "global.h"
#include <cassert>
#include "VThread.hxx"
#include "cpLog.h"

/*********************************************************************
 
VThread for Linux
 
*********************************************************************/

VThread::VThread()
    : myId(VTHREAD_ID_INVALID)
{
    memset(&myAttributes, 0, sizeof(myAttributes));
}

VThread::~VThread()
{
    if ( myId != VTHREAD_ID_INVALID )
    {
        // warn user but don't kill thread
        cpLog( LOG_WARNING, "active VThread:%d going out of scope", myId );
	assert(0); // this is probably a design error
    }
    myId = VTHREAD_ID_INVALID;
    pthread_attr_destroy(&myAttributes);
}

int
VThread::spawn( void *( *startFunc )( void * ),
                void *startArgs /*Default Arguments*/,
                unsigned long flags /*Default Arguments*/,
                unsigned long priority /*Default Arguments*/,
                unsigned long stack_size /* Default Arguments*/
              ) throw (VThreadException&)
{
    if ( myId != VTHREAD_ID_INVALID )
    {
        // return without spawning another thread
        cpLog( LOG_ERR, "VThread:%d is already active", myId );
        return -1;  // type of vthread_t is not int necessarily
    }


    struct sched_param priorityParams;
    int retVal = 0;

    retVal = pthread_attr_init(&myAttributes);
    assert (retVal == 0);

    // extract scheduling preference from flags and set scheduling
    // policy accordingly
    switch ( flags & VTHREAD_SCHED_MASK )
    {
        case VTHREAD_SCHED_FIFO:
        {
            #if !defined(WIN32)
            retVal = pthread_attr_setinheritsched( &myAttributes,
						   PTHREAD_EXPLICIT_SCHED );
	    assert ( retVal == 0 );

            retVal = pthread_attr_setschedpolicy( &myAttributes,
                                                  SCHED_FIFO );
            if ( retVal )
            {
                throw VThreadExceptionInvalidAttributes(
                    "cannot set schedule as VTHREAD_SCHED_FIFO",
                    __FILE__,
                    __LINE__,
                    retVal );
            }
            #else
            cpLog(LOG_ERR, "priorities not implemented");
            assert(0);
            #endif
        }
        break;

        case VTHREAD_SCHED_RR:
        {
            #if !defined(WIN32)
            retVal = pthread_attr_setinheritsched( &myAttributes,
						   PTHREAD_EXPLICIT_SCHED );
	    assert (retVal == 0 );
            retVal = pthread_attr_setschedpolicy( &myAttributes,
                                                  SCHED_RR );
            if ( retVal )
            {
                throw VThreadExceptionInvalidAttributes(
                    "cannot set schedule as VTHREAD_SCHED_RR",
                    __FILE__,
                    __LINE__,
                    retVal );
            }
            #else
                cpLog(LOG_ERR, "priorities not implemented");
                assert(0);
            #endif
        }
        break;

        case VTHREAD_SCHED_DEFAULT:
        default:
        {
            // PTHREAD_INHERIT_SCHED is not supported by Solaris
            // and is not required for Linux so it has been removed

            // inherit scheduling policy of parent as default
            //assert( pthread_attr_setinheritsched( &myAttributes,
            //                                      PTHREAD_INHERIT_SCHED )
            //    == 0 );
        }

        break;
    }

    // if anything expect default, set scheduling priority explicitly;
    // note that by default the priority of the parent thread is inherited
    if ( priority != VTHREAD_PRIORITY_DEFAULT )
    {
        // probably should improve to use relative values
        priorityParams.sched_priority = priority;
        retVal = pthread_attr_setschedparam( &myAttributes,
                                             &priorityParams );
        if ( retVal )
        {
            throw VThreadExceptionInvalidPriority(
                "cannot set priority",
                __FILE__,
                __LINE__,
                retVal );
        }
    }
#if defined (__SUNPRO_CC)
    pthread_attr_setscope(&myAttributes, PTHREAD_SCOPE_SYSTEM);
#endif

#if defined (__FreeBSD__)
    // Default FreeBSD thread stack size (64KB) is not sufficient for the
    // b2bua bloatware
    pthread_attr_setstacksize (&myAttributes, 1024*1024);
#endif

    // spawn the thread
    return ( pthread_create( &myId, &myAttributes, startFunc, startArgs ) );
}


int
VThread::join( void **status /*Default Arguments*/)
{
    int retVal = pthread_join( myId, status );
    myId = VTHREAD_ID_INVALID;
    return retVal;
}


int
VThread::getPriority() const
{
    struct sched_param priorityParams;

    int retVal = pthread_attr_getschedparam( &myAttributes, &priorityParams );

    assert( retVal == 0 );

    return priorityParams.sched_priority;
}


int
VThread::setPriority( int priority ) throw (VThreadExceptionInvalidPriority&)
{
    struct sched_param priorityParams;
    int policy = 0;
    int retVal = 0;

    retVal = pthread_attr_setschedparam( &myAttributes, &priorityParams );
    if ( retVal )
    {
        throw VThreadExceptionInvalidPriority(
            "cannot set priority",
            __FILE__,
            __LINE__,
            retVal );
    }
#ifndef WIN32
    retVal = pthread_attr_getschedpolicy( &myAttributes, &policy );
    if ( retVal )
    {
        throw VThreadExceptionInvalidPriority(
            "cannot set policy",
            __FILE__,
            __LINE__,
            retVal );
    }
#endif
    return ( pthread_setschedparam( myId, policy, &priorityParams ) );
}


const vthread_t*
VThread::getId() const
{
    return &myId;
}


const vthread_attr_t*
VThread::getAttributes() const
{
    return &myAttributes;
}


void
VThread::exit()
{
    if ( myId != VTHREAD_ID_INVALID )
    {
        pthread_cancel( myId );
        myId = VTHREAD_ID_INVALID;
    }
}


vthread_t
VThread::selfId()
{
    return pthread_self();
}

#if defined(WIN32)
int
sleep( int seconds )
{
	Sleep (seconds * 1000);
	return 0;
}

int
usleep( int useconds )
{
    Sleep(useconds / 1000);
	return 0;
}
#endif

#endif
// not __vxworks


syntax highlighted by Code2HTML, v. 0.9.1