diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 68c3c91..e260896 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,43 @@
 2017-04-19  Pedro Alves  <palves@redhat.com>
 
+	* common/refcounted-object.h: New file.
+	* gdbthread.h: Include "common/refcounted-object.h".
+	(thread_info): Inherit from refcounted_object and add comments.
+	(thread_info::incref, thread_info::decref)
+	(thread_info::m_refcount): Delete.
+	(thread_info::deletable): Use the refcounted_object::refcount()
+	method.
+	* inferior.c (current_inferior_): Add comment.
+	(set_current_inferior): Increment/decrement refcounts.
+	(prune_inferiors, remove_inferior_command): Skip inferiors marked
+	not-deletable instead of comparing with the current inferior.
+	(initialize_inferiors): Increment the initial inferior's refcount.
+	* inferior.h (struct inferior): Forward declare.
+	Include "common/refcounted-object.h".
+	(current_inferior, set_current_inferior): Move declaration to
+	before struct inferior's definition, and fix comment.
+	(inferior): Inherit from refcounted_object.  Add comments.
+	* thread.c (switch_to_thread_no_regs): Reference the thread's
+	inferior pointer directly instead of doing a ptid lookup.
+	(switch_to_no_thread): New function.
+	(switch_to_thread(thread_info *)): New function, factored out
+	from ...
+	(switch_to_thread(ptid_t)): ... this.
+	(restore_current_thread): Delete.
+	(current_thread_cleanup): Remove 'inf_id' and 'was_removable'
+	fields, and add 'inf' field.
+	(do_restore_current_thread_cleanup): Check whether old->inf is
+	alive instead of looking up an inferior by ptid.  Use
+	switch_to_thread and switch_to_no_thread.
+	(restore_current_thread_cleanup_dtor): Use old->inf directly
+	instead of lookup up an inferior by id.  Decref the inferior.
+	Don't restore 'removable'.
+	(make_cleanup_restore_current_thread): Same the inferior pointer
+	in old, instead of the inferior number.  Incref the inferior.
+	Don't save/clear 'removable'.
+
+2017-04-19  Pedro Alves  <palves@redhat.com>
+
 	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
 	unittests/scoped_restore-selftests.c.
 	(SUBDIR_UNITTESTS_OBS): Add scoped_restore-selftests.o.
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 42d6a8d..c4d5b79 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2017-04-19  Pedro Alves  <palves@redhat.com>
+
+	* gdb.threads/threadapply.exp (kill_and_remove_inferior): New
+	procedure.
+	(top level): Call it.
+	* lib/gdb.exp (gdb_define_cmd): New procedure.
+
 2017-04-12  Pedro Alves  <palves@redhat.com>
 
 	PR gdb/21323
diff --git a/gdb/common/refcounted-object.h b/gdb/common/refcounted-object.h
new file mode 100644
index 0000000..9d0ac10
--- /dev/null
+++ b/gdb/common/refcounted-object.h
@@ -0,0 +1,56 @@
+/* Base class of intrusively reference-counted objects.
+   Copyright (C) 2017 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/>.  */
+
+#ifndef REFCOUNTED_OBJECT_H
+#define REFCOUNTED_OBJECT_H
+
+/* Base class of intrusively reference-countable objects.
+   Incrementing and decrementing the reference count is an external
+   responsibility.  */
+
+class refcounted_object
+{
+public:
+  refcounted_object () = default;
+
+  /* Increase the refcount.  */
+  void incref ()
+  {
+    gdb_assert (m_refcount >= 0);
+    m_refcount++;
+  }
+
+  /* Decrease the refcount.  */
+  void decref ()
+  {
+    m_refcount--;
+    gdb_assert (m_refcount >= 0);
+  }
+
+  int refcount () const { return m_refcount; }
+
+private:
+  /* Disable copy.  */
+  refcounted_object (const refcounted_object &) = delete;
+  refcounted_object &operator=(const refcounted_object &) = delete;
+
+  /* The reference count.  */
+  int m_refcount = 0;
+};
+
+#endif /* REFCOUNTED_OBJECT_H */
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4cd7390..33977ee 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -31,6 +31,7 @@ struct symtab;
 #include "common/vec.h"
 #include "target/waitstatus.h"
 #include "cli/cli-utils.h"
+#include "common/refcounted-object.h"
 
 /* Frontend view of the thread state.  Possible extensions: stepping,
    finishing, until(ling),...  */
@@ -177,7 +178,24 @@ typedef struct value *value_ptr;
 DEF_VEC_P (value_ptr);
 typedef VEC (value_ptr) value_vec;
 
