/* -*- mode: c++; c-basic-offset: 4; -*-
 *
 * MICO --- a CORBA implementation
 * Copyright (C) 1997-98 Kay Roemer & Arno Puder
 * 
 * thread management
 * Copyright (C) 1999 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: mt_manager.cc,v 1.34 2000/01/21 18:40:22 aschultz Exp $
 *
 */

#define MICO_DEBUG_MODULE MICODebug::Thread

#define MICO_CONF_IMR
#include <CORBA-SMALL.h>
#include <mico/impl.h>

/********************************** ThreadPool **********************************/

MICO::ThreadPool::ThreadPool( unsigned int _max,
			      unsigned int _max_idle,
			      unsigned int _min_idle): idle_threads(_max),
                                                       tp_lock(FALSE, MICOMT::Mutex::Recursive) {
    
    max = _max;
    max_idle = _max_idle;
    min_idle = _min_idle;
    op       = 0;
    input_mc = 0;
    cnt_all  = 0;
}

MICO::ThreadPool::~ThreadPool() {
    unsigned int i, cnt;
    
    assert( cnt_all == idle_threads.count());

    i = idle_threads.first();
    for (cnt = idle_threads.count(); cnt > 0;
	 i = idle_threads.next(i), cnt--) {
	idle_threads[i]->terminate( NULL );
    }
}

// fire up all threads
void
MICO::ThreadPool::start_threads (void *arg) {
    unsigned int i;
    MICO::WorkerThread *kt;

    for (i = 0; i < min_idle; i++) {
	kt = new_idle_thread();
	if ( getOperation() )
	    kt->registerOperation( getOperation()->copy() );
    }
}

// returns - a idle thread or creates a new thread if needed
//         - or NULL if limits are exhausted
//
// removes threads from the idle list
//
MICO::WorkerThread *
MICO::ThreadPool::get_idle_thread()
{
    MICOMT::auto_Lock l(tp_lock);
    WorkerThread *kt;
    
    //no idle threads
    if (idle_threads.count() == 0) {

	// limit exhausted
	if (cnt_all >= max) {
	    __MTOUT( MT_TRACE ( MICODebug::instance()->printer() 
				<< "ThreadPool::get_idle_thread(): thread limit exhausted "
				<< endl ));
	    return 0;
	}
	
	kt = new MICO::WorkerThread( this );
	if (op)
	    kt->registerOperation( op->copy() );
	cnt_all++;
	kt->start();
    } else {
	kt = idle_threads[idle_threads.first()];
	idle_threads.remove(idle_threads.first());
    }
    kt->state( MICO::WorkerThread::Transition );
    return kt;
}

MICO::WorkerThread *
MICO::ThreadPool::new_idle_thread()
{
    WorkerThread *kt;
    
    kt = new MICO::WorkerThread( this );
    {
	MICOMT::auto_Lock l(tp_lock);
	
	kt->no( idle_threads.insert( kt ) );
	kt->state( MICO::WorkerThread::Idle );
	cnt_all++;
    }
    kt->start();
    return kt;
}
 
void
MICO::ThreadPool::mark_idle( MICO::WorkerThread *kt )
{
    if (input_mc && input_mc->check_msg(kt)) 
	return;

    if ( kt->state() != MICO::WorkerThread::Idle )
    {
	MICOMT::auto_Lock l(tp_lock);
	
	kt->no( idle_threads.insert( kt ) );
	kt->state( MICO::WorkerThread::Idle );
    }
    if (input_mc)
	input_mc->check_msg( 0 );
}

void 
MICO::ThreadPool::mark_busy( MICO::WorkerThread *kt )
{
    //    MICOMT::auto_Lock l(tp_lock);

    assert ( kt->state() != MICO::WorkerThread::Idle);

    kt->state( MICO::WorkerThread::Busy );
}

void 
MICO::ThreadPool::remove_thread( MICO::WorkerThread *kt )
{
    MICOMT::auto_Lock l(tp_lock);
	
    if ( kt->state() == MICO::WorkerThread::Idle ) {
	idle_threads.remove( kt->no() );
    }
    cnt_all--;
}

/****************************** WorkerThread *******************************/

void
MICO::ThreadPool::put_msg( MICO::OP_id_type nextOP_id, MICO::msg_type * msg) {
    if (input_mc)
	input_mc->put_msg(nextOP_id, msg);
}

