#include "fbsd-nat.h"
 #include "fbsd-tdep.h"
 
-#include <list>
-
 #ifndef PT_GETREGSET
 #define        PT_GETREGSET    42      /* Get a target register set */
 #define        PT_SETREGSET    43      /* Set a target register set */
 #endif
 
+/* See fbsd-nat.h.  */
+
+void
+fbsd_nat_target::add_pending_event (const ptid_t &ptid,
+                                   const target_waitstatus &status)
+{
+  gdb_assert (find_inferior_ptid (this, ptid) != nullptr);
+  m_pending_events.emplace_back (ptid, status);
+}
+
+/* See fbsd-nat.h.  */
+
+bool
+fbsd_nat_target::have_pending_event (ptid_t filter)
+{
+  for (const pending_event &event : m_pending_events)
+    if (event.ptid.matches (filter))
+      return true;
+  return false;
+}
+
+/* See fbsd-nat.h.  */
+
+gdb::optional<fbsd_nat_target::pending_event>
+fbsd_nat_target::take_pending_event ()
+{
+  for (auto it = m_pending_events.begin (); it != m_pending_events.end (); it++)
+    if (it->ptid.matches (m_resume_ptid))
+      {
+       pending_event event = *it;
+       m_pending_events.erase (it);
+       return event;
+      }
+  return {};
+}
+
 /* Return the name of a file that can be opened to get the symbols for
    the child process identified by PID.  */
 
 }
 
 #ifndef PTRACE_VFORK
-static std::forward_list<ptid_t> fbsd_pending_vfork_done;
-
 /* Record a pending vfork done event.  */
 
 static void
 fbsd_add_vfork_done (ptid_t pid)
 {
-  fbsd_pending_vfork_done.push_front (pid);
+  add_pending_event (ptid, target_waitstatus ().set_vfork_done ());
 
   /* If we're in async mode, need to tell the event loop there's
      something here to process.  */
   if (target_is_async_p ())
     async_file_mark ();
 }
