/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    This program 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; either version 2 of the License, or
    (at your option) any later version.
	  
	  Orginal Author : Dibyendu Majumdar
    Email  : dibyendu@mazumdar.demon.co.uk

***************************************************************************/


#include <ddthread.h>

/// A ddthreadkey object represents the key for thread specific data.
/**
	A ddthreadkey object represents the key for ddthread Specific
	data. It is mainly used to initialize the key through the
	Constructor. By declaring a ddthreadkey object as static, it is
	possible to initialize the key before any threads execute.

	Status: <b>beta</b><br>
	<br>
	<b>FIX:</b><br>
	<ol>
		<li>Create better documentation
		<li>Fix all remaining bugs <b>;-)</b>
		<li>...
 	</ol>
*/


class ddthreadkey  : public ddbaseobject
{

friend class ddthread;
friend class ddthreadinitializer;

private:

	ddthreadkey();
	pthread_key_t getKey() { return key; }
	
	pthread_key_t key;

};


/**
 * This is a helper class whose sole purpose is to initialize certain
 * static objects in a particular order.
 */
 
class ddthreadinitializer
{

public:

	ddthreadinitializer()
	{
		ddthread::key = new ddthreadkey();
		ddthreadfactory::_mutex = new ddthreadmutex();		
	}

	~ddthreadinitializer()
	{
		
	}

};


ddthreadkey*   ddthread::key;
ddthreadmutex*       ddthreadfactory::_mutex;

/*
 * The initializer object initializes the above two objects.
 */

ddthreadinitializer initialize;

/**
	Construct a thread key.
*/

ddthreadkey::ddthreadkey()
{
	int rc = pthread_key_create(&key, 0);
	if (rc != 0)
		log_fatal( "ddtreadkey.ctor: Error: Failed to create ddtreadkey" );
}


/**
 * A C wrapper to C++ function.
 */

static void dm1_thread_cleanup( void *arg )
{
	ddthread *me = (ddthread *)arg;
	me->cleanup();
}


/**
 * The thread library expects the startup function to be in C.
 * We therefore wrap a C function around the C++ method.
 */

static void* dm1_thread_main( void *arg )
{
	ddthread* thread = (ddthread*) arg;
	thread->execute();
	return 0;
}

/**
 * Initialize the thread. This is called immediately after the thread has
 * started executing.
 */


void ddthread::init()
{	
	ddthreadfactory::lock();
	status = DM1_THREAD_RUNNING;
	ddthreadfactory::unlock();

	tid = ::pthread_self();	/* BUG in Linux pthread package ?? */
	int rc = ::pthread_setspecific(key->getKey(), (const void *)this);
	if (rc != 0)
		log_fatal( "ddthread.init: Error: Failed to set key value" );
}


/**
	Cleanup the thread. This is called before the thread terminates.
*/

void ddthread::cleanup()
{	
	::pthread_setspecific( key->getKey(), 0 );
	ddthreadfactory::lock();
	status = DM1_THREAD_DEAD;
	ddthreadfactory::unlock();
}


/**
	This is the wrapper around the run() method.
	It performs initialization and cleanup, and also handles any
	exceptions raised by the run() method.
*/

void ddthread::execute()
{
	init();
	pthread_cleanup_push( dm1_thread_cleanup, this );
	_runnable->run();
	pthread_cleanup_pop(1);
}


/**
 This is the method that should be called to start the ddthread
*/

void ddthread::start()
{

	int rc = ::pthread_create( &tid, 0, dm1_thread_main, this );
	if (rc != 0)
		log_fatal( "ddthread.start: Error: Failed to start thread %s\n" + get_name());
}


/**
	Cancel this thread
*/

void ddthread::cancel()
{
	int rc = ::pthread_cancel( tid );
	if (rc != 0 )
		log_fatal( "ddthread.cancel: Error: Failed to start thread " + get_name());
}


/**
 * The ddthread constructor.
 */

ddthread::ddthread( ddthreadrunnable* runnable, const ddstring& name, bool daemon)
{
	status = DM1_THREAD_UNUSED;
	isDaemon = daemon;
	_runnable = runnable;
	_runnable->set_thread( this );

	isSignaled = 0;
	set_name( name );
	status = DM1_THREAD_INITED;

}

/**
 * Terminates the thread.
 */

void ddthread::exit()
{
	cout << "exit: " << get_name() << endl;
	::pthread_exit(0);
}


/**
	Waits until there is a timeout or some other thread signals this
	thread.
*/

int ddthread::wait( unsigned secs )
{
	if (secs == 0)
		return 0;
	return event.wait(secs);
}


/**
	Waits until some other thread wakes it up.
*/

void ddthread::wait()
{
	event.wait();
}


/**
	Wakes up a waiting thread.
*/

void ddthread::notify( ddthread* other )
{
	other->event.notify();
}


/**
	Waits for the thread to complete.
 */

void ddthread::join()
{

	int rc = ::pthread_join(tid, 0);
	if (rc != 0)
		log_fatal( "ddthread.join: Error: Failed to join thread: " + get_name());
	ddthreadfactory::lock();
	status = DM1_THREAD_UNUSED;
	ddthreadfactory::unlock();
}


/**
	 Destroys the ddthread object.
 	IMPORTANT: thread must be joined before this is called.
*/

ddthread::~ddthread()
{
	if( status != DM1_THREAD_UNUSED)
		log_fatal( "ddthread.dtor: Error: ddthread object destroyed before ddthread %s has been joined" + get_name());
  //??
	//delete _runnable;
	//_runnable = 0;

}


/**
 * Returns the current ddthread object.
 */

ddthread* ddthread::get_current_thread()
{
	return (ddthread*) ::pthread_getspecific (key->getKey() );
}


/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    This program 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; either version 2 of the License, or
    (at your option) any later version.

***************************************************************************/


/**
	
*/

ddthread * ddthreadfactory::create_thread( ddthreadrunnable *runnable, const ddstring& name, bool daemon )
{
	lock();
	ddthread* thread = new ddthread( runnable, name, daemon );
	//_threads->append(t);
	unlock();	
	return thread;
}

