/* ====================================================================
 * 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 Thread_cxx_Version = 
    "$Id: Thread.cxx,v 1.4 2001/08/10 04:02:08 icahoon Exp $";


#include "global.h"
#include "Thread.hxx"
#include "Runnable.hxx"
#include "SignalHandler.hxx"
#include "Application.hxx"
#include "Lock.hxx"
#include <cstdio>
#include <cassert>

using Vocal::Threads::Thread;
using Vocal::Threads::Mutex;
using Vocal::Threads::Lock;
using Vocal::Signals::SignalHandler;
using Vocal::Runnable;
using Vocal::ReturnCode;


Thread::ThreadMap   		*   Thread::myThreadMap = 0;
Mutex		    	    	    Thread::myThreadMapMutex;
Thread          	    	*   Thread::myMainThread = 0;


extern "C"
{

static void *  
localThreadMain(void * data)
{
    return ( Thread::threadMain(data) );
}

} // extern "C"


void	    	    
Thread::init()
{
    Lock    lock(myThreadMapMutex);
    
    if ( !myThreadMap )
    {
        myThreadMap = new map<vthread_t, Thread *>;
        myMainThread = new Thread;

    	vthread_t   mainThreadId = VThread::selfId();
	
	(*myThreadMap)[mainThreadId] = myMainThread;
    }
}


void	    	    
Thread::uninit()
{
    Lock    lock(myThreadMapMutex);

    delete myThreadMap;
    myThreadMap = 0;

    delete myMainThread;
    myMainThread = 0;
}


Thread *
Thread::self()
{
    init();
    
    Lock    lock(myThreadMapMutex);

    vthread_t	id = VThread::selfId();

    assert( myThreadMap != 0 );
    
    ThreadMap::iterator it = myThreadMap->find(id);
    
    return ( it == myThreadMap->end() ? 0 : (*it).second );
}


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


Thread::Thread(Runnable & runnable, const char * name)
    :	myThread(new VThread),
    	myRunnable(runnable),
	mySignalHandler(0),
	myName(name ? name : "thread")
{
    init();

    myThread->spawn(localThreadMain, this);
}


Thread::~Thread()
{
    const vthread_t *   threadId = myThread->getId();

    if ( threadId )
    {
        Lock    lock(myThreadMapMutex);

    	if ( myThreadMap )
	{
            myThreadMap->erase(*threadId);
	}
	else
	{
    	    assert( myThreadMap != 0 );
	}
    }

    delete myThread;
    
    delete mySignalHandler;
}


ReturnCode  	    	    
Thread::join()
{
    // Can't join from your own thread.
    //
    if ( this == self() )
    {
    	return ( 0 );
    }
    
    ReturnCode rc = 0;
    
    if ( myThread )
    {
    	rc = myThread->join();
    }
    return ( rc );
}


void
Thread::exit()
{
    // Can't exit unless this is the thread of execution.
    //
    if ( myThread == 0 || this != self() )
    {
    	return;
    }

    myThread->exit();
}


Runnable &
Thread::runnable()
{
    return ( myRunnable );
}	


SignalHandler &     	    
Thread::signalHandler()
{
    assert( mySignalHandler != 0 );
    return ( *mySignalHandler );
}


ostream &           
Thread::writeTo(ostream & out) const
{
    return ( out << myName.c_str() );
}	


Thread::Thread()
    :	myThread(0),
    	myRunnable(Application::instance()),
	mySignalHandler(new SignalHandler),
	myName("main")
{
    char    buffer[64];
    sprintf(buffer, "%lu", (unsigned long)VThread::selfId());
    myName += '.';
    myName += buffer;
}


void *	    
Thread::threadMain(void * data)
{
    Thread *	thread = (Thread *)data;
    
    if ( thread == 0 )
    {
    	return ( (void *)1 );
    }
    
    const vthread_t	*   threadId = thread->myThread->getId();

    // Scope the lock for update of thread map only.
    //
    {
        Lock    lock(myThreadMapMutex);
	if ( myThreadMap )
	{
    	    (*myThreadMap)[*threadId] = thread;
	}
	else
	{
	    assert( myThreadMap != 0 );
	    return ( (void *)1 );
	}
    }

    // Update name
    //
    char	    buffer[64];
    sprintf(buffer, "%lu", (unsigned long)*threadId);
    thread->myName += '.';
    thread->myName += buffer;

    // The signalHandler will persist for the lifetime of the run.
    //
    thread->mySignalHandler = new SignalHandler;

    void    * rc = (void *)(thread->myRunnable.run());

    delete thread->mySignalHandler;
    thread->mySignalHandler = 0;
    
    return ( rc );
}


syntax highlighted by Code2HTML, v. 0.9.1