MICO::WorkerThread::WorkerThread(): op_critical( FALSE, MICOMT::Mutex::Recursive)
{
    op = 0;
#ifdef DEBUG_NAMES
    _state_sem.name("WorkerThread._state_sem");
#endif
}

MICO::WorkerThread::WorkerThread( MICO::ThreadPool *_tp ): op_critical( FALSE, MICOMT::Mutex::Recursive)
{

    op = 0;
    tp = _tp;
#ifdef DEBUG_NAMES
    _state_sem.name("WorkerThread._state_sem");
#endif
}

MICO::WorkerThread::~WorkerThread()
{
    if (tp) {
	tp->remove_thread( this );
    }
}

void
MICO::WorkerThread::registerOperation( MICO::Operation* _op )
{
    MICOMT::auto_Lock l(op_critical);

    op = _op;
    op->Thread( this );
}

void
MICO::WorkerThread::deregisterOperation( MICO::Operation* _op )
{
    MICOMT::auto_Lock l(op_critical);

    if (op == _op)
    {
	op->Thread( 0 );
	op = 0;
    }
}

void
MICO::WorkerThread::_run (void *arg) {

    __MTOUT( MT_TRACE ( MICODebug::instance()->printer() 
			<< "WorkerThread::_run: " << endl ));
    do {
	wait_for_state_change();
	if ( state() == Busy ) {

	    op_critical.lock();
	    if ( getOperation() != 0 )
		getOperation()->_run();
	    op_critical.unlock();
	    
	    mark_idle();
	    post_state_change();
	}
    } while ( state() != Stop );
}

MICO::ThreadPoolManager *MICO::_tpm;

struct MICO::tm_init_t tm_init[] = {
    { MICO::Operation::Accept, 20, 0, 0, MICO::MsgChannel::passive, 0, 0, FALSE },
    { MICO::Operation::Writer, 20, 0, 0, MICO::MsgChannel::passive, 0, 0, FALSE },
    { MICO::Operation::Reader, 20, 0, 0, MICO::MsgChannel::passive, 0, 0, FALSE },
    { MICO::Operation::DeCode, 20, 0, 0, MICO::MsgChannel::active,  1, 0, TRUE  },
    { MICO::Operation::ORB,    10, 0, 0, MICO::MsgChannel::active,  1, 0, TRUE  } };

void MICO::thread_setup(unsigned int conn_limit, unsigned int req_limit)
{
    if (!conn_limit)
    {
        cerr << "-ORBConnLimit: Connection Limit must be non-zero" << endl;
        exit (-1);
    }
    tm_init[1].max = conn_limit;
    tm_init[2].max = conn_limit;

    if (!req_limit)
    {
        cerr << "-ORBRequestLimit: Request Limit must be non-zero" << endl;
        exit (-1);
    }
    tm_init[3].max = req_limit;

    _tpm = new MICO::ThreadPoolManager();

    for (unsigned int i = 0; i < sizeof(tm_init)/sizeof(MICO::tm_init_t); i++) {

	MICO::ThreadPool *tp = new MICO::ThreadPool( tm_init[i].max,
									tm_init[i].max_idle,
									tm_init[i].min_idle );
	_tpm->register_tp( tm_init[i].OP_type, *tp );

	switch (tm_init[i].OP_type) {
	    case MICO::Operation::DeCode: {
		tp->setOperation( new MICO::InputHandler() );
		break;
	    }
	    case MICO::Operation::ORB: {
		tp->setOperation( new MICO::MTDispatcher() );
		break;
	    }
	}
	
	switch ( tm_init[i].mct ) {
	case MICO::MsgChannel::active:
	    if ( tm_init[i].mc_size )
		tp->registerInputMC( *new MICO::activeMsgQueue() );
	    break;
	case MICO::MsgChannel::direct:
	    {         
		MICO::directMsgConnector *mq = new MICO::directMsgConnector();
		mq->setOperation( tp->getOperation() );
		tp->registerInputMC( *mq );
		break;
	    }
	default:
	    break;
	}
	if ( tm_init[i].start ) {
	    tp->start_threads();
	}
    }
#ifdef DEBUG_NAMES
    _tpm->getOperation( MICO::Operation::DeCode )->name("InputHandler");
    _tpm->getOperation( MICO::Operation::ORB )->name("MTDispatcher");
#endif
}
