/* Thread iterators and ranges for GDB, the GNU debugger.

   Copyright (C) 2018-2023 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "defs.h"
#include "gdbthread.h"
#include "inferior.h"

/* See thread-iter.h.  */

all_threads_iterator::all_threads_iterator (begin_t)
{
  /* Advance M_INF/M_THR to the first thread's position.  */

  for (inferior &inf : inferior_list)
    {
      auto thr_iter = inf.thread_list.begin ();
      if (thr_iter != inf.thread_list.end ())
	{
	  m_inf = &inf;
	  m_thr = &*thr_iter;
	  return;
	}
    }
  m_inf = nullptr;
  m_thr = nullptr;
}

/* See thread-iter.h.  */

void
all_threads_iterator::advance ()
{
  intrusive_list<inferior>::iterator inf_iter (m_inf);
  intrusive_list<thread_info>::iterator thr_iter (m_thr);

  /* The loop below is written in the natural way as-if we'd always
     start at the beginning of the inferior list.  This fast forwards
     the algorithm to the actual current position.  */
  goto start;

  for (; inf_iter != inferior_list.end (); ++inf_iter)
    {
      m_inf = &*inf_iter;
      thr_iter = m_inf->thread_list.begin ();
      while (thr_iter != m_inf->thread_list.end ())
	{
	  m_thr = &*thr_iter;
	  return;
	start:
	  ++thr_iter;
	}
    }

  m_thr = nullptr;
}

/* See thread-iter.h.  */

bool
all_matching_threads_iterator::m_inf_matches ()
{
  return (m_filter_target == nullptr
	  || m_filter_target == m_inf->process_target ());
}

/* See thread-iter.h.  */

all_matching_threads_iterator::all_matching_threads_iterator
  (process_stratum_target *filter_target, ptid_t filter_ptid)
  : m_filter_target (filter_target)
{
  if (filter_ptid == minus_one_ptid)
    {
      /* Iterate on all threads of all inferiors, possibly filtering on
	 FILTER_TARGET.  */
      m_mode = mode::ALL_THREADS;

      /* Seek the first thread of the first matching inferior.  */
      for (inferior &inf : inferior_list)
	{
	  m_inf = &inf;

	  if (!m_inf_matches ()
	      || inf.thread_list.empty ())
	    continue;

	  m_thr = &inf.thread_list.front ();
	  return;
	}
    }
  else
    {
      gdb_assert (filter_target != nullptr);

      if (filter_ptid.is_pid ())
	{
	  /* Iterate on all threads of the given inferior.  */
	  m_mode = mode::ALL_THREADS_OF_INFERIOR;

	  m_inf = find_inferior_pid (filter_target, filter_ptid.pid ());
	  if (m_inf != nullptr)
	    m_thr = &m_inf->thread_list.front ();
	}
      else
	{
	  /* Iterate on a single thread.  */
	  m_mode = mode::SINGLE_THREAD;

	  m_thr = filter_target->find_thread (filter_ptid);
	}
    }
}

/* See thread-iter.h.  */

void
all_matching_threads_iterator::advance ()
{
  switch (m_mode)
    {
    case mode::ALL_THREADS:
      {
	intrusive_list<inferior>::iterator inf_iter (m_inf);
	intrusive_list<thread_info>::iterator thr_iter
	  = m_inf->thread_list.iterator_to (*m_thr);

	/* The loop below is written in the natural way as-if we'd always
	   start at the beginning of the inferior list.  This fast forwards
	   the algorithm to the actual current position.  */
	goto start;

	for (; inf_iter != inferior_list.end (); ++inf_iter)
	  {
	    m_inf = &*inf_iter;

	    if (!m_inf_matches ())
	      continue;

	    thr_iter = m_inf->thread_list.begin ();
	    while (thr_iter != m_inf->thread_list.end ())
	      {
		m_thr = &*thr_iter;
		return;

	      start:
		++thr_iter;
	      }
	  }
      }
      m_thr = nullptr;
      break;

    case mode::ALL_THREADS_OF_INFERIOR:
      {
	intrusive_list<thread_info>::iterator thr_iter
	  = m_inf->thread_list.iterator_to (*m_thr);
	++thr_iter;
	if (thr_iter != m_inf->thread_list.end ())
	  m_thr = &*thr_iter;
	else
	  m_thr = nullptr;
	break;
      }

    case mode::SINGLE_THREAD:
      m_thr = nullptr;
      break;

    default:
      gdb_assert_not_reached ("invalid mode value");
    }
}
