/*
** (c) 1996-2000 The Regents of the University of California (through
** E.O. Lawrence Berkeley National Laboratory), subject to approval by
** the U.S. Department of Energy.  Your use of this software is under
** license -- the license agreement is attached and included in the
** directory as license.txt or you may contact Berkeley Lab's Technology
** Transfer Department at TTD@lbl.gov.  NOTICE OF U.S. GOVERNMENT RIGHTS.
** The Software was developed under funding from the U.S. Government
** which consequently retains certain rights as follows: the
** U.S. Government has been granted for itself and others acting on its
** behalf a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, and perform publicly
** and display publicly.  Beginning five (5) years after the date
** permission to assert copyright is obtained from the U.S. Department of
** Energy, and subject to any subsequent five (5) year renewals, the
** U.S. Government is granted for itself and others acting on its behalf
** a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, distribute copies to
** the public, perform publicly and display publicly, and to permit
** others to do so.
*/

#include <cassert>
#include <list>
#include <cstdio>

#ifndef WIN32
#include <unistd.h>
#endif

#include <WorkQueue.H>

namespace
{
Mutex print_mutex;
}

#if 1
#define DPRINTF(arg)							\
do									\
  {									\
    Lock<Mutex> lock(print_mutex);					\
    std::printf("tid(%d): ", Thread::getID());				\
    std::printf arg;							\
  }									\
while (false)
#else
#define DPRINTF(arg)
#endif

namespace
{
Mutex rand_mutex;
int random_l()
{
    Lock<Mutex> lock(rand_mutex);
    int i = rand();
    return i;
}
}

namespace
{
const int ITERATIONS = 25;

struct power_t
    : public WorkQueue::task
{
    power_t(int value_, int power_) : value(value_), power(power_)
    {
	assert(value >= 0);
	assert(power >= 0);
    }
    virtual void run();
    int value;
    int power;
};

struct engine_t
{
    explicit engine_t(const pthread_t& thr_) : thread_id(thr_), calls(1) {}
    pthread_t thread_id;
    int calls;
};

Mutex engine_list_mutex;
std::list<engine_t*> engine_list;
WorkQueue workq(4);
}

void
Engine_destructor(void* value_ptr)
{
    engine_t* engine = static_cast<engine_t*>(value_ptr);
    Lock<Mutex> lock(engine_list_mutex);
    engine_list.push_back(engine);
}

namespace
{
ThreadSpecificData<engine_t> engine_key(0, Engine_destructor);

void
power_t::run()
{
    engine_t* engine = engine_key.get();
    if ( engine == 0 )
    {
	engine = new engine_t(pthread_self());
	engine_key.set(engine);
    }
    else
    {
	engine->calls++;
    }
    int result = 1;
    DPRINTF(("Engine: computing %d^%d\n", value, power));
    for ( int count = 1; count <= power; count++ )
    {
	result *= value;
    }
    DPRINTF(("Engine: result %d\n", result));
}
}

extern "C"
void*
WorkQueue_routine(void*)
{
    for ( int count = 0; count < ITERATIONS; count++ )
    {
	power_t* element = new power_t(random_l() % 20, random_l() % 7);
	DPRINTF(("Request: %d^%d\n", element->value, element->power));
	workq.add(element);
	sleep(random_l() % 5);
    }
    return 0;
}

int
main(int argc, char** argv)
{
    BoxLib::Initialize(argc, argv);

    FunctionThread ft(WorkQueue_routine);
    WorkQueue_routine(0);
    ft.join();
    workq.add(0);

    workq.wait();
    workq.drain();
    sleep(1);			// FIXME: wait for descructors to all be called

    int count = 0;
    int calls = 0;
    for ( std::list<engine_t*>::const_iterator it = engine_list.begin(); it != engine_list.end(); ++it )
    {
	engine_t* engine = *it;
	count++;
	calls += engine->calls;
	std::printf("engine %d(%ld): %d calls\n", count, engine->thread_id, engine->calls);
	delete engine;
    }
    std::printf("%d engine threads processed %d calls\n", count, calls);
    BoxLib::Finalize();
}
