
//=============================================================================
/**
 *  @file    Task_Group_Test.cpp
 *
 *    This test program illustrates the logic of the grp_id handling
 *    mechanism in <ACE_Task_Base> and the <ACE_Thread_Manager>.
 *    Partially reuses test submitted by Paxton Mason in Bugzilla #2366
 *
 *  @author Martin Corino <mcorino@remedy.nl>
 */
//=============================================================================


#include "test_config.h"
#include "ace/Atomic_Op.h"
#include "ace/Task.h"
#include "ace/OS_NS_unistd.h"

#if defined (ACE_HAS_THREADS)

const int MAX_GROUP_THREADS = ACE_MAX_THREADS * 2;

static ACE_Atomic_Op<ACE_Thread_Mutex, int> run_count (0);

class Waiter_Task : public ACE_Task_Base
{
public:
  Waiter_Task (ACE_Thread_Manager* tm = 0)
    : ACE_Task_Base (tm) {}
  int svc() override;
};

int
Waiter_Task::svc()
{
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("(%t|grp_id=%d) waiting for thread cancellation.\n"), this->grp_id ()));

  run_count++;

  // wait until canceled
  while (!ACE_Task_Base::thr_mgr ()->testcancel (ACE_OS::thr_self ()))
    ACE_OS::sleep (0);

  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("(%t|grp_id=%d) thread cancelled.\n"), this->grp_id ()));

  run_count--;

  return 0;
}

#endif /* ACE_HAS_THREADS */

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

#if defined (ACE_HAS_THREADS)

  ACE_thread_t thread_list[MAX_GROUP_THREADS] = {0};
  int group_id = -1;

  Waiter_Task waiter_task (ACE_Thread_Manager::instance ());

  // activate a single thread
  waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1);
  // get the assigned grp_id
  group_id = waiter_task.grp_id ();
  // attempt to add a thread -> this should fail without force_activate
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1) != 1)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have failed to add new thread.")));
  }
  // force addition of new thread
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1, 1) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to force addition of new thread.")));
  }
  // force addition of new thread ignoring specific group id
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1, 1, ACE_DEFAULT_THREAD_PRIORITY, group_id+1) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to force addition of new thread.")));
  }
  // we should now have 3 threads in group 'group_id'
  if (waiter_task.thr_mgr ()->thread_grp_list (group_id, &thread_list[0], MAX_GROUP_THREADS) != 3)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Should have found 3 threads in group %d."), group_id));
  }

  // cancel all threads
  waiter_task.thr_mgr ()->cancel_all ();
  // wait for threads to exit cleanly
  while (waiter_task.thr_mgr ()->num_threads_in_task (&waiter_task) > 0)
    ACE_OS::sleep (0);

  // restart 2 threads reusing the group id
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 2) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to add new threads.")));
  }
  // attempt to add a thread -> this should fail without force_activate
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1) != 1)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have failed to add new thread.")));
  }
  // force addition of new thread
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1, 1) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to force addition of new thread.")));
  }
  // force addition of new thread ignoring specific group id
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1, 1, ACE_DEFAULT_THREAD_PRIORITY, group_id+1) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to force addition of new thread.")));
  }
  // we should now have 4 threads in group 'group_id'
  if (waiter_task.thr_mgr ()->thread_grp_list (group_id, &thread_list[0], MAX_GROUP_THREADS) != 4)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Should have found 4 threads in group %d."), group_id));
  }

  // wait till the threads actually started up
  while (run_count != 4)
    ACE_OS::sleep (0);

  // change the group id of all threads of the task
  group_id += 1;
  waiter_task.grp_id (group_id);

  // we should still have 4 threads in group with new 'group_id'
  if (waiter_task.thr_mgr ()->thread_grp_list (group_id, &thread_list[0], MAX_GROUP_THREADS) != 4)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Should have found 4 threads in group %d."), group_id));
  }

  // cancel all threads
  waiter_task.thr_mgr ()->cancel_all ();
  // wait for threads to exit cleanly
  while (waiter_task.thr_mgr ()->num_threads_in_task (&waiter_task) > 0)
    ACE_OS::sleep (0);

  // restart 3 threads using explicit new group id
  group_id = 33;
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 3, 0, ACE_DEFAULT_THREAD_PRIORITY, group_id) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to add new threads.")));
  }
  // attempt to add a thread -> this should fail without force_activate
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1) != 1)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have failed to add new thread.")));
  }
  // force addition of new thread
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1, 1) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to force addition of new thread.")));
  }
  // force addition of new thread ignoring specific group id
  if (waiter_task.activate (THR_NEW_LWP | THR_DETACHED, 1, 1, ACE_DEFAULT_THREAD_PRIORITY, group_id+1) != 0)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Task_Base::activate should have succeeded to force addition of new thread.")));
  }
  // we should now have 5 threads in group 'group_id'
  if (waiter_task.thr_mgr ()->thread_grp_list (group_id, &thread_list[0], MAX_GROUP_THREADS) != 5)
  {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Should have found 5 threads in group %d."), group_id));
  }

  // cancel all threads
  waiter_task.thr_mgr ()->cancel_all ();
  // wait for threads to exit cleanly
  while (waiter_task.thr_mgr ()->num_threads_in_task (&waiter_task) > 0)
    ACE_OS::sleep (0);

#else
  ACE_ERROR ((LM_INFO,
              ACE_TEXT ("threads not supported on this platform\n")));
#endif /* ACE_HAS_THREADS */
  ACE_END_TEST;
  return 0;
}
