/* -*- mode: c++; c-basic-offset: 4; -*-
 *
 * MICO --- a CORBA implementation
 * Copyright (C) 1997-98 Kay Roemer & Arno Puder
 * 
 * OSThread: An abstract Thread class for MICO
 * Copyright (C) 1999 Andy Kersting & Andreas Schultz
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Send comments and/or bug reports to:
 *                mico@informatik.uni-frankfurt.de
 *
 * $Id: pthreads.cc,v 1.20 2000/02/09 18:07:34 akerst Exp $
 *
 * support for POSIX Draft 10ish Pthreads for Linux as developed by
 * Xavier LeRoy@inria.fr
 *
 */

#define MICO_CONF_IMR

#include <pthread.h>
#include <CORBA-SMALL.h>

//
// Global things (used/defined ONLY when HAVE_THREADS is defined)
//
#ifdef HAVE_THREADS  

static pthread_mutex_t __debug_mutex = PTHREAD_MUTEX_INITIALIZER;

void __mtdebug_init()
{ 
}

void __mtdebug_lock()
{
    assert (!pthread_mutex_lock( &__debug_mutex ));
}

void __mtdebug_unlock()
{
    assert (!pthread_mutex_unlock( &__debug_mutex ));
}

//
// No matter what the actual thread implementation is, _init ()
// takes care of initializing whatever has to be initialized.
//
static void MICOMT::_init (MICO_ULong _tpsize, MICO_ULong _tpincr) {
 
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    << "_init (_tpsize = " << _tpsize << ", _tpincr = "
			    << _tpincr << ")" << endl ));
}

//
// Thread::ThreadWrapper is a static member function that
// pthread_create(...) (or the like) can call as its "thread start routine".
// Any object whose methods are to be invoked as a thread, must derive from
// the class "Thread".  Thread has a method, _run(),
// which starts the Thread (or a derivative) "in motion".
//
void *
MICOMT::Thread::ThreadWrapper( void *arg )
{
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    << "ThreadWrapper()" << endl ));
    static_cast<Thread *>(arg)->_thr_startup( arg );
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    << "ThreadWrapper(): after calling _run()" << endl ));
 
    return NULL;          // the "start_routine" must return a void *.
}

//
// reader/writer implementation
//   xxlock() and unlock() are inlined for optimal speed
//
MICOMT::rwLock::rwLock() __NAME( : _namedObject () )
{
    RWL_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			 << "rwLock::rwLock ()" << endl ));
    __NAME (name ("<UnNamed rwLock>"));
    assert (!pthread_rwlock_init( &_rwlock, NULL ));
}

MICOMT::rwLock::~rwLock()
{
    RWL_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			 __NAME (<< name ()) << ": rwLock::~rwLock ()" << endl ));
    assert (!pthread_rwlock_destroy( &_rwlock ));
}

//
// Semaphore implementation
//   post() and wait() are inlined for optimal speed
//
MICOMT::Semaphore::Semaphore( unsigned int val ) __NAME( : _namedObject () )
{
    SEMA_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			  << "Semaphore::Semaphore (val = " << val << ")" << endl ));
    __NAME (name ("<UnNamed Semaphore>"));
    assert( sem_init( &s, 0, val) == 0 ); 
}

MICOMT::Semaphore::~Semaphore()
{
    SEMA_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			  __NAME (<< name ()) << ": Semaphore::~Semaphore ()" << endl ));
    sem_destroy( &s );
}

//
// Mutex implementation
//   lock() and unlock() are inlined for optimal speed
//
MICOMT::Mutex::Mutex( MICO_Boolean locked, Attribute attr ) __NAME( :_namedObject () )
{
    pthread_mutexattr_t m_attr;

    MUTEX_DEBUG( MT_TRACE( MICODebug::instance()->printer() 
			   << "Mutex::Mutex (initially locked = "
                           << ((locked == FALSE) ? "FALSE" : "TRUE")
			   << ", type = "
                           << ((attr == Normal) ? "Normal" : "Recursive")
                           << ")" << endl ));

    __NAME (name ("<UnNamed Mutex>"));

    assert (!pthread_mutexattr_init( &m_attr));
    if ( attr != Normal ) {
	switch (attr) {
	case Recursive: {
	    assert (!pthread_mutexattr_settype( &m_attr,
                                                PTHREAD_MUTEX_RECURSIVE ));
	    break;
	default:
	    break;
	}
	}
    }
    assert (!pthread_mutex_init(&_mutex, &m_attr));
    assert (!pthread_mutexattr_destroy( &m_attr));
    if (locked) lock();
}

MICOMT::Mutex::~Mutex()
{ 
    MUTEX_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			   __NAME (<< name ()) << ": Mutex::~Mutex ()"
                           << endl ));
    assert (!pthread_mutex_destroy(&_mutex));
}
	
//
// condition variable
//
MICOMT::CondVar::CondVar()
{
    __NAME( name ("<UnNamed CondVar>") );
    COND_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			  __NAME (<< name ()) << ": ContVar::CondVar ()"
                          << endl ));

    assert (!pthread_cond_init(&_cond, NULL));
}

MICOMT::CondVar::~CondVar()
{
    COND_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			  __NAME (<< name ()) << ": ContVar::~CondVar ()"
                          << endl ));

    assert (!pthread_cond_destroy(&_cond));
}


//
// Thread implementation for LinuxThreads
//
MICOMT::Thread::Thread (MICOMT::Thread::DetachFlag detached) __NAME( :_namedObject () )
{
    _detached = detached;
    __NAME (name ("<Un-Named Thread>"));
 
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    <<"Thread::Thread (detached = " << (int)(char)detached
			    << ")" << endl ));
#ifdef _THR_CREATE_AND_BLOCK
    create_thread();
#endif
}
 
//
// Start the thread ala JAVA:
//
MICOMT::Thread::ErrorType 
MICOMT::Thread::start (void *arg)
{
    //FIXME: whats that for ?
    _arg = arg;
 
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    << "Thread::start (void *arg)" << endl ));
#ifdef _THR_CREATE_AND_BLOCK
    _ready.unlock();
    return MICOMT::Thread::NoError;
#else
    return create_thread();
#endif
}
 
//
// see if the thread given by ThreadID `id' is the same as this
// thread:
//
MICO_Boolean
MICOMT::Thread::operator == (const MICOMT::Thread &thread) const
{
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    << "Thread::operator == ()" << endl ));
    return (MICO_Boolean)pthread_equal (thread.id(), id() );
}

//
// set the priority of this thread to `new_priority'.
// let's avoid using this if at all possible, some pthreads impls don't
// support it fully or at all:
//
void
MICOMT::Thread::priority (MICO_Long new_priority) {
}

//
// destoying the object that runs our thread is not a good idea
// kill it first
//
MICOMT::Thread::~Thread ()
{
    // make sure the thread is really finished, before we remove it data structures 
    wait();
    _ready.unlock();
    THREAD_DEBUG( MT_TRACE( MICODebug::instance()->printer()
			    << "Thread::~Thread()" << endl ));
}


#endif // HAVE_THREADS
