diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index 195c4feb49a..f114f88e649 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -47,6 +47,8 @@
 #include "fbsd-nat.h"
 #include "fbsd-tdep.h"
 
+#include <unordered_set>
+
 #ifndef PT_GETREGSET
 #define	PT_GETREGSET	42	/* Get a target register set */
 #define	PT_SETREGSET	43	/* Set a target register set */
@@ -55,17 +57,52 @@
 /* Information stored about each inferior.  */
 struct fbsd_inferior_info
 {
-  /* Filter for resumed LWPs which can report events from wait.  */
-  ptid_t resumed_lwps = null_ptid;
+  /* Needs to be resumed.  */
+  bool resume_pending = false;
+
+  /* Either all threads in a process are resumed, a subset of threads
+     are resumed, or the entire process is stopped.  For the first
+     case, resumed is true and resumed_lwps is empty.  For the second
+     case, resumed is false and resumed_lwps contains the set of LWPs
+     resumed.  For the third case, resumed is false and resumed_lwps
+     is empty.  */
+  bool resumed_all_lwps = false;
+  std::unordered_set<lwpid_t> resumed_lwps;
+
+  /* Thread to step on next resume.  -1 if no thread to step.  */
+  lwpid_t stepping_lwp = -1;
+
+  /* Signal to send on next resume.  */
+  enum gdb_signal resume_signal = GDB_SIGNAL_0;
 
   /* Number of LWPs this process contains.  */
   unsigned int num_lwps = 0;
 
-  /* Number of LWPs currently running.  */
+  /* Number of resumed LWPs currently running.  */
   unsigned int running_lwps = 0;
 
   /* Have a pending SIGSTOP event that needs to be discarded.  */
   bool pending_sigstop = false;
+
+  /* Return true if a specific LWP has been resumed.  */
+  bool is_lwp_resumed (lwpid_t lwpid)
+  {
+    return (resumed_all_lwps || resumed_lwps.count (lwpid) != 0);
+  }
+
+  /* Return true if any LWPs in the process have been resumed.  */
+  bool is_process_resumed ()
+  {
+    return (resumed_all_lwps || !resumed_lwps.empty ());
+  }
+
+  /* Mark the process as stopped with no threads resumed or running.  */
+  void mark_stopped ()
+  {
+    resumed_all_lwps = false;
+    resumed_lwps.clear ();
+    running_lwps = 0;
+  }
 };
 
 /* Per-inferior data key.  */