-struct thread_info
+/* Threads are intrusively refcounted objects.  Being the
+   user-selected thread is normally considered an implicit strong
+   reference and is thus not accounted in the refcount, unlike
+   inferior objects.  This is necessary, because there's no "current
+   thread" pointer.  Instead the current thread is inferred from the
+   inferior_ptid global.  However, when GDB needs to remember the
+   selected thread to later restore it, GDB bumps the thread object's
+   refcount, to prevent something deleting the thread object before
+   reverting back (e.g., due to a "kill" command.  If the thread
+   meanwhile exits before being re-selected, then the thread object is
+   left listed in the thread list, but marked with state
+   THREAD_EXITED.  (See make_cleanup_restore_current_thread and
+   delete_thread).  All other thread references are considered weak
+   references.  Placing a thread in the thread list is an implicit
+   strong reference, and is thus not accounted for in the thread's
+   refcount.  */
+
+class thread_info : public refcounted_object
 {
 public:
   explicit thread_info (inferior *inf, ptid_t ptid);
@@ -186,22 +204,8 @@ public:
   bool deletable () const
   {
     /* If this is the current thread, or there's code out there that
-       relies on it existing (m_refcount > 0) we can't delete yet.  */
-    return (m_refcount == 0 && !ptid_equal (ptid, inferior_ptid));
-  }
-
-  /* Increase the refcount.  */
-  void incref ()
-  {
-    gdb_assert (m_refcount >= 0);
-    m_refcount++;
-  }
-
-  /* Decrease the refcount.  */
-  void decref ()
-  {
-    m_refcount--;
-    gdb_assert (m_refcount >= 0);
+       relies on it existing (refcount > 0) we can't delete yet.  */
+    return (refcount () == 0 && !ptid_equal (ptid, inferior_ptid));
   }
 
   struct thread_info *next = NULL;
@@ -362,13 +366,6 @@ public:
      fields point to self.  */
   struct thread_info *step_over_prev = NULL;
   struct thread_info *step_over_next = NULL;
-
-private:
-
-  /* If this is > 0, then it means there's code out there that relies
-     on this thread being listed.  Don't delete it from the lists even
-     if we detect it exiting.  */
-  int m_refcount = 0;
 };
 
 /* Create an empty thread list, or empty the existing one.  */
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 327590a..e85859f 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -50,7 +50,9 @@ static int highest_inferior_num;
    `set print inferior-events'.  */
 static int print_inferior_events = 0;
 
-/* The Current Inferior.  */
+/* The Current Inferior.  This is a strong reference.  I.e., whenever
+   an inferior is the current inferior, its refcount is
+   incremented.  */
 static struct inferior *current_inferior_ = NULL;
 
 struct inferior*
@@ -65,6 +67,8 @@ set_current_inferior (struct inferior *inf)
   /* There's always an inferior.  */
   gdb_assert (inf != NULL);
 
+  inf->incref ();
+  current_inferior_->decref ();
   current_inferior_ = inf;
 }
 
@@ -483,13 +487,12 @@ void
 prune_inferiors (void)
 {
   struct inferior *ss, **ss_link;
-  struct inferior *current = current_inferior ();
 
   ss = inferior_list;
   ss_link = &inferior_list;
   while (ss)
     {
-      if (ss == current
+      if (!ss->deletable ()
 	  || !ss->removable
 	  || ss->pid != 0)
 	{
@@ -774,7 +777,7 @@ remove_inferior_command (char *args, int from_tty)
 	  continue;
 	}
 
-      if (inf == current_inferior ())
+      if (!inf->deletable ())
 	{
 	  warning (_("Can not remove current inferior %d."), num);
 	  continue;
@@ -1017,6 +1020,7 @@ initialize_inferiors (void)
      that.  Do this after initialize_progspace, due to the
      current_program_space reference.  */
   current_inferior_ = add_inferior (0);
+  current_inferior_->incref ();
   current_inferior_->pspace = current_program_space;
   current_inferior_->aspace = current_program_space->aspace;
   /* The architecture will be initialized shortly, by
diff --git a/gdb/inferior.h b/gdb/inferior.h
index e39a80c..c6fb9d3 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -32,6 +32,7 @@ struct terminal_info;
 struct target_desc_info;
 struct gdb_environ;
 struct continuation;
+struct inferior;
 
 /* For bpstat.  */
 #include "breakpoint.h"
@@ -46,6 +47,7 @@ struct continuation;
 #include "registry.h"
 
 #include "symfile-add-flags.h"
+#include "common/refcounted-object.h"
 
 struct infcall_suspend_state;
 struct infcall_control_state;
@@ -298,6 +300,11 @@ struct inferior_control_state
   enum stop_kind stop_soon;
 };
 
+/* Return a pointer to the current inferior.  */
+extern inferior *current_inferior ();
+
+extern void set_current_inferior (inferior *);
+
 /* GDB represents the state of each program execution with an object
    called an inferior.  An inferior typically corresponds to a process
    but is more general and applies also to targets that do not have a
@@ -305,14 +312,30 @@ struct inferior_control_state
    inferior, as does each attachment to an existing process.
    Inferiors have unique internal identifiers that are different from
    target process ids.  Each inferior may in turn have multiple
-   threads running in it.  */
-
-class inferior
+   threads running in it.
+
+   Inferiors are intrusively refcounted objects.  Unlike thread
+   objects, being the user-selected inferior is considered a strong
+   reference and is thus accounted for in the inferior object's
+   refcount (see set_current_inferior).  When GDB needs to remember
+   the selected inferior to later restore it, GDB temporarily bumps
+   the inferior object's refcount, to prevent something deleting the
+   inferior object before reverting back (e.g., due to a
+   "remove-inferiors" command (see
+   make_cleanup_restore_current_thread).  All other inferior
+   references are considered weak references.  Inferiors are always
+   listed exactly once in the inferior list, so placing an inferior in
+   the inferior list is an implicit, not counted strong reference.  */
+
+class inferior : public refcounted_object
 {
 public:
   explicit inferior (int pid);
   ~inferior ();
 
+  /* Returns true if we can delete this inferior.  */
+  bool deletable () const { return refcount () == 0; }
+
   /* Pointer to next inferior in singly-linked list of inferiors.  */
   struct inferior *next = NULL;
 
@@ -517,12 +540,6 @@ extern int number_of_live_inferiors (void);
    (not cores, not executables, real live processes).  */
 extern int have_live_inferiors (void);
 
-/* Return a pointer to the current inferior.  It is an error to call
-   this if there is no current inferior.  */
-extern struct inferior *current_inferior (void);
-
-extern void set_current_inferior (struct inferior *);
-
 extern struct cleanup *save_current_inferior (void);
 
 /* Traverse all inferiors.  */
diff --git a/gdb/testsuite/gdb.threads/threadapply.exp b/gdb/testsuite/gdb.threads/threadapply.exp
index 959e8b9..39687da 100644
--- a/gdb/testsuite/gdb.threads/threadapply.exp
+++ b/gdb/testsuite/gdb.threads/threadapply.exp
@@ -93,3 +93,138 @@ proc thr_apply_detach {thread_set} {
 foreach thread_set {"all" "1.1 1.2 1.3"} {
     thr_apply_detach $thread_set
 }
+
+# Test killing and removing inferiors from a command run via "thread
+# apply THREAD_SET".  THREAD_SET can be either "1.1", or "all".  GDB
+# used to mistakenly allow deleting the previously-selected inferior,
+# in some cases, leading to crashes.
+
+proc kill_and_remove_inferior {thread_set} {
+    global binfile
+    global gdb_prompt
+
+    # The test starts multiple inferiors, therefore non-extended
+    # remote is not supported.
+    if [target_info exists use_gdb_stub] {
+	unsupported "using gdb stub"
+	return
+    }
+
+    set any "\[^\r\n\]*"
+    set ws "\[ \t\]\+"
+
+    clean_restart ${binfile}
+
+    with_test_prefix "start inferior 1" {
+	runto_main
+    }
+
+    # Add and start inferior number NUM.
+    proc add_and_start_inferior {num} {
+	global binfile
+
+	# Start another inferior.
+	gdb_test "add-inferior" "Added inferior $num.*" \
+	    "add empty inferior $num"
+	gdb_test "inferior $num" "Switching to inferior $num.*" \
+	    "switch to inferior $num"
+	gdb_test "file ${binfile}" ".*" "load file in inferior $num"
+
+	with_test_prefix "start inferior $num" {
+	    runto_main
+	}
+    }
+
+    # Start another inferior.
+    add_and_start_inferior 2
+
+    # And yet another.
+    add_and_start_inferior 3
+
+    gdb_test "thread 2.1" "Switching to thread 2.1 .*"
+
+    # Try removing an active inferior via a "thread apply" command.
+    # Should fail/warn.
+    with_test_prefix "try remove" {
+
+	gdb_define_cmd "remove" {
+	    "remove-inferiors 3"
+	}
+
+	# Inferior 3 is still alive, so can't remove it.
+	gdb_test "thread apply $thread_set remove" \
+	    "warning: Can not remove active inferior 3.*"
+	# Check that GDB restored the selected thread.
+	gdb_test "thread" "Current thread is 2.1 .*"
+
+	gdb_test "info inferiors" \
+	    [multi_line \
+		 "${ws}1${ws}process ${any}" \
+		 "\\\* 2${ws}process ${any}" \
+		 "${ws}3${ws}process ${any}" \
+		]
+    }
+
+    # Kill and try to remove inferior 2 while inferior 2 is selected.
+    # Removing the inferior should fail/warn.
+    with_test_prefix "try kill-and-remove" {
+
+	# The "inferior 1" command works around PR gdb/19318 ("kill
+	# inferior N" shouldn't switch to inferior N).
+	gdb_define_cmd "kill-and-remove" {
+	    "kill inferiors 2"
+	    "inferior 1"
+	    "remove-inferiors 2"
+	}
+
+	# Note that when threads=1.1, this makes sure we're really
+	# testing failure to remove the inferior the user had selected
+	# before the "thread apply" command, instead of testing
+	# refusal to remove the currently-iterated inferior.
+	gdb_test "thread apply $thread_set kill-and-remove" \
+	    "warning: Can not remove current inferior 2.*"
+	gdb_test "thread" "No thread selected" \
+	    "switched to no thread selected"
+
+	gdb_test "info inferiors" \
+	    [multi_line \
+		 "${ws}1${ws}process ${any}" \
+		 "\\\* 2${ws}<null>${any}" \
+		 "${ws}3${ws}process ${any}" \
+		]
+    }
+
+    # Try removing (the now dead) inferior 2 while inferior 1 is
+    # selected.  This should succeed.
+    with_test_prefix "try remove 2" {
+
+	gdb_test "thread 1.1" "Switching to thread 1.1 .*"
+
+	gdb_define_cmd "remove-again" {
+	    "remove-inferiors 2"
+	}
+
+	set test "thread apply $thread_set remove-again"
+	gdb_test_multiple $test $test {
+	    -re "warning: Can not remove.*$gdb_prompt $" {
+		fail $test
+	    }
+	    -re "$gdb_prompt $" {
+		pass $test
+	    }
+	}
+	gdb_test "thread" "Current thread is 1.1 .*"
+	# Check that only inferiors 1 and 3 are around.
+	gdb_test "info inferiors" \
+	    [multi_line \
+		 "\\\* 1${ws}process ${any}" \
+		 "${ws}3${ws}process ${any}" \
+		]
+    }
+}
+
+# Test both "all" and a thread list, because those are implemented as
+# different commands in GDB.
+foreach_with_prefix thread_set {"all" "1.1"} {
+    kill_and_remove_inferior $thread_set
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index ea77361..6633d24 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6070,5 +6070,24 @@ proc dejagnu_version { } {
     return $dg_ver
 }
 
+# Define user-defined command COMMAND using the COMMAND_LIST as the
+# command's definition.  The terminating "end" is added automatically.
+
+proc gdb_define_cmd {command command_list} {
+    global gdb_prompt
+
+    set input [multi_line_input {*}$command_list "end"]
+    set test "define $command"
+
+    gdb_test_multiple "define $command" $test {
+	-re "End with"  {
+	    gdb_test_multiple $input $test {
+		-re "\r\n$gdb_prompt " {
+		}
+	    }
+	}
+    }
+}
+
 # Always load compatibility stuff.
 load_lib future.exp
diff --git a/gdb/thread.c b/gdb/thread.c
index 88fd521..e4113c2 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -68,7 +68,6 @@ static void thread_apply_all_command (char *, int);
 static int thread_alive (struct thread_info *);
 static void info_threads_command (char *, int);
 static void thread_apply_command (char *, int);
-static void restore_current_thread (ptid_t);
 
 /* RAII type used to increase / decrease the refcount of each thread
    in a given list of threads.  */
@@ -1458,10 +1457,8 @@ info_threads_command (char *arg, int from_tty)
 void
 switch_to_thread_no_regs (struct thread_info *thread)
 {
-  struct inferior *inf;
+  struct inferior *inf = thread->inf;
 
-  inf = find_inferior_ptid (thread->ptid);
-  gdb_assert (inf != NULL);
   set_current_program_space (inf->pspace);
   set_current_inferior (inf);
 
@@ -1469,45 +1466,50 @@ switch_to_thread_no_regs (struct thread_info *thread)
   stop_pc = ~(CORE_ADDR) 0;
 }
 
-/* Switch from one thread to another.  */
+/* Switch to no thread selected.  */
 
-void
-switch_to_thread (ptid_t ptid)
+static void
+switch_to_no_thread ()
 {
-  /* Switch the program space as well, if we can infer it from the now
-     current thread.  Otherwise, it's up to the caller to select the
-     space it wants.  */
-  if (ptid != null_ptid)
-    {
-      struct inferior *inf;
+  if (inferior_ptid == null_ptid)
+    return;
 
-      inf = find_inferior_ptid (ptid);
-      gdb_assert (inf != NULL);
-      set_current_program_space (inf->pspace);
-      set_current_inferior (inf);
-    }
+  inferior_ptid = null_ptid;
+  reinit_frame_cache ();
+  stop_pc = ~(CORE_ADDR) 0;
+}
+
+/* Switch from one thread to another.  */
 
-  if (ptid == inferior_ptid)
+static void
+switch_to_thread (thread_info *thr)
+{
+  gdb_assert (thr != NULL);
+
+  if (inferior_ptid == thr->ptid)
     return;
 
-  inferior_ptid = ptid;
+  switch_to_thread_no_regs (thr);
+
   reinit_frame_cache ();
 
   /* We don't check for is_stopped, because we're called at times
      while in the TARGET_RUNNING state, e.g., while handling an
      internal event.  */
-  if (inferior_ptid != null_ptid
-      && !is_exited (ptid)
-      && !is_executing (ptid))
-    stop_pc = regcache_read_pc (get_thread_regcache (ptid));
-  else
-    stop_pc = ~(CORE_ADDR) 0;
+  if (thr->state != THREAD_EXITED
+      && !thr->executing)
+    stop_pc = regcache_read_pc (get_thread_regcache (thr->ptid));
 }
 
-static void
-restore_current_thread (ptid_t ptid)
+/* See gdbthread.h.  */
+
+void
+switch_to_thread (ptid_t ptid)
 {
-  switch_to_thread (ptid);
+  if (ptid == null_ptid)
+    switch_to_no_thread ();
+  else
+    switch_to_thread (find_thread_ptid (ptid));
 }
 
 static void
@@ -1578,8 +1580,7 @@ struct current_thread_cleanup
   struct frame_id selected_frame_id;
   int selected_frame_level;
   int was_stopped;
-  int inf_id;
-  int was_removable;
+  inferior *inf;
 };
 
 static void
@@ -1595,12 +1596,12 @@ do_restore_current_thread_cleanup (void *arg)
       /* If the previously selected thread belonged to a process that has
 	 in the mean time exited (or killed, detached, etc.), then don't revert
 	 back to it, but instead simply drop back to no thread selected.  */
-      && find_inferior_ptid (old->thread->ptid) != NULL)
-    restore_current_thread (old->thread->ptid);
+      && old->inf->pid != 0)
+    switch_to_thread (old->thread);
   else
     {
-      restore_current_thread (null_ptid);
-      set_current_inferior (find_inferior_id (old->inf_id));
+      switch_to_no_thread ();
+      set_current_inferior (old->inf);
     }
 
   /* The running state of the originally selected thread may have
@@ -1619,15 +1620,11 @@ static void
 restore_current_thread_cleanup_dtor (void *arg)
 {
   struct current_thread_cleanup *old = (struct current_thread_cleanup *) arg;
-  struct thread_info *tp;
-  struct inferior *inf;
 
   if (old->thread != NULL)
     old->thread->decref ();
 
-  inf = find_inferior_id (old->inf_id);
-  if (inf != NULL)
-    inf->removable = old->was_removable;
+  old->inf->decref ();
   xfree (old);
 }
 
@@ -1637,8 +1634,7 @@ make_cleanup_restore_current_thread (void)
   struct current_thread_cleanup *old = XNEW (struct current_thread_cleanup);
 
   old->thread = NULL;
-  old->inf_id = current_inferior ()->num;
-  old->was_removable = current_inferior ()->removable;
+  old->inf = current_inferior ();
 
   if (inferior_ptid != null_ptid)
     {
@@ -1670,7 +1666,7 @@ make_cleanup_restore_current_thread (void)
       old->thread = tp;
     }
 
-  current_inferior ()->removable = 0;
+  old->inf->incref ();
 
   return make_cleanup_dtor (do_restore_current_thread_cleanup, old,
 			    restore_current_thread_cleanup_dtor);