-
-/* Check for a pending vfork done event for a specific PID.  */
-
-static int
-fbsd_is_vfork_done_pending (pid_t pid)
-{
-  for (auto it = fbsd_pending_vfork_done.begin ();
-       it != fbsd_pending_vfork_done.end (); it++)
-    if (it->pid () == pid)
-      return 1;
-  return 0;
-}
-
-/* Check for a pending vfork done event.  If one is found, remove it
-   from the list and return the PTID.  */
-
-static ptid_t
-fbsd_next_vfork_done (void)
-{
-  if (!fbsd_pending_vfork_done.empty ())
-    {
-      ptid_t ptid = fbsd_pending_vfork_done.front ();
-      fbsd_pending_vfork_done.pop_front ();
-      return ptid;
-    }
-  return null_ptid;
-}
 #endif
 #endif
 
 void
 fbsd_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
 {
-#if defined(TDP_RFPPWAIT) && !defined(PTRACE_VFORK)
-  pid_t pid;
-
-  /* Don't PT_CONTINUE a process which has a pending vfork done event.  */
-  if (minus_one_ptid == ptid)
-    pid = inferior_ptid.pid ();
-  else
-    pid = ptid.pid ();
-  if (fbsd_is_vfork_done_pending (pid))
-    return;
-#endif
-
   fbsd_nat_debug_printf ("[%s], step %d, signo %d (%s)",
                         target_pid_to_str (ptid).c_str (), step, signo,
                         gdb_signal_to_name (signo));
+
+  /* Don't PT_CONTINUE a thread or process which has a pending event.  */
+  m_resume_ptid = ptid;
+  if (have_pending_event (ptid))
+    {
+      fbsd_nat_debug_printf ("found pending event");
+      return;
+    }
+
   if (ptid.lwp_p ())
     {
       /* If ptid is a specific LWP, suspend all other LWPs in the process.  */
 
   while (1)
     {
-#ifndef PTRACE_VFORK
-      wptid = fbsd_next_vfork_done ();
-      if (wptid != null_ptid)
-       {
-         ourstatus->set_vfork_done ();
-         return wptid;
-       }
-#endif
       wptid = inf_ptrace_target::wait (ptid, ourstatus, target_options);
       if (ourstatus->kind () == TARGET_WAITKIND_STOPPED)
        {
 fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
                       target_wait_flags target_options)
 {
-  ptid_t wptid;
-
   fbsd_nat_debug_printf ("[%s], [%s]", target_pid_to_str (ptid).c_str (),
                         target_options_to_string (target_options).c_str ());
 
+  /* If there is a valid pending event, return it.  */
+  gdb::optional<pending_event> event = take_pending_event ();
+  if (event.has_value ())
+    {
+      fbsd_nat_debug_printf ("returning pending event [%s], [%s]",
+                            target_pid_to_str (event->ptid).c_str (),
+                            event->status.to_string ().c_str ());
+      gdb_assert (event->ptid.matches (ptid));
+      *ourstatus = event->status;
+      return event->ptid;
+    }
+
   /* Ensure any subsequent events trigger a new event in the loop.  */
   if (is_async_p ())
     async_file_flush ();
 
-  wptid = wait_1 (ptid, ourstatus, target_options);
+  ptid_t wptid = wait_1 (ptid, ourstatus, target_options);
 
   /* If we are in async mode and found an event, there may still be
      another event pending.  Trigger the event pipe so that that the
     (disable_randomization);
 #endif
 
+  /* Expect a wait for the new process.  */
+  m_resume_ptid = minus_one_ptid;
+  fbsd_nat_debug_printf ("setting resume_ptid to [%s]",
+                        target_pid_to_str (m_resume_ptid).c_str ());
   inf_ptrace_target::create_inferior (exec_file, allargs, env, from_tty);
 }
 
+void
+fbsd_nat_target::attach (const char *args, int from_tty)
+{
+  /* Expect a wait for the new process.  */
+  m_resume_ptid = minus_one_ptid;
+  fbsd_nat_debug_printf ("setting resume_ptid to [%s]",
+                        target_pid_to_str (m_resume_ptid).c_str ());
+  inf_ptrace_target::attach (args, from_tty);
+}
+
 #ifdef TDP_RFPPWAIT
 /* Target hook for follow_fork.  On entry and at return inferior_ptid is
    the ptid of the followed inferior.  */
 
 #ifndef FBSD_NAT_H
 #define FBSD_NAT_H
 
+#include "gdbsupport/gdb_optional.h"
 #include "inf-ptrace.h"
 #include "regcache.h"
 #include "regset.h"
 #include <osreldate.h>
 #include <sys/proc.h>
 
+#include <list>
+
 /* FreeBSD kernels 11.3 and later report valid si_code values for
    SIGTRAP on all architectures.  Older FreeBSD kernels that supported
    TRAP_BRKPT did not report valid values for MIPS and sparc64.  Even
   void create_inferior (const char *, const std::string &,
                        char **, int) override;
 
+  void attach (const char *, int) override;
+
   void resume (ptid_t, int, enum gdb_signal) override;
 
   ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
     return store_regset (regcache, regnum, note, regset, regbase, ®s,
                         sizeof (regs));
   }
+
+private:
+  /* If an event is triggered asynchronously (fake vfork_done events)
+     or occurs when the core is not expecting it, a pending event is
+     created.  This event is then returned by a future call to the
+     target wait method.  */
+
+  struct pending_event
+  {
+    pending_event (const ptid_t &_ptid, const target_waitstatus &_status) :
+      ptid (_ptid), status (_status) {}
+
+    ptid_t ptid;
+    target_waitstatus status;
+  };
+
+  /* Add a new pending event to the list.  */
+
+  void add_pending_event (const ptid_t &ptid, const target_waitstatus &status);
+
+  /* Return true if there is a pending event matching FILTER.  */
+
+  bool have_pending_event (ptid_t filter);
+
+  /* Helper method called by the target wait method.  Check if there
+     is a pending event matching M_RESUME_PTID.  If there is a
+     matching event, the event is removed from the pending list and
+     returned.  */
+
+  gdb::optional<pending_event> take_pending_event ();
+
+  /* List of pending events.  */
+
+  std::list<pending_event> m_pending_events;
+
+  /* Filter for ptid's allowed to report events from wait.  Normally
+     set in resume, but also reset to minus_one_ptid in
+     create_inferior and attach.  */
+
+  ptid_t m_resume_ptid;
 };
 
 /* Fetch the signal information for PTID and store it in *SIGINFO.