@@ -103,7 +140,7 @@ fbsd_nat_target::take_pending_event (ptid_t filter)
       {
 	inferior *inf = find_inferior_ptid (this, it->ptid);
 	fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
-	if (it->ptid.matches (info->resumed_lwps))
+	if (info->is_lwp_resumed (it->ptid.lwp ()))
 	  {
 	    pending_event event = *it;
 	    m_pending_events.erase (it);
@@ -798,7 +835,10 @@ show_fbsd_nat_debug (struct ui_file *file, int from_tty,
 #define fbsd_nat_debug_printf(fmt, ...) \
   debug_prefixed_printf_cond (debug_fbsd_nat, "fbsd-nat", fmt, ##__VA_ARGS__)
 
-#define fbsd_nat_debug_start_end(fmt, ...) \
+#define fbsd_nat_debug_enter_exit() \
+  scoped_debug_enter_exit (debug_fbsd_nat, "fbsd-nat")
+
+#define fbsd_nat_debug_start_end(fmt, ...)				\
   scoped_debug_start_end (debug_fbsd_nat, "fbsd-nat", fmt, ##__VA_ARGS__)
 
 /*
@@ -1177,11 +1217,11 @@ fbsd_add_vfork_done (ptid_t pid)
 #endif
 #endif
 
-/* Resume a single process.  */
+/* Mark a single process for resumption.  */
 
 void
-fbsd_nat_target::resume_one_process (ptid_t ptid, int step,
-				     enum gdb_signal signo)
+fbsd_nat_target::record_resume (ptid_t ptid, int step,
+				enum gdb_signal signo)
 {
   fbsd_nat_debug_printf ("[%s], step %d, signo %d (%s)",
 			 target_pid_to_str (ptid).c_str (), step, signo,
@@ -1189,80 +1229,39 @@ fbsd_nat_target::resume_one_process (ptid_t ptid, int step,
 
   inferior *inf = find_inferior_ptid (this, ptid);
   fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
-  info->resumed_lwps = ptid;
   gdb_assert (info->running_lwps == 0);
 
-  /* Don't PT_CONTINUE a thread or process which has a pending event.  */
-  if (have_pending_event (ptid))
+  if (!info->resume_pending)
     {
-      fbsd_nat_debug_printf ("found pending event");
-      return;
+      info->stepping_lwp = -1;
+      info->resume_signal = GDB_SIGNAL_0;
+      info->resume_pending = true;
     }
 
-  for (thread_info *tp : inf->non_exited_threads ())
+  if (ptid.lwp_p ())
     {
-      int request;
-
-      /* If ptid is a specific LWP, suspend all other LWPs in the
-	 process, otherwise resume all LWPs in the process..  */
-      if (!ptid.lwp_p() || tp->ptid.lwp () == ptid.lwp ())
-	{
-	  if (ptrace (PT_RESUME, tp->ptid.lwp (), NULL, 0) == -1)
-	    perror_with_name (("ptrace (PT_RESUME)"));
-	  low_prepare_to_resume (tp);
-	  info->running_lwps++;
-	}
-      else
-	{
-	  if (ptrace (PT_SUSPEND, tp->ptid.lwp (), NULL, 0) == -1)
-	    perror_with_name (("ptrace (PT_SUSPEND)"));
-	}
-    }
-
-  if (ptid.pid () != inferior_ptid.pid ())
-    {
-      step = 0;
-      signo = GDB_SIGNAL_0;
-      gdb_assert (!ptid.lwp_p ());
+      gdb_assert (!info->is_lwp_resumed (ptid.lwp ()));
+      info->resumed_lwps.insert (ptid.lwp ());
     }
   else
     {
-      ptid = inferior_ptid;
-#if __FreeBSD_version < 1200052
-      /* When multiple threads within a process wish to report STOPPED
-	 events from wait(), the kernel picks one thread event as the
-	 thread event to report.  The chosen thread event is retrieved
-	 via PT_LWPINFO by passing the process ID as the request pid.
-	 If multiple events are pending, then the subsequent wait()
-	 after resuming a process will report another STOPPED event
-	 after resuming the process to handle the next thread event
-	 and so on.
+      gdb_assert (!info->is_process_resumed ());
+      info->resumed_all_lwps = true;
+    }
 
-	 A single thread event is cleared as a side effect of resuming
-	 the process with PT_CONTINUE, PT_STEP, etc.  In older
-	 kernels, however, the request pid was used to select which
-	 thread's event was cleared rather than always clearing the
-	 event that was just reported.  To avoid clearing the event of
-	 the wrong LWP, always pass the process ID instead of an LWP
-	 ID to PT_CONTINUE or PT_SYSCALL.
-
-	 In the case of stepping, the process ID cannot be used with
-	 PT_STEP since it would step the thread that reported an event
-	 which may not be the thread indicated by PTID.  For stepping,
-	 use PT_SETSTEP to enable stepping on the desired thread
-	 before resuming the process via PT_CONTINUE instead of using
-	 PT_STEP.  */
+  if (ptid.pid () == inferior_ptid.pid ())
+    {
       if (step)
 	{
-	  if (ptrace (PT_SETSTEP, get_ptrace_pid (ptid), NULL, 0) == -1)
-	    perror_with_name (("ptrace (PT_SETSTEP)"));
-	  step = 0;
+	  gdb_assert (info->stepping_lwp == -1);
+	  info->stepping_lwp = inferior_ptid.lwp ();
+	}
+      if (signo != GDB_SIGNAL_0)
+	{
+	  gdb_assert (info->resume_signal == GDB_SIGNAL_0);
+	  info->resume_signal = signo;
 	}
-      ptid = ptid_t (ptid.pid ());
-#endif
     }
-
-  inf_ptrace_target::resume (ptid, step, signo);
 }
 
 /* Implement the "resume" target_ops method.  */
@@ -1280,14 +1279,94 @@ fbsd_nat_target::resume (ptid_t scope_ptid, int step, enum gdb_signal signo)
   if (scope_ptid == minus_one_ptid)
     {
       for (inferior *inf : all_non_exited_inferiors (this))
-	resume_one_process (ptid_t (inf->pid), step, signo);
+	record_resume (ptid_t (inf->pid), step, signo);
     }
   else
     {
-      resume_one_process (scope_ptid, step, signo);
+      record_resume (scope_ptid, step, signo);
     }
 }
 
+/* Resume a single process.  */
+
+void
+fbsd_nat_target::resume_one_process (inferior *inf)
+{
+  fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
+
+  if (!info->resume_pending)
+    return;
+  info->resume_pending = false;
+
+  ptid_t ptid (inf->pid);
+  fbsd_nat_debug_printf ("[%s], step LWP %d, signo %d (%s)",
+			 target_pid_to_str (ptid).c_str (),
+			 info->stepping_lwp, info->resume_signal,
+			 gdb_signal_to_name (info->resume_signal));
+
+  /* Enable stepping if requested.  Note that if a different resumed
+     thread in the process has a pending event the step might not
+     occur until a future resume.  */
+  if (info->stepping_lwp != -1)
+    {
+      gdb_assert (!have_pending_event (ptid_t (inf->pid, info->stepping_lwp)));
+      if (ptrace (PT_SETSTEP, info->stepping_lwp, NULL, 0) == -1)
+	perror_with_name (("ptrace (PT_SETSTEP)"));
+    }
+
+  /* Don't PT_CONTINUE a thread or process which has a pending event.  */
+  if (info->resumed_all_lwps)
+    {
+      if (have_pending_event (ptid))
+	{
+	  fbsd_nat_debug_printf ("found pending event");
+	  gdb_assert (info->resume_signal == GDB_SIGNAL_0);
+	  return;
+	}
+    }
+  else
+    for (lwpid_t lwp : info->resumed_lwps)
+      {
+	if (have_pending_event (ptid_t (inf->pid, lwp)))
+	  {
+	    fbsd_nat_debug_printf ("found pending event");
+	    gdb_assert (info->resume_signal == GDB_SIGNAL_0);
+	    return;
+	  }
+      }
+
+  gdb_assert (info->running_lwps == 0);
+
+  /* Suspend or resume individual threads.  */
+  for (thread_info *tp : inf->non_exited_threads ())
+    {
+      if (info->is_lwp_resumed (tp->ptid.lwp ()))
+	{
+	  if (ptrace (PT_RESUME, tp->ptid.lwp (), NULL, 0) == -1)
+	    perror_with_name (("ptrace (PT_RESUME)"));
+	  low_prepare_to_resume (tp);
+	  info->running_lwps++;
+	}
+      else
+	{
+	  if (ptrace (PT_SUSPEND, tp->ptid.lwp (), NULL, 0) == -1)
+	    perror_with_name (("ptrace (PT_SUSPEND)"));
+	}
+    }
+
+  inf_ptrace_target::resume (ptid, 0, info->resume_signal);
+}
+
+/* Implement the "commit_resumed" target_ops method.  */
+
+void
+fbsd_nat_target::commit_resumed ()
+{
+  fbsd_nat_debug_enter_exit ();
+  for (inferior *inf : all_non_exited_inferiors (this))
+    resume_one_process (inf);
+}
+
 #ifdef USE_SIGTRAP_SIGINFO
 /* Handle breakpoint and trace traps reported via SIGTRAP.  If the
    trap was a breakpoint or trace trap that should be reported to the
@@ -1397,18 +1476,23 @@ fbsd_nat_target::wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
 		  delete_thread (thr);
 		  info->num_lwps--;
 
-		  /* If this LWP was the only resumed LWP from the
-		     process, report an event to the core.  */
-		  if (wptid == info->resumed_lwps)
+		  /* During process exit LWPs that were suspended will
+		     report exit events which should be quietly
+		     ignored.  Only update accounting for exit events
+		     on resumed LWPs. */
+		  if (info->is_lwp_resumed (pl.pl_lwpid))
 		    {
-		      ourstatus->set_spurious ();
-		      return wptid;
-		    }
+		      /* If this LWP was the only resumed LWP from the
+			 process still running, report an event to the
+			 core.  */
+		      if (info->running_lwps == 1)
+			{
+			  ourstatus->set_spurious ();
+			  return wptid;
+			}
 
-		  /* During process exit LWPs that were not resumed
-		     will report exit events.  */
-		  if (wptid.matches (info->resumed_lwps))
-		    info->running_lwps--;
+		      info->running_lwps--;
+		    }
 		}
 	      if (ptrace (PT_CONTINUE, pid, (caddr_t) 1, 0) == -1)
 		perror_with_name (("ptrace (PT_CONTINUE)"));
@@ -1443,7 +1527,7 @@ fbsd_nat_target::wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
 		  add_thread (this, wptid);
 		  info->num_lwps++;
 
-		  if (wptid.matches(info->resumed_lwps))
+		  if (info->resumed_all_lwps)
 		    info->running_lwps++;
 		}
 	      ourstatus->set_spurious ();
@@ -1588,9 +1672,16 @@ fbsd_nat_target::stop_process (inferior *inf)
   fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
   gdb_assert (info != nullptr);
 
-  info->resumed_lwps = null_ptid;
   if (info->running_lwps == 0)
-    return;
+    {
+      /* If a resume is pending cancel it.  The process might be
+	 resumed even without any running LWPs if it had a pending
+	 event when it was resumed.  */
+      info->resume_pending = false;
+      info->mark_stopped ();
+      return;
+    }
+  gdb_assert (info->resume_pending == false);
 
   ptid_t ptid (inf->pid);
   target_waitstatus status;
@@ -1600,7 +1691,7 @@ fbsd_nat_target::stop_process (inferior *inf)
     {
       /* Save the current event as a pending event.  */
       add_pending_event (wptid, status);
-      info->running_lwps = 0;
+      info->mark_stopped ();
       return;
     }
 
@@ -1653,7 +1744,7 @@ fbsd_nat_target::stop_process (inferior *inf)
       info->pending_sigstop = true;
       break;
     }
-  info->running_lwps = 0;
+  info->mark_stopped ();
 }
 
 ptid_t
@@ -1663,6 +1754,15 @@ fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
   fbsd_nat_debug_printf ("[%s], [%s]", target_pid_to_str (ptid).c_str (),
 			 target_options_to_string (target_options).c_str ());
 
+  /* Resume any pending processes with a pending signal even if we are
+     going to return a pending event so as to not lose the signal.  */
+  for (inferior *inf : all_non_exited_inferiors (this))
+    {
+      fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
+      if (info->resume_pending && info->resume_signal != GDB_SIGNAL_0)
+	resume_one_process (inf);
+    }
+
   /* If there is a valid pending event, return it.  */
   gdb::optional<pending_event> event = take_pending_event (ptid);
   if (event.has_value ())
@@ -1683,6 +1783,9 @@ fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
   if (is_async_p ())
     async_file_flush ();
 
+  /* Resume any pending processes.  */
+  commit_resumed ();
+
   ptid_t wptid;
   while (1)
     {
@@ -1697,14 +1800,14 @@ fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
       gdb_assert (inf != nullptr);
       fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
       gdb_assert (info != nullptr);
-      gdb_assert (info->resumed_lwps != null_ptid);
+      gdb_assert (info->is_process_resumed ());
       gdb_assert (info->running_lwps > 0);
 
       /* If an event is reported for a thread or process while
 	 stepping some other thread, suspend the thread reporting the
 	 event and defer the event until it can be reported to the
 	 core.  */
-      if (!wptid.matches (info->resumed_lwps))
+      if (!info->is_lwp_resumed (wptid.lwp ()))
 	{
 	  add_pending_event (wptid, *ourstatus);
 	  fbsd_nat_debug_printf ("deferring event [%s], [%s]",
@@ -1718,8 +1821,7 @@ fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
 	}
 
       /* This process is no longer running.  */
-      info->resumed_lwps = null_ptid;
-      info->running_lwps = 0;
+      info->mark_stopped ();
 
       /* Stop any other inferiors currently running.  */
       for (inferior *inf : all_non_exited_inferiors (this))
@@ -1830,7 +1932,7 @@ fbsd_nat_target::create_inferior (const char *exec_file,
 #endif
 
   fbsd_inferior_info *info = fbsd_inferior_data.emplace (current_inferior ());
-  info->resumed_lwps = minus_one_ptid;
+  info->resumed_all_lwps = true;
   info->num_lwps = 1;
   info->running_lwps = 1;
   inf_ptrace_target::create_inferior (exec_file, allargs, env, from_tty);
@@ -1840,7 +1942,7 @@ void
 fbsd_nat_target::attach (const char *args, int from_tty)
 {
   fbsd_inferior_info *info = fbsd_inferior_data.emplace (current_inferior ());
-  info->resumed_lwps = minus_one_ptid;
+  info->resumed_all_lwps = true;
   info->num_lwps = 1;
   info->running_lwps = 1;
   inf_ptrace_target::attach (args, from_tty);
@@ -1889,12 +1991,12 @@ fbsd_nat_target::detach_fork_children (inferior *inf)
   for (thread_info *tp : inf->non_exited_threads ())
     detach_fork_children (tp);
 
-  /* Unwind state associated with any pending events.  Reset
-     info->resumed_lwps so that take_pending_event will harvest
+  /* Unwind state associated with any pending events.  Set
+     info->resumed_all_lwps so that take_pending_event will harvest
      events.  */
   fbsd_inferior_info *info = fbsd_inferior_data.get (inf);
   ptid_t ptid = ptid_t (inf->pid);
-  info->resumed_lwps = ptid;
+  info->resumed_all_lwps = true;
 
   while (1)
     {
@@ -2030,7 +2132,7 @@ fbsd_nat_target::detach (inferior *inf, int from_tty)
       info->pending_sigstop = false;
 
       /* Report event for all threads from wait_1.  */
-      info->resumed_lwps = ptid;
+      info->resumed_all_lwps = true;
 
       do
 	{
diff --git a/gdb/fbsd-nat.h b/gdb/fbsd-nat.h
index 7016cc0242a..b149217b71b 100644
--- a/gdb/fbsd-nat.h
+++ b/gdb/fbsd-nat.h
@@ -89,6 +89,8 @@ class fbsd_nat_target : public inf_ptrace_target
 
   void resume (ptid_t, int, enum gdb_signal) override;
 
+  void commit_resumed () override;
+
   ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
 
   void post_attach (int) override;
@@ -143,7 +145,9 @@ class fbsd_nat_target : public inf_ptrace_target
 private:
   ptid_t wait_1 (ptid_t, struct target_waitstatus *, target_wait_flags);
 
-  void resume_one_process (ptid_t, int, enum gdb_signal);
+  void record_resume (ptid_t, int, enum gdb_signal);
+
+  void resume_one_process (inferior *);
 
   void stop_process (inferior *);
 
@@ -258,9 +262,9 @@ class fbsd_nat_target : public inf_ptrace_target
 
   bool have_pending_event (ptid_t filter);
 
-  /* Check if there is a pending event for a resumed process matching
-     FILTER.  If there is a matching event, the event is removed from
-     the pending list and returned.  */
+  /* Check if there is a pending event for a resumed thread or process
+     matching FILTER.  If there is a matching event, the event is
+     removed from the pending list and returned.  */
 
   gdb::optional<pending_event> take_pending_event (ptid_t filter);
 
