// Recursive_Condition_Bug_Test.cpp,v 4.5 2003/12/26 21:59:35 shuston Exp

// ============================================================================
//
// = LIBRARY
//    tests
//
// = FILENAME
//    Recursive_Condition_Bug_Test.cpp
//
// = DESCRIPTION
//      This test program validates the functionality of the
//      ACE_Condition<ACE_Recursive_Thread_Mutex> template
//      specialization when combined with the
//      ACE_Thread_Timer_Queue_Adapter on Win32 and Posix pthreads.
//      It was added to test for bugs with the ACE_OS recursive
//      condition implementation.
//
// = AUTHOR
//    Leonid Kvetnyi <leonidk@nice.com> and
//    Douglas C. Schmidt <schmidt@cs.wustl.edu>
//
// ============================================================================

#include "test_config.h"
#include "ace/OS_NS_sys_time.h"
#include "ace/Task_T.h"
#include "ace/Activation_Queue.h"
#include "ace/Timer_Heap.h"
#include "ace/Timer_Queue_Adapters.h"
#include "ace/Condition_Recursive_Thread_Mutex.h"

ACE_RCSID (tests,
           Recursive_Condition_Bug_Test,
           "Recursive_Condition_Bug_Test.cpp,v 4.5 2003/12/26 21:59:35 shuston Exp")

#if defined (ACE_HAS_THREADS)

// Number of iterations for the performance tests.
static int max_iterations = 30;

typedef ACE_Thread_Timer_Queue_Adapter<ACE_Timer_Heap> Thread_Timer_Queue;

class Test_Handler;

class Test_Task : public ACE_Task<ACE_MT_SYNCH>
{
public:
  virtual int svc (void)
  {
    while (--max_iterations > 0)
      {
        // dequeue the next object
        ACE_Message_Block * mb;

        if (this->getq (mb) == -1)
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("(%t) %p\n"),
                             ACE_TEXT ("getq failed")),
                            -1);

        Test_Handler *handler = ACE_reinterpret_cast (Test_Handler *, mb->base ());
        mb->release ();

        ACE_Time_Value timeout = ACE_OS::gettimeofday () + ACE_Time_Value (1, 0);

        if (timer_queue_.schedule (ACE_reinterpret_cast (ACE_Event_Handler *,
                                                         handler),
                                   this,
                                   timeout) == -1)
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("(%t) %p\n"),
                             ACE_TEXT ("schedule failed")),
                            -1);

        ACE_DEBUG ((LM_INFO,
                    ACE_TEXT ("(%t) %d iteration(s) remain\n"),
                    max_iterations));
      }

    timer_queue_.deactivate ();
    ACE_DEBUG ((LM_INFO,
                ACE_TEXT ("(%t) leaving the Test_task\n")));
    return 0;
  }

  virtual int open (void * = 0)
  {
    if (ACE_Task<ACE_MT_SYNCH>::activate (THR_NEW_LWP, 1) != 0)
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT ("(%t) %p\n"),
                         ACE_TEXT ("Test_Task::activate")),
                        -1);
    if (0 != timer_queue_.activate ())
      ACE_ERROR_RETURN ((LM_ERROR,
                         ACE_TEXT ("(%t) %p\n"),
                         ACE_TEXT ("Test_Task::queue activate")),
                        -1);
    return 0;
  }

private:
  Thread_Timer_Queue timer_queue_;
};

class Test_Handler : public ACE_Event_Handler
{
public:
  virtual int handle_timeout (const ACE_Time_Value &,
                              const void *arg)
  {
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("(%t) Test_Handler::handle_timeout\n")));

    void *nc_arg = ACE_const_cast (void *, arg);
    Test_Task *test_task =
      ACE_reinterpret_cast (Test_Task *, nc_arg);
    ACE_Message_Block *mb;
    ACE_NEW_MALLOC_RETURN (mb,
                           ACE_static_cast (ACE_Message_Block *,
                                            ACE_Allocator::instance()->malloc (sizeof (ACE_Message_Block))),
                           ACE_Message_Block (sizeof (*this),    // size
                                              ACE_Message_Block::MB_DATA, // type
                                              0,       // cont
                                              (char *) this),     // data
                           -1);

    test_task->putq (mb);
    return 0;
  }
};

#endif /* ACE_HAS_THREADS */

int
run_main (int, ACE_TCHAR *[])
{
  ACE_START_TEST (ACE_TEXT ("Recursive_Condition_Bug_Test"));

#if defined (ACE_HAS_THREADS)

  // Timer queue usage.

  Test_Handler handler;
  Test_Task task;

  if (0 != task.open ())
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("(%t) %p\n"),
                       ACE_TEXT ("open")),
                      -1);

  ACE_Message_Block *mb;
  ACE_NEW_MALLOC_RETURN (mb,
                         ACE_static_cast(ACE_Message_Block *,
                                         ACE_Allocator::instance()->malloc (sizeof (ACE_Message_Block))),
                         ACE_Message_Block (sizeof (handler),    // size
                                            ACE_Message_Block::MB_DATA, // type
                                            0,       // cont
                                            (char *) &handler),     // data
                         -1);

  if (-1 == task.putq (mb))
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("(%t) %p\n"),
                       ACE_TEXT ("putq")),
                      -1);

  if (ACE_Thread_Manager::instance ()->wait () == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "wait on Thread_Manager failed"),
                      -1);
#else
  ACE_ERROR ((LM_ERROR,
              ACE_TEXT ("ACE doesn't support recursive condition variables on this platform\n")));
#endif /* ACE_HAS_THREADS */
  ACE_END_TEST;
  return 0;
}

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
template class ACE_Thread_Timer_Queue_Adapter<ACE_Timer_Heap>;
#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
#pragma instantiate ACE_Thread_Timer_Queue_Adapter<ACE_Timer_Heap>
#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */
