diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d1e0b00..895ece6 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -876,7 +876,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	stabsread.c stack.c probe.c stap-probe.c std-regs.c \
 	symfile.c symfile-debug.c symfile-mem.c symmisc.c symtab.c \
 	target.c target-dcache.c target-descriptions.c target-memory.c \
-	thread.c top.c tracepoint.c \
+	tid-parse.c thread.c top.c tracepoint.c \
 	trad-frame.c \
 	tramp-frame.c \
 	typeprint.c \
@@ -987,7 +987,8 @@ common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
 common/common-exceptions.h target/target.h common/symbol.h \
 common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \
 common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h \
-nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h
+nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \
+tid-parse.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
@@ -1029,7 +1030,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	linespec.o dictionary.o namespace.o \
 	location.o infcall.o \
 	infcmd.o infrun.o \
-	expprint.o environ.o stack.o thread.o thread-fsm.o \
+	expprint.o environ.o stack.o tid-parse.o thread.o thread-fsm.o \
 	exceptions.o \
 	extension.o \
 	filesystem.o \
diff --git a/gdb/NEWS b/gdb/NEWS
index 57a6c71..03944383 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,37 @@
 
 *** Changes since GDB 7.10
 
+* Per-inferior thread numbers
+
+  Thread numbers are now per inferior instead of global.  If you're
+  debugging multiple inferiors, GDB displays thread IDs using a
+  qualified INF_NUM.THR_NUM form.  For example:
+
+     (gdb) info threads
+       Id   Target Id         Frame
+       1.1  Thread 0x7ffff7fc2740 (LWP 8155) (running)
+       1.2  Thread 0x7ffff7fc1700 (LWP 8168) (running)
+     * 2.1  Thread 0x7ffff7fc2740 (LWP 8157) (running)
+       2.2  Thread 0x7ffff7fc1700 (LWP 8190) (running)
+
+  As consequence, thread numbers as visible in the $_thread
+  convenience variable and in Python's InferiorThread.num attribute
+  are no longer unique between inferiors.
+
+  GDB now maintains a second thread ID per thread, referred to as the
+  global thread ID, which is the new equivalent of thread numbers in
+  previous releases.
+
+  For backwards compatibility, MI's thread IDs always refer to global
+  IDs.
+
+* Commands that accept thread IDs now accept the qualified
+  INF_NUM.THR_NUM form as well.  For example:
+
+     (gdb) thread 2.1
+     [Switching to thread 2.1 (Thread 0x7ffff7fc2740 (LWP 8157))] (running)
+     (gdb)
+
 * The new convenience variable $_inferior holds the number of the
   current inferior.
 
@@ -153,6 +184,9 @@ show remote exec-event-feature-packet
 
 * Python Scripting
 
+  ** The "num" attribute of gdb.InferiorThread objects now refers to
+     the thread's per-inferior number.  See "Per-inferior thread
+     numbers" above.
   ** gdb.InferiorThread objects have a new attribute "inferior", which
      is the Inferior object the thread belongs to.
 
diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c
index 062c372..c067ae6 100644
--- a/gdb/ada-tasks.c
+++ b/gdb/ada-tasks.c
@@ -1102,7 +1102,7 @@ print_ada_task_info (struct ui_out *uiout,
       /* Print the associated Thread ID.  */
       if (ui_out_is_mi_like_p (uiout))
         {
-	  const int thread_id = pid_to_thread_id (task_info->ptid);
+	  const int thread_id = ptid_to_global_thread_id (task_info->ptid);
 
 	  if (thread_id != 0)
 	    ui_out_field_int (uiout, "thread-id", thread_id);
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b2a3c47..72da4ef 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -69,6 +69,7 @@
 #include "format.h"
 #include "location.h"
 #include "thread-fsm.h"
+#include "tid-parse.h"
 
 /* readline include files */
 #include "readline/readline.h"
@@ -3144,7 +3145,7 @@ insert_breakpoint_locations (void)
 	 the thread no longer exists.  ALL_BP_LOCATIONS bp_location
 	 has BL->OWNER always non-NULL.  */
       if (bl->owner->thread != -1
-	  && !valid_thread_id (bl->owner->thread))
+	  && !valid_global_thread_id (bl->owner->thread))
 	continue;
 
       switch_to_program_space_and_thread (bl->pspace);
@@ -3244,7 +3245,7 @@ remove_threaded_breakpoints (struct thread_info *tp, int silent)
 
   ALL_BREAKPOINTS_SAFE (b, b_tmp)
     {
-      if (b->thread == tp->num && user_breakpoint_p (b))
+      if (b->thread == tp->global_num && user_breakpoint_p (b))
 	{
 	  b->disposition = disp_del_at_next_stop;
 
@@ -5447,7 +5448,7 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
   /* If this is a thread/task-specific breakpoint, don't waste cpu
      evaluating the condition if this isn't the specified
      thread/task.  */
-  if ((b->thread != -1 && b->thread != pid_to_thread_id (ptid))
+  if ((b->thread != -1 && b->thread != ptid_to_global_thread_id (ptid))
       || (b->task != 0 && b->task != ada_get_task_number (ptid)))
 
     {
@@ -6514,11 +6515,16 @@ print_one_breakpoint_location (struct breakpoint *b,
 
   if (!part_of_multiple && b->thread != -1)
     {
-      struct thread_info *thr = find_thread_id (b->thread);
-
       /* FIXME should make an annotation for this.  */
       ui_out_text (uiout, "\tstop only in thread ");
-      ui_out_field_string (uiout, "thread", print_thread_id (thr));
+      if (ui_out_is_mi_like_p (uiout))
+	ui_out_field_int (uiout, "thread", b->thread);
+      else
+	{
+	  struct thread_info *thr = find_thread_global_id (b->thread);
+
+	  ui_out_field_string (uiout, "thread", print_thread_id (thr));
+	}
       ui_out_text (uiout, "\n");
     }
   
@@ -7574,7 +7580,7 @@ void
 set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
 {
   struct breakpoint *b, *b_tmp;
-  int thread = tp->num;
+  int thread = tp->global_num;
 
   /* To avoid having to rescan all objfile symbols at every step,
      we maintain a list of continually-inserted but always disabled
@@ -7643,7 +7649,7 @@ set_longjmp_breakpoint_for_call_dummy (void)
 	new_b = momentary_breakpoint_from_master (b, bp_longjmp_call_dummy,
 						  &momentary_breakpoint_ops,
 						  1);
-	new_b->thread = pid_to_thread_id (inferior_ptid);
+	new_b->thread = ptid_to_global_thread_id (inferior_ptid);
 
 	/* Link NEW_B into the chain of RETVAL breakpoints.  */
 
@@ -7673,7 +7679,7 @@ check_longjmp_breakpoint_for_call_dummy (struct thread_info *tp)
   struct breakpoint *b, *b_tmp;
 
   ALL_BREAKPOINTS_SAFE (b, b_tmp)
-    if (b->type == bp_longjmp_call_dummy && b->thread == tp->num)
+    if (b->type == bp_longjmp_call_dummy && b->thread == tp->global_num)
       {
 	struct breakpoint *dummy_b = b->related_breakpoint;
 
@@ -8893,7 +8899,7 @@ set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal,
      momentary breakpoints to be active in only a single thread of
      control.  */
   if (in_thread_list (inferior_ptid))
-    b->thread = pid_to_thread_id (inferior_ptid);
+    b->thread = ptid_to_global_thread_id (inferior_ptid);
 
   update_global_location_list_nothrow (UGLL_MAY_INSERT);
 
@@ -9567,14 +9573,6 @@ check_fast_tracepoint_sals (struct gdbarch *gdbarch,
     }
 }
 
-/* Issue an invalid thread ID error.  */
-
-static void ATTRIBUTE_NORETURN
-invalid_thread_id_error (int id)
-{
-  error (_("Unknown thread %d."), id);
-}
-
 /* Given TOK, a string specification of condition and thread, as
    accepted by the 'break' command, extract the condition
    string and thread number and set *COND_STRING and *THREAD.
@@ -9623,14 +9621,14 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc,
 	}
       else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
 	{
-	  char *tmptok;
+	  const char *tmptok;
+	  struct thread_info *thr;
 
 	  tok = end_tok + 1;
-	  *thread = strtol (tok, &tmptok, 0);
+	  thr = parse_thread_id (tok, &tmptok);
 	  if (tok == tmptok)
 	    error (_("Junk after thread keyword."));
-	  if (!valid_thread_id (*thread))
-	    invalid_thread_id_error (*thread);
+	  *thread = thr->global_num;
 	  tok = tmptok;
 	}
       else if (toklen >= 1 && strncmp (tok, "task", toklen) == 0)
@@ -11135,25 +11133,23 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
 
 	  if (toklen == 6 && startswith (tok, "thread"))
 	    {
+	      struct thread_info *thr;
 	      /* At this point we've found a "thread" token, which means
 		 the user is trying to set a watchpoint that triggers
 		 only in a specific thread.  */
-	      char *endp;
+	      const char *endp;
 
 	      if (thread != -1)
 		error(_("You can specify only one thread."));
 
 	      /* Extract the thread ID from the next token.  */
-	      thread = strtol (value_start, &endp, 0);
+	      thr = parse_thread_id (value_start, &endp);
 
-	      /* Check if the user provided a valid numeric value for the
-		 thread ID.  */
+	      /* Check if the user provided a valid thread ID.  */
 	      if (*endp != ' ' && *endp != '\t' && *endp != '\0')
-		error (_("Invalid thread ID specification %s."), value_start);
+		invalid_thread_id_error (value_start);
 
-	      /* Check if the thread actually exists.  */
-	      if (!valid_thread_id (thread))
-		invalid_thread_id_error (thread);
+	      thread = thr->global_num;
 	    }
 	  else if (toklen == 4 && startswith (tok, "mask"))
 	    {
@@ -11694,7 +11690,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
   resolve_sal_pc (&sal);
 
   tp = inferior_thread ();
-  thread = tp->num;
+  thread = tp->global_num;
 
   old_chain = make_cleanup (null_cleanup, NULL);
 
@@ -11744,7 +11740,8 @@ until_break_command (char *arg, int from_tty, int anywhere)
 						    stack_frame_id, bp_until);
   make_cleanup_delete_breakpoint (location_breakpoint);
 
-  sm = new_until_break_fsm (tp->num, location_breakpoint, caller_breakpoint);
+  sm = new_until_break_fsm (tp->global_num,
+			    location_breakpoint, caller_breakpoint);
   tp->thread_fsm = &sm->thread_fsm;
 
   discard_cleanups (old_chain);
@@ -13366,7 +13363,7 @@ momentary_bkpt_print_mention (struct breakpoint *b)
 static void
 longjmp_bkpt_dtor (struct breakpoint *self)
 {
-  struct thread_info *tp = find_thread_id (self->thread);
+  struct thread_info *tp = find_thread_global_id (self->thread);
 
   if (tp)
     tp->initiating_frame = null_frame_id;
@@ -14547,7 +14544,7 @@ breakpoint_re_set_thread (struct breakpoint *b)
   if (b->thread != -1)
     {
       if (in_thread_list (inferior_ptid))
-	b->thread = pid_to_thread_id (inferior_ptid);
+	b->thread = ptid_to_global_thread_id (inferior_ptid);
 
       /* We're being called after following a fork.  The new fork is
 	 selected as current, and unless this was a vfork will have a
@@ -15058,7 +15055,7 @@ insert_single_step_breakpoint (struct gdbarch *gdbarch,
   if (tp->control.single_step_breakpoints == NULL)
     {
       tp->control.single_step_breakpoints
-	= new_single_step_breakpoint (tp->num, gdbarch);
+	= new_single_step_breakpoint (tp->global_num, gdbarch);
     }
 
   sal = find_pc_line (pc, 0);
diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index 08b099f..a68b67d 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -23,16 +23,9 @@
 
 #include <ctype.h>
 
-/* *PP is a string denoting a number.  Get the number of the.  Advance
-   *PP after the string and any trailing whitespace.
-
-   Currently the string can either be a number, or "$" followed by the
-   name of a convenience variable, or ("$" or "$$") followed by digits.
-
-   TRAILER is a character which can be found after the number; most
-   commonly this is `-'.  If you don't want a trailer, use \0.  */
+/* See documentation in cli-utils.h.  */
 
-static int
+int
 get_number_trailer (const char **pp, int trailer)
 {
   int retval = 0;	/* default */
diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h
index ace46b4..ebf11f2 100644
--- a/gdb/cli/cli-utils.h
+++ b/gdb/cli/cli-utils.h
@@ -20,11 +20,18 @@
 #ifndef CLI_UTILS_H
 #define CLI_UTILS_H
 
-/* *PP is a string denoting a number.  Get the number of the.  Advance
-   *PP after the string and any trailing whitespace.
+/* *PP is a string denoting a number.  Get the number.  Advance *PP
+   after the string and any trailing whitespace.
 
-   Currently the string can either be a number,  or "$" followed by the
-   name of a convenience variable, or ("$" or "$$") followed by digits.  */
+   The string can either be a number, or "$" followed by the name of a
+   convenience variable, or ("$" or "$$") followed by digits.
+
+   TRAILER is a character which can be found after the number; most
+   commonly this is `-'.  If you don't want a trailer, use \0.  */
+
+extern int get_number_trailer (const char **pp, int trailer);
+
+/* Convenience.  Like get_number_trailer, but with no TRAILER.  */
 
 extern int get_number_const (const char **);
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f01b703..1bf2208 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2838,9 +2838,9 @@ programs:
 
 @itemize @bullet
 @item automatic notification of new threads
-@item @samp{thread @var{threadno}}, a command to switch among threads
+@item @samp{thread @var{thread-id}}, a command to switch among threads
 @item @samp{info threads}, a command to inquire about existing threads
-@item @samp{thread apply [@var{threadno}] [@var{all}] @var{args}},
+@item @samp{thread apply [@var{thread-id}] [@var{all}] @var{args}},
 a command to apply a command to a list of threads
 @item thread-specific breakpoints
 @item @samp{set print thread-events}, which controls printing of 
@@ -2886,26 +2886,77 @@ further qualifier.
 @c         multithread systems permit starting a program with multiple
 @c         threads ab initio?
 
-@cindex thread number
+@anchor{thread numbers}
+@cindex thread number, per inferior
 @cindex thread identifier (GDB)
-For debugging purposes, @value{GDBN} associates its own thread
-number---always a single integer---with each thread in your program.
+For debugging purposes, @value{GDBN} associates its own thread number
+---always a single integer---with each thread of an inferior.  This
+number is unique between all threads of an inferior, but not unique
+between threads of different inferiors.
+
+@cindex qualified thread ID
+You can refer to a given thread in an inferior using the qualified
+@var{inferior-num}.@var{thread-num} syntax, also known as
+@dfn{qualified thread ID}, with @var{inferior-num} being the inferior
+number and @var{thread-num} being the thread number of the given
+inferior.  For example, thread @code{2.3} refers to thread number 3 of
+inferior 2.  If you omit @var{inferior-num} (e.g., @code{thread 3}),
+then @value{GDBN} infers you're referring to a thread of the current
+inferior.
+
+Until you create a second inferior, @value{GDBN} does not show the
+@var{inferior-num} part of thread IDs, even though you can always use
+the full @var{inferior-num}.@var{thread-num} form to refer to threads
+of inferior 1, the initial inferior.
+
+@anchor{thread ID lists}
+@cindex thread ID lists
+Some commands accept a space-separated @dfn{thread ID list} as
+argument.  A list element can be a thread ID as shown in the first
+field of the @samp{info threads} display, with or without an inferior
+qualifier (e.g., @samp{2.1} or @samp{1}); or can be a range of thread
+numbers, again with or without an inferior qualifier, as in
+@var{inf1}.@var{thr1}-@var{thr2} or @var{thr1}-@var{thr2} (e.g.,
+@samp{1.2-4} or @samp{2-4}).  For example, if the current inferior is
+1, the thread list @samp{1 2-3 4.5 6.7-9} includes threads 1 to 3 of
+inferior 1, thread 5 of inferior 4 and threads 7 to 9 of inferior 6.
+That is, in expanded qualified form, the same as @samp{1.1 1.2 1.3 4.5
+6.7 6.8 6.9}.
+
+@anchor{global thread numbers}
+@cindex global thread number
+@cindex global thread identifier (GDB)
+In addition to a @emph{per-inferior} number, each thread is also
+assigned a unique @emph{global} number, also known as @dfn{global
+thread ID}, a single integer.  Unlike the thread number component of
+the thread ID, no two threads have the same global ID, even when
+you're debugging multiple inferiors.
 
 From @value{GDBN}'s perspective, a process always has at least one
 thread.  In other words, @value{GDBN} assigns a thread number to the
 program's ``main thread'' even if the program is not multi-threaded.
 
+@vindex $_thread@r{, convenience variable}
+The debugger convenience variable @samp{$_thread} contains the
+per-inferior thread number of the current thread.  You may find this
+useful in writing breakpoint conditional expressions, command scripts,
+and so forth.  @xref{Convenience Vars,, Convenience Variables}, for
+general information on convenience variables.
+
 @table @code
 @kindex info threads
-@item info threads @r{[}@var{id}@dots{}@r{]}
-Display a summary of all threads currently in your program.  Optional 
-argument @var{id}@dots{} is one or more thread ids separated by spaces, and
-means to print information only about the specified thread or threads.
+@item info threads @r{[}@var{thread-id-list}@r{]}
+
+Display information about one or more threads.  With no arguments
+displays information about all threads.  You can specify the list of
+threads that you want to display using the thread ID list syntax
+(@pxref{thread ID lists}).
+
 @value{GDBN} displays for each thread (in this order):
 
 @enumerate
 @item
-the thread number assigned by @value{GDBN}
+the per-inferior thread number assigned by @value{GDBN}
 
 @item
 the target system's thread identifier (@var{systag})
@@ -2936,6 +2987,19 @@ For example,
     at threadtest.c:68
 @end smallexample
 
+If you're debugging multiple inferiors, @value{GDBN} displays thread
+IDs using the qualified @var{inferior-num}.@var{thread-num} format.
+Otherwise, only @var{thread-num} is shown:
+
+@smallexample
+(@value{GDBP}) info threads
+  Id   Target Id             Frame
+  1.1  process 35 thread 13  main (argc=1, argv=0x7ffffff8)
+  1.2  process 35 thread 23  0x34e5 in sigpause ()
+  1.3  process 35 thread 27  0x34e5 in sigpause ()
+* 2.1  process 65 thread 1   main (argc=1, argv=0x7ffffff8)
+@end smallexample
+
 On Solaris, you can display more information about user threads with a
 Solaris-specific command:
 
@@ -2947,13 +3011,15 @@ Display info on Solaris user threads.
 @end table
 
 @table @code
-@kindex thread @var{threadno}
-@item thread @var{threadno}
-Make thread number @var{threadno} the current thread.  The command
-argument @var{threadno} is the internal @value{GDBN} thread number, as
-shown in the first field of the @samp{info threads} display.
-@value{GDBN} responds by displaying the system identifier of the thread
-you selected, and its current stack frame summary:
+@kindex thread @var{thread-id}
+@item thread @var{thread-id}
+Make thread ID @var{thread-id} the current thread.  The command
+argument @var{thread-id} is the @value{GDBN} thread ID, as shown in
+the first field of the @samp{info threads} display, with or without an
+inferior qualifier (e.g., @samp{2.1} or @samp{1}).
+
+@value{GDBN} responds by displaying the system identifier of the
+thread you selected, and its current stack frame summary:
 
 @smallexample
 (@value{GDBP}) thread 2
@@ -2967,23 +3033,14 @@ As with the @samp{[New @dots{}]} message, the form of the text after
 @samp{Switching to} depends on your system's conventions for identifying
 threads.
 
-@vindex $_thread@r{, convenience variable}
-The debugger convenience variable @samp{$_thread} contains the number
-of the current thread.  You may find this useful in writing breakpoint
-conditional expressions, command scripts, and so forth.  See
-@xref{Convenience Vars,, Convenience Variables}, for general
-information on convenience variables.
-
 @kindex thread apply
 @cindex apply command to several threads
-@item thread apply [@var{threadno} | all [-ascending]] @var{command}
+@item thread apply [@var{thread-id-list} | all [-ascending]] @var{command}
 The @code{thread apply} command allows you to apply the named
-@var{command} to one or more threads.  Specify the numbers of the
-threads that you want affected with the command argument
-@var{threadno}.  It can be a single thread number, one of the numbers
-shown in the first field of the @samp{info threads} display; or it
-could be a range of thread numbers, as in @code{2-4}.  To apply
-a command to all threads in descending order, type @kbd{thread apply all
+@var{command} to one or more threads.  Specify the threads that you
+want affected using the thread ID list syntax (@pxref{thread ID
+lists}), or specify @code{all} to apply to all threads.  To apply a
+command to all threads in descending order, type @kbd{thread apply all
 @var{command}}.  To apply a command to all threads in ascending order,
 type @kbd{thread apply all -ascending @var{command}}.
 
@@ -3983,7 +4040,7 @@ slow down the running of your program.
 
 @table @code
 @kindex watch
-@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
 Set a watchpoint for an expression.  @value{GDBN} will break when the
 expression @var{expr} is written into by the program and its value
 changes.  The simplest (and the most popular) use of this command is
@@ -3993,9 +4050,9 @@ to watch the value of a single variable:
 (@value{GDBP}) watch foo
 @end smallexample
 
-If the command includes a @code{@r{[}thread @var{threadnum}@r{]}}
+If the command includes a @code{@r{[}thread @var{thread-id}@r{]}}
 argument, @value{GDBN} breaks only when the thread identified by
-@var{threadnum} changes the value of @var{expr}.  If any other threads
+@var{thread-id} changes the value of @var{expr}.  If any other threads
 change the value of @var{expr}, @value{GDBN} will not break.  Note
 that watchpoints restricted to a single thread in this way only work
 with Hardware Watchpoints.
@@ -4027,12 +4084,12 @@ Examples:
 @end smallexample
 
 @kindex rwatch
-@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
 Set a watchpoint that will break when the value of @var{expr} is read
 by the program.
 
 @kindex awatch
-@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
 Set a watchpoint that will break when @var{expr} is either read from
 or written into by the program.
 
@@ -6051,25 +6108,25 @@ breakpoints on all threads, or on a particular thread.
 @table @code
 @cindex breakpoints and threads
 @cindex thread breakpoints
-@kindex break @dots{} thread @var{threadno}
-@item break @var{location} thread @var{threadno}
-@itemx break @var{location} thread @var{threadno} if @dots{}
+@kindex break @dots{} thread @var{thread-id}
+@item break @var{location} thread @var{thread-id}
+@itemx break @var{location} thread @var{thread-id} if @dots{}
 @var{location} specifies source lines; there are several ways of
 writing them (@pxref{Specify Location}), but the effect is always to
 specify some source line.
 
-Use the qualifier @samp{thread @var{threadno}} with a breakpoint command
+Use the qualifier @samp{thread @var{thread-id}} with a breakpoint command
 to specify that you only want @value{GDBN} to stop the program when a
-particular thread reaches this breakpoint.  The @var{threadno} specifier
-is one of the numeric thread identifiers assigned by @value{GDBN}, shown
+particular thread reaches this breakpoint.  The @var{thread-id} specifier
+is one of the thread identifiers assigned by @value{GDBN}, shown
 in the first column of the @samp{info threads} display.
 
-If you do not specify @samp{thread @var{threadno}} when you set a
+If you do not specify @samp{thread @var{thread-id}} when you set a
 breakpoint, the breakpoint applies to @emph{all} threads of your
 program.
 
 You can use the @code{thread} qualifier on conditional breakpoints as
-well; in this case, place @samp{thread @var{threadno}} before or
+well; in this case, place @samp{thread @var{thread-id}} before or
 after the breakpoint condition, like this:
 
 @smallexample
@@ -10392,6 +10449,9 @@ This variable contains the address of the thread information block.
 The number of the current inferior.  @xref{Inferiors and
 Programs, ,Debugging Multiple Inferiors and Programs}.
 
+@item $_thread
+The thread number of the current thread.  @xref{thread numbers}.
+
 @end table
 
 @node Convenience Funs
@@ -16134,7 +16194,7 @@ This command prints the ID of the current task.
 
 @item task @var{taskno}
 @cindex Ada task switching
-This command is like the @code{thread @var{threadno}}
+This command is like the @code{thread @var{thread-id}}
 command (@pxref{Threads}).  It switches the context of debugging
 from the current task to the given task.
 
@@ -25479,8 +25539,8 @@ increases the risk that by relying on implicitly selected thread, the
 frontend may be operating on a wrong one.  Therefore, each MI command
 should explicitly specify which thread and frame to operate on.  To
 make it possible, each MI command accepts the @samp{--thread} and
-@samp{--frame} options, the value to each is @value{GDBN} identifier
-for thread and frame to operate on.
+@samp{--frame} options, the value to each is @value{GDBN} global
+identifier for thread and frame to operate on.
 
 Usually, each top-level window in a frontend allows the user to select
 a thread and a frame, and remembers the user selection for further
@@ -26033,15 +26093,16 @@ The following is the list of possible async records:
 @table @code
 
 @item *running,thread-id="@var{thread}"
-The target is now running.  The @var{thread} field tells which
-specific thread is now running, and can be @samp{all} if all threads
-are running.  The frontend should assume that no interaction with a 
-running thread is possible after this notification is produced.
-The frontend should not assume that this notification is output
-only once for any command.  @value{GDBN} may emit this notification 
-several times, either for different threads, because it cannot resume
-all threads together, or even for a single thread, if the thread must
-be stepped though some code before letting it run freely.
+The target is now running.  The @var{thread} field can be the global
+thread ID of the the thread that is now running, and it can be
+@samp{all} if all threads are running.  The frontend should assume
+that no interaction with a running thread is possible after this
+notification is produced.  The frontend should not assume that this
+notification is output only once for any command.  @value{GDBN} may
+emit this notification several times, either for different threads,
+because it cannot resume all threads together, or even for a single
+thread, if the thread must be stepped though some code before letting
+it run freely.
 
 @item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
 The target has stopped.  The @var{reason} field can have one of the
@@ -26095,8 +26156,9 @@ The inferior called @code{exec}.  This is reported when @code{catch exec}
 (@pxref{Set Catchpoints}) has been used.
 @end table
 
-The @var{id} field identifies the thread that directly caused the stop
--- for example by hitting a breakpoint.  Depending on whether all-stop
+The @var{id} field identifies the global thread ID of the thread
+that directly caused the stop -- for example by hitting a breakpoint.
+Depending on whether all-stop
 mode is in effect (@pxref{All-Stop Mode}), @value{GDBN} may either
 stop all threads, or only the thread that directly triggered the stop.
 If all threads are stopped, the @var{stopped} field will have the
@@ -26132,7 +26194,7 @@ only when the inferior exited with some code.
 @item =thread-created,id="@var{id}",group-id="@var{gid}"
 @itemx =thread-exited,id="@var{id}",group-id="@var{gid}"
 A thread either was created, or has exited.  The @var{id} field
-contains the @value{GDBN} identifier of the thread.  The @var{gid}
+contains the global @value{GDBN} identifier of the thread.  The @var{gid}
 field identifies the thread group this thread belongs to.
 
 @item =thread-selected,id="@var{id}"
@@ -26393,7 +26455,7 @@ uses a tuple with the following fields:
 
 @table @code
 @item id
-The numeric id assigned to the thread by @value{GDBN}.  This field is
+The global numeric id assigned to the thread by @value{GDBN}.  This field is
 always present.
 
 @item target-id
@@ -26866,7 +26928,8 @@ Make the breakpoint conditional on @var{condition}.
 @item -i @var{ignore-count}
 Initialize the @var{ignore-count}.
 @item -p @var{thread-id}
-Restrict the breakpoint to the specified @var{thread-id}.
+Restrict the breakpoint to the thread with the specified global
+@var{thread-id}.
 @end table
 
 @subsubheading Result
@@ -26956,7 +27019,8 @@ Make the breakpoint conditional on @var{condition}.
 Set the ignore count of the breakpoint (@pxref{Conditions, ignore count})
 to @var{ignore-count}.
 @item -p @var{thread-id}
-Restrict the breakpoint to the specified @var{thread-id}.
+Restrict the breakpoint to the thread with the specified global
+@var{thread-id}.
 @end table
 
 @subsubheading Result
@@ -27606,10 +27670,11 @@ The corresponding @value{GDBN} command is @samp{pwd}.
  -thread-info [ @var{thread-id} ]
 @end smallexample
 
-Reports information about either a specific thread, if 
-the @var{thread-id} parameter is present, or about all
-threads.  When printing information about all threads,
-also reports the current thread.
+Reports information about either a specific thread, if the
+@var{thread-id} parameter is present, or about all threads.
+@var{thread-id} is the thread's global thread ID.  When printing
+information about all threads, also reports the global ID of the
+current thread.
 
 @subsubheading @value{GDBN} Command
 
@@ -27626,7 +27691,7 @@ defined for a given thread:
 This field exists only for the current thread.  It has the value @samp{*}.
 
 @item id
-The identifier that @value{GDBN} uses to refer to the thread.
+The global identifier that @value{GDBN} uses to refer to the thread.
 
 @item target-id
 The identifier that the target uses to refer to the thread.
@@ -27692,8 +27757,9 @@ current-thread-id="1"
  -thread-list-ids
 @end smallexample
 
-Produces a list of the currently known @value{GDBN} thread ids.  At the
-end of the list it also prints the total number of such threads.
+Produces a list of the currently known global @value{GDBN} thread ids.
+At the end of the list it also prints the total number of such
+threads.
 
 This command is retained for historical reasons, the
 @code{-thread-info} command should be used instead.
@@ -27719,11 +27785,12 @@ current-thread-id="1",number-of-threads="3"
 @subsubheading Synopsis
 
 @smallexample
- -thread-select @var{threadnum}
+ -thread-select @var{thread-id}
 @end smallexample
 
-Make @var{threadnum} the current thread.  It prints the number of the new
-current thread, and the topmost frame for that thread.
+Make thread with global thread number @var{thread-id} the current
+thread.  It prints the number of the new current thread, and the
+topmost frame for that thread.
 
 This command is deprecated in favor of explicitly using the
 @samp{--thread} option to each command.
@@ -27792,7 +27859,8 @@ The identifier that @value{GDBN} uses to refer to the Ada task.
 The identifier that the target uses to refer to the Ada task.
 
 @item thread-id
-The identifier of the thread corresponding to the Ada task.
+The global thread identifier of the thread corresponding to the Ada
+task.
 
 This field should always exist, as Ada tasks are always implemented
 on top of a thread.  But if @value{GDBN} cannot find this corresponding
@@ -29008,7 +29076,7 @@ would be printed by the @value{GDBN} CLI.  If @samp{print object}
 
 @item thread-id
 If a variable object is bound to a specific thread, then this is the
-thread's identifier.
+thread's global identifier.
 
 @item has_more
 For a dynamic varobj, this indicates whether there appear to be any
@@ -29189,8 +29257,8 @@ The type of the child.  If @samp{print object}
 If values were requested, this is the value.
 
 @item thread-id
-If this variable object is associated with a thread, this is the thread id.  
-Otherwise this result is not present.
+If this variable object is associated with a thread, this is the
+thread's global thread id.  Otherwise this result is not present.
 
 @item frozen
 If the variable object is frozen, this variable will be present with a value of 1.
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index fa1ad53..840defc 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -3127,13 +3127,14 @@ At present, @var{count} must be zero.
 @end deffn
 
 @deffn {Scheme Procedure} breakpoint-thread breakpoint
-Return the thread-id for thread-specific breakpoint @var{breakpoint}.
-Return #f if @var{breakpoint} is not thread-specific.
+Return the global-thread-id for thread-specific breakpoint
+@var{breakpoint}.  Return #f if @var{breakpoint} is not
+thread-specific.
 @end deffn
 
-@deffn {Scheme Procedure} set-breakpoint-thread! breakpoint thread-id|#f
-Set the thread-id for @var{breakpoint} to @var{thread-id}.
-If set to @code{#f}, the breakpoint is no longer thread-specific.
+@deffn {Scheme Procedure} set-breakpoint-thread! breakpoint global-thread-id|#f
+Set the thread-id for @var{breakpoint} to @var{global-thread-id} If
+set to @code{#f}, the breakpoint is no longer thread-specific.
 @end deffn
 
 @deffn {Scheme Procedure} breakpoint-task breakpoint
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 855da44..f9f9e5b 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2995,7 +2995,7 @@ user-specified thread name.
 @end defvar
 
 @defvar InferiorThread.num
-ID of the thread, as assigned by GDB.
+The per-inferior number of the thread, as assigned by GDB.
 @end defvar
 
 @defvar InferiorThread.ptid
@@ -4643,9 +4643,9 @@ first command is @code{silent}.  This is not reported by the
 @end defvar
 
 @defvar Breakpoint.thread
-If the breakpoint is thread-specific, this attribute holds the thread
-id.  If the breakpoint is not thread-specific, this attribute is
-@code{None}.  This attribute is writable.
+If the breakpoint is thread-specific, this attribute holds the
+thread's global id.  If the breakpoint is not thread-specific, this
+attribute is @code{None}.  This attribute is writable.
 @end defvar
 
 @defvar Breakpoint.task
diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c
index 6e9dc2e..606fb85 100644
--- a/gdb/dummy-frame.c
+++ b/gdb/dummy-frame.c
@@ -130,7 +130,7 @@ pop_dummy_frame_bpt (struct breakpoint *b, void *dummy_voidp)
 {
   struct dummy_frame *dummy = (struct dummy_frame *) dummy_voidp;
 
-  if (b->thread == pid_to_thread_id (dummy->id.ptid)
+  if (b->thread == ptid_to_global_thread_id (dummy->id.ptid)
       && b->disposition == disp_del && frame_id_eq (b->frame_id, dummy->id.id))
     {
       while (b->related_breakpoint != b)
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 1eb341b..55674f3 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -902,7 +902,7 @@ elf_gnu_ifunc_resolver_stop (struct breakpoint *b)
   struct frame_info *prev_frame = get_prev_frame (get_current_frame ());
   struct frame_id prev_frame_id = get_stack_frame_id (prev_frame);
   CORE_ADDR prev_pc = get_frame_pc (prev_frame);
-  int thread_id = pid_to_thread_id (inferior_ptid);
+  int thread_id = ptid_to_global_thread_id (inferior_ptid);
 
   gdb_assert (b->type == bp_gnu_ifunc_resolver);
 
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index e1e264a..6c23687 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -30,6 +30,7 @@ struct symtab;
 #include "btrace.h"
 #include "common/vec.h"
 #include "target/waitstatus.h"
+#include "cli/cli-utils.h"
 
 /* Frontend view of the thread state.  Possible extensions: stepping,
    finishing, until(ling),...  */
@@ -187,7 +188,45 @@ struct thread_info
   ptid_t ptid;			/* "Actual process id";
 				    In fact, this may be overloaded with 
 				    kernel thread id, etc.  */
-  int num;			/* Convenient handle (GDB thread id) */
+
+  /* Each thread has two GDB IDs.
+
+     a) The thread ID (Id).  This consists of the pair of:
+
+        - the number of the thread's inferior and,
+
+        - the thread's thread number in its inferior, aka, the
+          per-inferior thread number.  This number is unique in the
+          inferior but not unique between inferiors.
+
+     b) The global ID (GId).  This is a a single integer unique
+        between all inferiors.
+
+     E.g.:
+
+      (gdb) info threads -gid
+	Id    GId   Target Id   Frame
+      * 1.1   1     Thread A    0x16a09237 in foo () at foo.c:10
+	1.2   3     Thread B    0x15ebc6ed in bar () at foo.c:20
+	1.3   5     Thread C    0x15ebc6ed in bar () at foo.c:20
+	2.1   2     Thread A    0x16a09237 in foo () at foo.c:10
+	2.2   4     Thread B    0x15ebc6ed in bar () at foo.c:20
+	2.3   6     Thread C    0x15ebc6ed in bar () at foo.c:20
+
+     Above, both inferiors 1 and 2 have threads numbered 1-3, but each
+     thread has its own unique global ID.  */
+
+  /* The thread's global GDB thread number.  This is exposed to MI and
+     Python/Scheme.  */
+  int global_num;
+
+  /* The per-inferior thread number.  This is unique in the inferior
+     the thread belongs to, but not unique between inferiors.  This is
+     what the $_thread convenience variable is bound to.  */
+  int per_inf_num;
+
+  /* The inferior this thread belongs to.  */
+  struct inferior *inf;
 
   /* The name of the thread, as specified by the user.  This is NULL
      if the thread does not have a user-given name.  */
@@ -353,31 +392,40 @@ extern int thread_has_single_step_breakpoint_here (struct thread_info *tp,
 						   struct address_space *aspace,
 						   CORE_ADDR addr);
 
-/* Translate the integer thread id (GDB's homegrown id, not the system's)
-   into a "pid" (which may be overloaded with extra thread information).  */
-extern ptid_t thread_id_to_pid (int);
+/* Translate the global integer thread id (GDB's homegrown id, not the
+   system's) into a "pid" (which may be overloaded with extra thread
+   information).  */
+extern ptid_t global_thread_id_to_ptid (int num);
+
+/* Translate a 'pid' (which may be overloaded with extra thread
+   information) into the global integer thread id (GDB's homegrown id,
+   not the system's).  */
+extern int ptid_to_global_thread_id (ptid_t ptid);
 
-/* Translate a 'pid' (which may be overloaded with extra thread information) 
-   into the integer thread id (GDB's homegrown id, not the system's).  */
-extern int pid_to_thread_id (ptid_t ptid);
+/* Returns whether to show inferior-qualified thread IDs, or plain
+   thread numbers.  Inferior-qualified IDs are shown whenever we have
+   multiple inferiors, or the only inferior left has number > 1.  */
+extern int show_inferior_qualified_tids (void);
 
-/* Return a string version of THR's thread ID.  The result is stored
-   in a circular static buffer, NUMCELLS deep.  */
+/* Return a string version of THR's thread ID.  If there are multiple
+   inferiors, then this prints the inferior-qualifier form, otherwise
+   it only prints the thread number.  The result is stored in a
+   circular static buffer, NUMCELLS deep.  */
 const char *print_thread_id (struct thread_info *thr);
 
 /* Boolean test for an already-known pid (which may be overloaded with
    extra thread information).  */
 extern int in_thread_list (ptid_t ptid);
 
-/* Boolean test for an already-known thread id (GDB's homegrown id, 
-   not the system's).  */
-extern int valid_thread_id (int thread);
+/* Boolean test for an already-known global thread id (GDB's homegrown
+   global id, not the system's).  */
+extern int valid_global_thread_id (int global_id);
 
 /* Search function to lookup a thread by 'pid'.  */
 extern struct thread_info *find_thread_ptid (ptid_t ptid);
 
-/* Find thread by GDB user-visible thread number.  */
-struct thread_info *find_thread_id (int num);
+/* Find thread by GDB global thread ID.  */
+struct thread_info *find_thread_global_id (int global_id);
 
 /* Finds the first thread of the inferior given by PID.  If PID is -1,
    returns the first thread in the list.  */
@@ -399,6 +447,16 @@ void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 typedef int (*thread_callback_func) (struct thread_info *, void *);
 extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
 
+/* Traverse all threads.  */
+#define ALL_THREADS(T)				\
+  for (T = thread_list; T; T = T->next)		\
+
+/* Traverse over all threads, sorted by inferior.  */
+#define ALL_THREADS_BY_INFERIOR(inf, tp) \
+  ALL_INFERIORS (inf) \
+    ALL_THREADS (tp) \
+      if (inf == tp->inf)
+
 /* Traverse all threads, except those that have THREAD_EXITED
    state.  */
 
@@ -504,7 +562,14 @@ extern void thread_command (char *tidstr, int from_tty);
    `set print thread-events'.  */
 extern int print_thread_events;
 
-extern void print_thread_info (struct ui_out *uiout, char *threads,
+/* Prints the list of threads and their details on UIOUT.  If
+   REQUESTED_THREADS, a list of GDB ids/ranges, is not NULL, only
+   print threads whose ID is included in the list.  If PID is not -1,
+   only print threads from the process PID.  Otherwise, threads from
+   all attached PIDs are printed.  If both REQUESTED_THREADS is not
+   NULL and PID is not -1, then the thread is printed if it belongs to
+   the specified process.  Otherwise, an error is raised.  */
+extern void print_thread_info (struct ui_out *uiout, char *requested_threads,
 			       int pid);
 
 extern struct cleanup *make_cleanup_restore_current_thread (void);
diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c
index 46f9918..716fe4c 100644
--- a/gdb/guile/scm-breakpoint.c
+++ b/gdb/guile/scm-breakpoint.c
@@ -745,7 +745,7 @@ gdbscm_set_breakpoint_thread_x (SCM self, SCM newvalue)
   if (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX))
     {
       id = scm_to_long (newvalue);
-      if (! valid_thread_id (id))
+      if (!valid_global_thread_id (id))
 	{
 	  gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
 				     _("invalid thread id"));
@@ -1273,14 +1273,14 @@ Set the breakpoint's \"hit\" count.  The value must be zero.\n\
 
   { "breakpoint-thread", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_thread),
     "\
-Return the breakpoint's thread id or #f if there isn't one." },
+Return the breakpoint's global thread id or #f if there isn't one." },
 
   { "set-breakpoint-thread!", 2, 0, 0,
     as_a_scm_t_subr (gdbscm_set_breakpoint_thread_x),
     "\
-Set the thread id for this breakpoint.\n\
+Set the global thread id for this breakpoint.\n\
 \n\
-  Arguments: <gdb:breakpoint> thread-id" },
+  Arguments: <gdb:breakpoint> global-thread-id" },
 
   { "breakpoint-task", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_task),
     "\
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index d46e8a7..df13896 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -960,7 +960,7 @@ step_command_fsm_prepare (struct step_command_fsm *sm,
   sm->skip_subroutines = skip_subroutines;
   sm->single_inst = single_inst;
   sm->count = count;
-  sm->thread = thread->num;
+  sm->thread = thread->global_num;
 
   /* Leave the si command alone.  */
   if (!sm->single_inst || sm->skip_subroutines)
@@ -1032,7 +1032,7 @@ static int
 step_command_fsm_should_stop (struct thread_fsm *self)
 {
   struct step_command_fsm *sm = (struct step_command_fsm *) self;
-  struct thread_info *tp = find_thread_id (sm->thread);
+  struct thread_info *tp = find_thread_global_id (sm->thread);
 
   if (tp->control.stop_step)
     {
@@ -1478,7 +1478,7 @@ until_next_command (int from_tty)
   struct symbol *func;
   struct symtab_and_line sal;
   struct thread_info *tp = inferior_thread ();
-  int thread = tp->num;
+  int thread = tp->global_num;
   struct cleanup *old_chain;
   struct until_next_fsm *sm;
 
@@ -1520,12 +1520,11 @@ until_next_command (int from_tty)
   set_longjmp_breakpoint (tp, get_frame_id (frame));
   old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
 
-  sm = new_until_next_fsm (tp->num);
+  sm = new_until_next_fsm (tp->global_num);
   tp->thread_fsm = &sm->thread_fsm;
   discard_cleanups (old_chain);
 
   proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
-
 }
 
 static void
@@ -1774,7 +1773,7 @@ finish_command_fsm_should_stop (struct thread_fsm *self)
 {
   struct finish_command_fsm *f = (struct finish_command_fsm *) self;
   struct return_value_info *rv = &f->return_value;
-  struct thread_info *tp = find_thread_id (f->thread);
+  struct thread_info *tp = find_thread_global_id (f->thread);
 
   if (f->function != NULL
       && bpstat_find_breakpoint (tp->control.stop_bpstat,
@@ -1967,7 +1966,7 @@ finish_command (char *arg, int from_tty)
 
   tp = inferior_thread ();
 
-  sm = new_finish_command_fsm (tp->num);
+  sm = new_finish_command_fsm (tp->global_num);
 
   tp->thread_fsm = &sm->thread_fsm;
 
@@ -2715,7 +2714,8 @@ attach_post_wait (char *args, int from_tty, enum attach_post_wait_mode mode)
 	    {
 	      if (ptid_get_pid (thread->ptid) == pid)
 		{
-		  if (thread->num < lowest->num)
+		  if (thread->inf->num < lowest->inf->num
+		      || thread->per_inf_num < lowest->per_inf_num)
 		    lowest = thread;
 		}
 	    }
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 67ac9e8..571d26a 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -304,6 +304,9 @@ struct inferior
   /* True if the PID was actually faked by GDB.  */
   int fake_pid_p;
 
+  /* The highest thread number this inferior ever had.  */
+  int highest_thread_num;
+
   /* State of GDB control of inferior process execution.
      See `struct inferior_control_state'.  */
   struct inferior_control_state control;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 001e7b4..b343783 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5702,7 +5702,7 @@ handle_signal_stop (struct execution_control_state *ecs)
       context_switch (ecs->ptid);
 
       if (deprecated_context_hook)
-	deprecated_context_hook (pid_to_thread_id (ecs->ptid));
+	deprecated_context_hook (ptid_to_global_thread_id (ecs->ptid));
     }
 
   /* At this point, get hold of the now-current thread's frame.  */
@@ -7510,7 +7510,7 @@ insert_exception_resume_breakpoint (struct thread_info *tp,
 	  /* set_momentary_breakpoint_at_pc invalidates FRAME.  */
 	  frame = NULL;
 
-	  bp->thread = tp->num;
+	  bp->thread = tp->global_num;
 	  inferior_thread ()->control.exception_resume_breakpoint = bp;
 	}
     }
@@ -7547,7 +7547,7 @@ insert_exception_resume_from_probe (struct thread_info *tp,
 
   bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
 				       handler, bp_exception_resume);
-  bp->thread = tp->num;
+  bp->thread = tp->global_num;
   inferior_thread ()->control.exception_resume_breakpoint = bp;
 }
 
diff --git a/gdb/mi/mi-cmd-var.c b/gdb/mi/mi-cmd-var.c
index e95ded7..04f326c 100644
--- a/gdb/mi/mi-cmd-var.c
+++ b/gdb/mi/mi-cmd-var.c
@@ -662,7 +662,7 @@ mi_cmd_var_update_iter (struct varobj *var, void *data_pointer)
     thread_stopped = 1;
   else
     {
-      struct thread_info *tp = find_thread_id (thread_id);
+      struct thread_info *tp = find_thread_global_id (thread_id);
 
       if (tp)
 	thread_stopped = is_stopped (tp->ptid);
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 91ca8b8..7f42367 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -362,7 +362,7 @@ mi_new_thread (struct thread_info *t)
 
   fprintf_unfiltered (mi->event_channel, 
 		      "thread-created,id=\"%d\",group-id=\"i%d\"",
-		      t->num, inf->num);
+		      t->global_num, inf->num);
   gdb_flush (mi->event_channel);
 }
 
@@ -383,7 +383,7 @@ mi_thread_exit (struct thread_info *t, int silent)
   target_terminal_ours ();
   fprintf_unfiltered (mi->event_channel, 
 		      "thread-exited,id=\"%d\",group-id=\"i%d\"",
-		      t->num, inf->num);
+		      t->global_num, inf->num);
   gdb_flush (mi->event_channel);
 
   do_cleanups (old_chain);
@@ -617,15 +617,14 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
 	  print_stop_event (mi->cli_uiout);
 	}
 
-      ui_out_field_int (mi_uiout, "thread-id",
-			pid_to_thread_id (inferior_ptid));
+      tp = inferior_thread ();
+      ui_out_field_int (mi_uiout, "thread-id", tp->global_num);
       if (non_stop)
 	{
 	  struct cleanup *back_to = make_cleanup_ui_out_list_begin_end 
 	    (mi_uiout, "stopped-threads");
 
-	  ui_out_field_int (mi_uiout, NULL,
-			    pid_to_thread_id (inferior_ptid));
+	  ui_out_field_int (mi_uiout, NULL, tp->global_num);
 	  do_cleanups (back_to);
 	}
       else
@@ -859,7 +858,7 @@ mi_output_running_pid (struct thread_info *info, void *arg)
   if (ptid_get_pid (*ptid) == ptid_get_pid (info->ptid))
     fprintf_unfiltered (raw_stdout,
 			"*running,thread-id=\"%d\"\n",
-			info->num);
+			info->global_num);
 
   return 0;
 }
@@ -925,7 +924,8 @@ mi_on_resume (ptid_t ptid)
       struct thread_info *ti = find_thread_ptid (ptid);
 
       gdb_assert (ti);
-      fprintf_unfiltered (raw_stdout, "*running,thread-id=\"%d\"\n", ti->num);
+      fprintf_unfiltered (raw_stdout, "*running,thread-id=\"%d\"\n",
+			  ti->global_num);
     }
 
   if (!running_result_record_printed && mi_proceeded)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index ab8eae3..e25eedf 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2165,7 +2165,7 @@ mi_execute_command (const char *cmd, int from_tty)
 	    {
 	      struct thread_info *ti = inferior_thread ();
 
-	      report_change = (ti->num != command->thread);
+	      report_change = (ti->global_num != command->thread);
 	    }
 
 	  if (report_change)
@@ -2175,7 +2175,7 @@ mi_execute_command (const char *cmd, int from_tty)
 	      target_terminal_ours ();
 	      fprintf_unfiltered (mi->event_channel,
 				  "thread-selected,id=\"%d\"",
-				  ti->num);
+				  ti->global_num);
 	      gdb_flush (mi->event_channel);
 	    }
 	}
@@ -2226,7 +2226,7 @@ mi_cmd_execute (struct mi_parse *parse)
 
   if (parse->thread != -1)
     {
-      struct thread_info *tp = find_thread_id (parse->thread);
+      struct thread_info *tp = find_thread_global_id (parse->thread);
 
       if (!tp)
 	error (_("Invalid thread id: %d"), parse->thread);
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 640ec1b..85b17d5 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -203,7 +203,7 @@ bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
       if (! gdb_py_int_as_long (newvalue, &id))
 	return -1;
 
-      if (! valid_thread_id (id))
+      if (!valid_global_thread_id (id))
 	{
 	  PyErr_SetString (PyExc_RuntimeError,
 			   _("Invalid thread ID."));
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index 38cf293..bff6dba 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -221,7 +221,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
   if (PyErr_Occurred ())
     return -1;
 
-  thread = pid_to_thread_id (inferior_ptid);
+  thread = ptid_to_global_thread_id (inferior_ptid);
   if (thread == 0)
     {
       PyErr_SetString (PyExc_ValueError,
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 5075071..a9dd5cb 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -115,6 +115,8 @@ thpy_set_name (PyObject *self, PyObject *newvalue, void *ignore)
   return 0;
 }
 
+/* Getter for InferiorThread.num.  */
+
 static PyObject *
 thpy_get_num (PyObject *self, void *closure)
 {
@@ -122,7 +124,7 @@ thpy_get_num (PyObject *self, void *closure)
 
   THPY_REQUIRE_VALID (thread_obj);
 
-  return PyLong_FromLong (thread_obj->thread->num);
+  return PyLong_FromLong (thread_obj->thread->per_inf_num);
 }
 
 /* Getter for InferiorThread.ptid  -> (pid, lwp, tid).
@@ -294,7 +296,8 @@ static PyGetSetDef thread_object_getset[] =
 {
   { "name", thpy_get_name, thpy_set_name,
     "The name of the thread, as set by the user or the OS.", NULL },
-  { "num", thpy_get_num, NULL, "ID of the thread, as assigned by GDB.", NULL },
+  { "num", thpy_get_num, NULL,
+    "Per-inferior number of the thread, as assigned by GDB.", NULL },
   { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
     NULL },
   { "inferior", thpy_get_inferior, NULL,
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 8c33010..b886fd8 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -218,7 +218,7 @@ record_btrace_open (const char *args, int from_tty)
 
   disable_chain = make_cleanup (null_cleanup, NULL);
   ALL_NON_EXITED_THREADS (tp)
-    if (args == NULL || *args == 0 || number_is_in_list (args, tp->num))
+    if (args == NULL || *args == 0 || number_is_in_list (args, tp->global_num))
       {
 	btrace_enable (tp, &record_btrace_conf);
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 528d863..3c58130 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3881,7 +3881,9 @@ process_initial_stop_replies (int from_tty)
 	  && thread->suspend.waitstatus_pending_p)
 	selected = thread;
 
-      if (lowest_stopped == NULL || thread->num < lowest_stopped->num)
+      if (lowest_stopped == NULL
+	  || thread->inf->num < lowest_stopped->inf->num
+	  || thread->per_inf_num < lowest_stopped->per_inf_num)
 	lowest_stopped = thread;
 
       if (non_stop)
diff --git a/gdb/target.c b/gdb/target.c
index d331fe4..a499b2f 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2157,6 +2157,8 @@ target_pre_inferior (int from_tty)
      the inferior was attached to.  */
   current_inferior ()->attach_flag = 0;
 
+  current_inferior ()->highest_thread_num = 0;
+
   agent_capability_invalidate ();
 }
 
diff --git a/gdb/testsuite/gdb.base/break.exp b/gdb/testsuite/gdb.base/break.exp
index 3ec74bf..2b4587e 100644
--- a/gdb/testsuite/gdb.base/break.exp
+++ b/gdb/testsuite/gdb.base/break.exp
@@ -550,7 +550,7 @@ gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
     "thread-specific breakpoint on non-existent thread disallowed"
 
 gdb_test "break $bp_location12 thread foo" \
-    "Junk after thread keyword.*" \
+    "Invalid thread ID: foo" \
     "thread-specific breakpoint on bogus thread ID disallowed"
 
 # Verify that GDB responds gracefully to a breakpoint command with
diff --git a/gdb/testsuite/gdb.base/hbreak2.exp b/gdb/testsuite/gdb.base/hbreak2.exp
index dbf55f3..3a303ec 100644
--- a/gdb/testsuite/gdb.base/hbreak2.exp
+++ b/gdb/testsuite/gdb.base/hbreak2.exp
@@ -341,7 +341,7 @@ gdb_test "hbreak $bp_location12 thread 999" "Unknown thread 999.*" \
     "thread-specific hardware breakpoint on non-existent thread disallowed"
 
 gdb_test "hbreak $bp_location12 thread foo" \
-    "Junk after thread keyword.*" \
+    "Invalid thread ID: foo" \
     "thread-specific hardware breakpoint on bogus thread ID disallowed"
 
 # Verify that GDB responds gracefully to a breakpoint command with
diff --git a/gdb/testsuite/gdb.base/sepdebug.exp b/gdb/testsuite/gdb.base/sepdebug.exp
index ad87269..3ad3669 100644
--- a/gdb/testsuite/gdb.base/sepdebug.exp
+++ b/gdb/testsuite/gdb.base/sepdebug.exp
@@ -361,7 +361,7 @@ gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
     "thread-specific breakpoint on non-existent thread disallowed"
 
 gdb_test "break $bp_location12 thread foo" \
-    "Junk after thread keyword.*" \
+    "Invalid thread ID: foo" \
     "thread-specific breakpoint on bogus thread ID disallowed"
 
 # Verify that GDB responds gracefully to a breakpoint command with
diff --git a/gdb/testsuite/gdb.base/watch_thread_num.exp b/gdb/testsuite/gdb.base/watch_thread_num.exp
index b3ce273..eff9fbd 100644
--- a/gdb/testsuite/gdb.base/watch_thread_num.exp
+++ b/gdb/testsuite/gdb.base/watch_thread_num.exp
@@ -46,7 +46,7 @@ if { ![runto main] } then {
    return
 }
 
-gdb_test "watch shared_var thread 0" "Unknown thread 0\." "Watchpoint on invalid thread"
+gdb_test "watch shared_var thread 0" "Invalid thread ID: 0" "Watchpoint on invalid thread"
 gdb_test "watch shared_var thread" "A syntax error in expression, near `thread'\." "Invalid watch syntax"
 
 set bpexitline [gdb_get_line_number "all threads started"]
diff --git a/gdb/testsuite/gdb.linespec/keywords.exp b/gdb/testsuite/gdb.linespec/keywords.exp
index 03f662c..11e2c4c 100644
--- a/gdb/testsuite/gdb.linespec/keywords.exp
+++ b/gdb/testsuite/gdb.linespec/keywords.exp
@@ -54,16 +54,16 @@ with_test_prefix "trailing whitespace" {
 # break {thread,task} NUMBER --> invalid thread/task
 # break {thread,task} STUFF --> "junk" after keyword (STUFF is not numeric)
 gdb_test "break thread 123" "Unknown thread 123\\."
-gdb_test "break thread foo" "Junk after thread keyword\\."
+gdb_test "break thread foo" "Invalid thread ID: foo"
 gdb_test "break task 123" "Unknown task 123\\."
 gdb_test "break task foo" "Junk after task keyword\\."
 gdb_breakpoint "thread if 0" "message"
 
 # These are also NULL locations, but using a subsequent keyword
 # as the "junk".
-gdb_test "break thread thread" "Junk after thread keyword\\."
-gdb_test "break thread task" "Junk after thread keyword\\."
-gdb_test "break thread if" "Junk after thread keyword\\."
+gdb_test "break thread thread" "Invalid thread ID: thread"
+gdb_test "break thread task" "Invalid thread ID: task"
+gdb_test "break thread if" "Invalid thread ID: if"
 gdb_test "break task task" "Junk after task keyword\\."
 gdb_test "break task thread" "Junk after task keyword\\."
 gdb_test "break task if" "Junk after task keyword\\."
diff --git a/gdb/testsuite/gdb.multi/info-threads.exp b/gdb/testsuite/gdb.multi/info-threads.exp
index 7ae60d7..f8d9c6d 100644
--- a/gdb/testsuite/gdb.multi/info-threads.exp
+++ b/gdb/testsuite/gdb.multi/info-threads.exp
@@ -36,4 +36,4 @@ gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
 
 # "info threads" while inferior 1 has execution and inferior 2 is not
 # running yet should show inferior 1's thread, and give no error.
-gdb_test "info threads" "1    .* main .* at .*$srcfile:.*No selected thread.*"
+gdb_test "info threads" "1\.1 .* main .* at .*$srcfile:.*No selected thread.*"
diff --git a/gdb/testsuite/gdb.multi/tids.c b/gdb/testsuite/gdb.multi/tids.c
new file mode 100644
index 0000000..00a8298
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/tids.c
@@ -0,0 +1,52 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 Free Software Foundation, Inc.
+
+   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 <unistd.h>
+#include <pthread.h>
+
+pthread_t child_thread[2];
+
+void *
+thread_function2 (void *arg)
+{
+  while (1)
+    sleep (1);
+}
+
+void *
+thread_function1 (void *arg)
+{
+  pthread_create (&child_thread[1], NULL, thread_function2, NULL);
+
+  while (1)
+    sleep (1);
+}
+
+int
+main (void)
+{
+  int i;
+
+  alarm (300);
+
+  pthread_create (&child_thread[0], NULL, thread_function1, NULL);
+
+  for (i = 0; i < 2; i++)
+    pthread_join (child_thread[i], NULL);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp
new file mode 100644
index 0000000..7b51c80
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/tids.exp
@@ -0,0 +1,296 @@
+# Copyright 2015 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test thread ID parsing and display.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Multiple inferiors are needed, therefore both native and extended
+# gdbserver modes are supported.  Only non-extended gdbserver is not
+# supported.
+if [target_info exists use_gdb_stub] {
+    untested ${testfile}.exp
+    return
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {pthreads debug}] } {
+    return -1
+}
+
+clean_restart ${testfile}
+
+if { ![runto_main] } then {
+    return -1
+}
+
+# Issue "thread apply TID_LIST p 1" and expect EXP_TID_LIST (a list of
+# thread ids) to be displayed.
+proc thread_apply {tid_list exp_tid_list {message ""}} {
+    global decimal
+    set any "\[^\r\n\]*"
+    set expected [string_to_regexp $exp_tid_list]
+
+    set r ""
+    foreach tid $expected {
+	append r "\[\r\n\]+"
+	append r "Thread $tid $any:\r\n"
+	append r "\\$$decimal = 1234"
+    }
+
+    set cmd "thread apply $tid_list"
+    if {$message == ""} {
+	set message $cmd
+    }
+    gdb_test "$cmd p 1234" $r $message
+}
+
+# Issue "info threads TID_LIST" and expect EXP_TID_LIST (a list of
+# thread ids) to be displayed.
+proc info_threads {tid_list exp_tid_list {message ""}} {
+    set any "\[^\r\n\]*"
+    set expected [string_to_regexp $exp_tid_list]
+    set r [join $expected " ${any}\r\n${any} "]
+    set r "${any} $r ${any}"
+    set cmd "info threads $tid_list"
+    if {$message == ""} {
+	set message $cmd
+    }
+    gdb_test $cmd $r $message
+}
+
+# Issue both "info threads TID_LIST" and expect INFO_THR output.  Then
+# issue "thread apply TID_LIST" and expect THR_APPLY output.  If
+# THR_APPLY is omitted, INFO_THR is expected instead.
+proc thr_apply_info_thr {tid_list info_thr {thr_apply ""}} {
+    if {$thr_apply == ""} {
+	set thr_apply $info_thr
+    }
+
+    info_threads $tid_list $info_thr
+    thread_apply $tid_list $thr_apply
+}
+
+# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and
+# expect both commands to error out with EXP_ERROR.
+proc thr_apply_info_thr_error {tid_list exp_error}  {
+    gdb_test "info threads $tid_list" \
+	$exp_error
+
+    gdb_test "thread apply $tid_list p 1234" \
+	$exp_error \
+	"thread apply $tid_list"
+}
+
+# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and
+# expect the command to error out with "Invalid thread ID: $EXPECTED".
+# EXPECTED is a literal string, not a regexp.
+proc thr_apply_info_thr_invalid {tid_list expected} {
+    set expected [string_to_regexp $expected]
+    gdb_test "info threads $tid_list" \
+	"Invalid thread ID: $expected"
+
+    gdb_test "thread apply $tid_list p 1234" \
+	"Invalid thread ID: $expected p 1234" \
+	"thread apply $tid_list"
+}
+
+# "info threads" while there's only inferior 1 should show
+# single-number thread IDs.
+with_test_prefix "single inferior" {
+    info_threads "" "1"
+
+    gdb_test "thread" "Current thread is 1 .*"
+}
+
+# "info threads" while there are multiple inferiors should show
+# qualified thread IDs.
+with_test_prefix "two inferiors" {
+    # Add another inferior.
+    gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2"
+
+    # Now that we'd added another inferior, thread IDs now show the
+    # inferior number.
+    info_threads "" "1.1"
+
+    gdb_test "thread" "Current thread is 1\.1 .*"
+
+    gdb_test "inferior 2" "Switching to inferior 2 .*" "switch to inferior 2"
+    gdb_test "file ${binfile}" ".*" "load file in inferior 2"
+
+    runto_main
+
+    # Now that we'd added another inferior, thread IDs now show the
+    # inferior number.
+    info_threads "" "1.1 2.1" \
+	"info threads show inferior numbers"
+
+    gdb_test "thread" "Current thread is 2\.1 .*" \
+	"switch to thread using extended thread ID"
+
+    gdb_breakpoint "thread_function1"
+
+    gdb_continue_to_breakpoint "once"
+    gdb_test "inferior 1" "Switching to inferior 1 .*"
+    gdb_continue_to_breakpoint "twice"
+
+    info_threads "" "1.1 1.2 2.1 2.2" \
+	"info threads again"
+
+    # Confirm the convenience variable show the expected number.
+    gdb_test "p \$_thread == 2" " = 1"
+
+    # Without an explicit inferior component, GDB defaults to the
+    # current inferior.  Make sure we don't refer to a thread by
+    # global ID by mistake.
+    gdb_test "thread 4" "Unknown thread 1.4\\."
+
+    # Test thread ID list parsing.  Test qualified and unqualified
+    # IDs; qualified and unqualified ranges; invalid IDs and invalid
+    # ranges.
+
+    # First spawn a couple more threads so ranges includes more than
+    # two threads.
+    with_test_prefix "more threads" {
+	gdb_breakpoint "thread_function2"
+
+	gdb_test "inferior 2" "Switching to inferior 2 .*"
+	gdb_continue_to_breakpoint "once"
+
+	gdb_test "inferior 1" "Switching to inferior 1 .*"
+	gdb_continue_to_breakpoint "twice"
+    }
+
+    thr_apply_info_thr "1 2 3" \
+	"1.1 1.2 1.3"
+
+    # Same, but with qualified thread IDs.
+    thr_apply_info_thr "1.1 1.2 1.3 2.1 2.2" \
+	"1.1 1.2 1.3 2.1 2.2"
+
+    # Test a thread number range.
+    thr_apply_info_thr "1-3" \
+	"1.1 1.2 1.3"
+
+    # Same, but using a qualified range.
+    thr_apply_info_thr "1.1-3" \
+	"1.1 1.2 1.3"
+
+    # A mix of qualified and unqualified thread IDs/ranges.
+    thr_apply_info_thr "1.1 2-3" \
+	"1.1 1.2 1.3"
+
+    thr_apply_info_thr "1 1.2-3" \
+	"1.1 1.2 1.3"
+
+    # Likewise, but mix inferiors too.
+    thr_apply_info_thr "2.1 2-3" \
+	"1.2 1.3 2.1" \
+	"2.1 1.2 1.3"
+
+    # Multiple ranges with mixed explicit inferiors.
+    thr_apply_info_thr "1.1-2 2.2-3" \
+	"1.1 1.2 2.2 2.3"
+
+    # Now test a set of invalid thread IDs/ranges.
+
+    thr_apply_info_thr_invalid "1." \
+	"1."
+
+    thr_apply_info_thr_invalid "1-3 1." \
+	"1."
+
+    thr_apply_info_thr_invalid "1.1.1" \
+	"1.1.1"
+
+    thr_apply_info_thr_invalid "2 1.1.1" \
+	"1.1.1"
+
+    thr_apply_info_thr_invalid "1.1.1 2" \
+	"1.1.1 2"
+
+    thr_apply_info_thr_invalid "1-2.1" \
+	"1-2.1"
+
+    thr_apply_info_thr_error "1-0" "inverted range"
+    thr_apply_info_thr_error "1.1-0" "inverted range"
+
+    thr_apply_info_thr_error "1-" "inverted range"
+    thr_apply_info_thr_error "1.1-" "inverted range"
+
+    thr_apply_info_thr_error "2-1" "inverted range"
+    thr_apply_info_thr_error "1.2-1" "inverted range"
+
+    thr_apply_info_thr_error "-1" "negative value"
+    thr_apply_info_thr_error "1.-1" "negative value"
+
+    # Check that we do parse the inferior number and don't confuse it.
+    gdb_test "info threads 3.1" \
+	"No threads match '3.1'\."
+}
+
+if { ![skip_python_tests] } {
+    with_test_prefix "python" {
+	# Check that InferiorThread.num returns the expected number.
+	gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" \
+	    "test gdb.selected_thread" 1
+	gdb_test "python print ('result = %s' % t0.num)" " = 3" \
+	    "test InferiorThread.num"
+    }
+}
+
+# Remove the second inferior and confirm that GDB goes back to showing
+# single-number thread IDs.
+with_test_prefix "back to one inferior" {
+    gdb_test "kill inferior 2" "" "kill inferior 2" "Kill the program being debugged.*" "y"
+    gdb_test "thread 1.1" "Switching to thread 1\.1 .*"
+    gdb_test "remove-inferior 2" ".*" "remove inferior 2"
+
+    # "info threads" while there's only inferior 1 should show
+    # single-number thread IDs.
+    info_threads "" "1 2 3"
+
+    gdb_test "thread" "Current thread is 1 .*"
+}
+
+# Add another inferior and remove inferior 1.  Since even though
+# there's a single inferior, its number is not 1, GDB should show
+# inferior-qualified thread IDs.
+with_test_prefix "single-inferior but not initial" {
+    # Add another inferior.
+    gdb_test "add-inferior" "Added inferior 3.*" "add empty inferior"
+
+    # Now that we'd added another inferior, thread IDs should show the
+    # inferior number.
+    info_threads "" "1.1 1.2 1.3" \
+	"info threads with multiple inferiors"
+
+    gdb_test "thread" "Current thread is 1\.1 .*"
+
+    gdb_test "inferior 3" "Switching to inferior 3 .*" "switch to inferior 3"
+    gdb_test "file ${binfile}" ".*" "load file in inferior 3"
+
+    runto_main
+
+    gdb_test "remove-inferior 1" ".*" "remove inferior 1"
+
+    # Even though we have a single inferior, its number is > 1, so
+    # thread IDs should include the inferior number.
+    info_threads "" "3.1" \
+	"info threads with single inferior"
+
+    gdb_test "thread" "Current thread is 3\.1 .*" "thread again"
+}
diff --git a/gdb/testsuite/gdb.threads/thread-find.exp b/gdb/testsuite/gdb.threads/thread-find.exp
index bd90c57..6d885a7 100644
--- a/gdb/testsuite/gdb.threads/thread-find.exp
+++ b/gdb/testsuite/gdb.threads/thread-find.exp
@@ -276,9 +276,9 @@ gdb_test_multiple "info threads 3-3" "info threads 3-3" {
 # Test bad input
 
 gdb_test "info thread foo" \
-    "Args must be numbers or '.' variables." \
+    "Invalid thread ID: foo" \
     "info thread foo"
 
 gdb_test "info thread foo -1" \
-    "Args must be numbers or '.' variables." \
+    "Invalid thread ID: foo -1" \
     "info thread foo -1"
diff --git a/gdb/thread.c b/gdb/thread.c
index 87861d5..64002e1 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -43,6 +43,7 @@
 #include "gdb_regex.h"
 #include "cli/cli-utils.h"
 #include "thread-fsm.h"
+#include "tid-parse.h"
 
 /* Definition of struct thread_info exported to gdbthread.h.  */
 
@@ -182,7 +183,7 @@ clear_thread_inferior_resources (struct thread_info *tp)
   delete_at_next_stop (&tp->control.exception_resume_breakpoint);
   delete_at_next_stop (&tp->control.single_step_breakpoints);
 
-  delete_longjmp_breakpoint_at_next_stop (tp->num);
+  delete_longjmp_breakpoint_at_next_stop (tp->global_num);
 
   bpstat_clear (&tp->control.stop_bpstat);
 
@@ -226,16 +227,22 @@ init_thread_list (void)
   threads_executing = 0;
 }
 
-/* Allocate a new thread with target id PTID and add it to the thread
-   list.  */
+/* Allocate a new thread of inferior INF with target id PTID and add
+   it to the thread list.  */
 
 static struct thread_info *
-new_thread (ptid_t ptid)
+new_thread (struct inferior *inf, ptid_t ptid)
 {
-  struct thread_info *tp = XCNEW (struct thread_info);
+  struct thread_info *tp;
+
+  gdb_assert (inf != NULL);
+
+  tp = XCNEW (struct thread_info);
 
   tp->ptid = ptid;
-  tp->num = ++highest_thread_num;
+  tp->global_num = ++highest_thread_num;
+  tp->inf = inf;
+  tp->per_inf_num = ++inf->highest_thread_num;
 
   if (thread_list == NULL)
     thread_list = tp;
@@ -260,6 +267,8 @@ struct thread_info *
 add_thread_silent (ptid_t ptid)
 {
   struct thread_info *tp;
+  struct inferior *inf = find_inferior_ptid (ptid);
+  gdb_assert (inf != NULL);
 
   tp = find_thread_ptid (ptid);
   if (tp)
@@ -277,7 +286,7 @@ add_thread_silent (ptid_t ptid)
 
       if (ptid_equal (inferior_ptid, ptid))
 	{
-	  tp = new_thread (null_ptid);
+	  tp = new_thread (inf, null_ptid);
 
 	  /* Make switch_to_thread not read from the thread.  */
 	  tp->state = THREAD_EXITED;
@@ -301,7 +310,7 @@ add_thread_silent (ptid_t ptid)
 	delete_thread (ptid);
     }
 
-  tp = new_thread (ptid);
+  tp = new_thread (inf, ptid);
   observer_notify_new_thread (tp);
 
   return tp;
@@ -481,12 +490,24 @@ delete_thread_silent (ptid_t ptid)
 }
 
 struct thread_info *
-find_thread_id (int num)
+find_thread_global_id (int global_id)
 {
   struct thread_info *tp;
 
   for (tp = thread_list; tp; tp = tp->next)
-    if (tp->num == num)
+    if (tp->global_num == global_id)
+      return tp;
+
+  return NULL;
+}
+
+static struct thread_info *
+find_thread_id (struct inferior *inf, int thr_num)
+{
+  struct thread_info *tp;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (tp->inf == inf && tp->per_inf_num == thr_num)
       return tp;
 
   return NULL;
@@ -548,38 +569,38 @@ thread_count (void)
 }
 
 int
-valid_thread_id (int num)
+valid_global_thread_id (int global_id)
 {
   struct thread_info *tp;
 
   for (tp = thread_list; tp; tp = tp->next)
-    if (tp->num == num)
+    if (tp->global_num == global_id)
       return 1;
 
   return 0;
 }
 
 int
-pid_to_thread_id (ptid_t ptid)
+ptid_to_global_thread_id (ptid_t ptid)
 {
   struct thread_info *tp;
 
   for (tp = thread_list; tp; tp = tp->next)
     if (ptid_equal (tp->ptid, ptid))
-      return tp->num;
+      return tp->global_num;
 
   return 0;
 }
 
 ptid_t
-thread_id_to_pid (int num)
+global_thread_id_to_ptid (int global_id)
 {
-  struct thread_info *thread = find_thread_id (num);
+  struct thread_info *thread = find_thread_global_id (global_id);
 
   if (thread)
     return thread->ptid;
   else
-    return pid_to_ptid (-1);
+    return minus_one_ptid;
 }
 
 int
@@ -604,7 +625,7 @@ first_thread_of_process (int pid)
 
   for (tp = thread_list; tp; tp = tp->next)
     if (pid == -1 || ptid_get_pid (tp->ptid) == pid)
-      if (ret == NULL || tp->num < ret->num)
+      if (ret == NULL || tp->global_num < ret->global_num)
 	ret = tp;
 
   return ret;
@@ -688,10 +709,10 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
 	continue;
 
       if (ptid_equal (tp->ptid, inferior_ptid))
-	current_thread = tp->num;
+	current_thread = tp->global_num;
 
       num++;
-      ui_out_field_int (uiout, "thread-id", tp->num);
+      ui_out_field_int (uiout, "thread-id", tp->global_num);
     }
 
   do_cleanups (cleanup_chain);
@@ -1105,25 +1126,62 @@ pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread)
 	  && pc < thread->control.step_range_end);
 }
 
-/* Prints the list of threads and their details on UIOUT.
-   This is a version of 'info_threads_command' suitable for
-   use from MI.
-   If REQUESTED_THREAD is not -1, it's the GDB id of the thread
-   that should be printed.  Otherwise, all threads are
-   printed.
-   If PID is not -1, only print threads from the process PID.
-   Otherwise, threads from all attached PIDs are printed.
-   If both REQUESTED_THREAD and PID are not -1, then the thread
-   is printed if it belongs to the specified process.  Otherwise,
-   an error is raised.  */
-void
-print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
+/* Helper for print_thread_info.  Returns true if THR should be
+   printed.  If REQUESTED_THREADS, a list of GDB ids/ranges, is not
+   NULL, only print THR if its ID is included in the list.  GLOBAL_IDS
+   is true if REQUESTED_THREADS is list of global IDs, false if a list
+   of per-inferior thread ids.  If PID is not -1, only print THR if it
+   is a thread from the process PID.  Otherwise, threads from all
+   attached PIDs are printed.  If both REQUESTED_THREADS is not NULL
+   and PID is not -1, then the thread is printed if it belongs to the
+   specified process.  Otherwise, an error is raised.  */
+
+static int
+should_print_thread (const char *requested_threads, int default_inf_num,
+		     int global_ids, int pid, struct thread_info *thr)
+{
+  if (requested_threads != NULL && *requested_threads != '\0')
+    {
+      int in_list;
+
+      if (global_ids)
+	in_list = number_is_in_list (requested_threads, thr->global_num);
+      else
+	in_list = tid_is_in_list (requested_threads, default_inf_num,
+				  thr->inf->num, thr->per_inf_num);
+      if (!in_list)
+	return 0;
+    }
+
+  if (pid != -1 && ptid_get_pid (thr->ptid) != pid)
+    {
+      if (requested_threads != NULL && *requested_threads != '\0')
+	error (_("Requested thread not found in requested process"));
+      return 0;
+    }
+
+  if (thr->state == THREAD_EXITED)
+    return 0;
+
+  return 1;
+}
+
+/* Like print_thread_info, but in addition, GLOBAL_IDS indicates
+   whether REQUESTED_THREADS is a list of global or per-inferior
+   thread ids.  */
+
+static void
+print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
+		     int global_ids, int pid,
+		     int show_global_ids)
 {
   struct thread_info *tp;
   ptid_t current_ptid;
   struct cleanup *old_chain;
   const char *extra_info, *name, *target_id;
   int current_thread = -1;
+  struct inferior *inf;
+  int default_inf_num = current_inferior ()->num;
 
   update_thread_list ();
   current_ptid = inferior_ptid;
@@ -1142,13 +1200,8 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 
       for (tp = thread_list; tp; tp = tp->next)
 	{
-	  if (!number_is_in_list (requested_threads, tp->num))
-	    continue;
-
-	  if (pid != -1 && ptid_get_pid (tp->ptid) != pid)
-	    continue;
-
-	  if (tp->state == THREAD_EXITED)
+	  if (!should_print_thread (requested_threads, default_inf_num,
+				    global_ids, pid, tp))
 	    continue;
 
 	  ++n_threads;
@@ -1165,34 +1218,32 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 	  return;
 	}
 
-      make_cleanup_ui_out_table_begin_end (uiout, 4, n_threads, "threads");
+      if (show_global_ids || ui_out_is_mi_like_p (uiout))
+	make_cleanup_ui_out_table_begin_end (uiout, 5, n_threads, "threads");
+      else
+	make_cleanup_ui_out_table_begin_end (uiout, 4, n_threads, "threads");
 
       ui_out_table_header (uiout, 1, ui_left, "current", "");
-      ui_out_table_header (uiout, 4, ui_left, "id", "Id");
+
+      if (!ui_out_is_mi_like_p (uiout))
+	ui_out_table_header (uiout, 4, ui_left, "id-in-tg", "Id");
+      if (show_global_ids || ui_out_is_mi_like_p (uiout))
+	ui_out_table_header (uiout, 4, ui_left, "id", "GId");
       ui_out_table_header (uiout, 17, ui_left, "target-id", "Target Id");
       ui_out_table_header (uiout, 1, ui_left, "frame", "Frame");
       ui_out_table_body (uiout);
     }
 
-  for (tp = thread_list; tp; tp = tp->next)
+  ALL_THREADS_BY_INFERIOR (inf, tp)
     {
       struct cleanup *chain2;
       int core;
 
-      if (!number_is_in_list (requested_threads, tp->num))
-	continue;
-
-      if (pid != -1 && ptid_get_pid (tp->ptid) != pid)
-	{
-	  if (requested_threads != NULL && *requested_threads != '\0')
-	    error (_("Requested thread not found in requested process"));
-	  continue;
-	}
-
       if (ptid_equal (tp->ptid, current_ptid))
-	current_thread = tp->num;
+	current_thread = tp->global_num;
 
-      if (tp->state == THREAD_EXITED)
+      if (!should_print_thread (requested_threads, default_inf_num,
+				global_ids, pid, tp))
 	continue;
 
       chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -1213,7 +1264,11 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 	    ui_out_field_skip (uiout, "current");
 	}
 
-      ui_out_field_int (uiout, "id", tp->num);
+      if (!ui_out_is_mi_like_p (uiout))
+	ui_out_field_string (uiout, "id-in-tg", print_thread_id (tp));
+
+      if (show_global_ids || ui_out_is_mi_like_p (uiout))
+	ui_out_field_int (uiout, "id", tp->global_num);
 
       /* For the CLI, we stuff everything into the target-id field.
 	 This is a gross hack to make the output come out looking
@@ -1289,27 +1344,35 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 
   if (pid == -1 && requested_threads == NULL)
     {
-      gdb_assert (current_thread != -1
-		  || !thread_list
-		  || ptid_equal (inferior_ptid, null_ptid));
-      if (current_thread != -1 && ui_out_is_mi_like_p (uiout))
-	ui_out_field_int (uiout, "current-thread-id", current_thread);
+      if (ui_out_is_mi_like_p (uiout)
+	  && !ptid_equal (inferior_ptid, null_ptid))
+	{
+	  int num = ptid_to_global_thread_id (inferior_ptid);
+
+	  gdb_assert (num != 0);
+	  ui_out_field_int (uiout, "current-thread-id", num);
+	}
 
-      if (current_thread != -1 && is_exited (current_ptid))
+      if (!ptid_equal (inferior_ptid, null_ptid) && is_exited (inferior_ptid))
 	ui_out_message (uiout, 0, "\n\
-The current thread <Thread ID %d> has terminated.  See `help thread'.\n",
-			current_thread);
-      else if (thread_list
-	       && current_thread == -1
-	       && ptid_equal (current_ptid, null_ptid))
+The current thread <Thread ID %s> has terminated.  See `help thread'.\n",
+			print_thread_id (inferior_thread ()));
+      else if (thread_list != NULL
+	       && ptid_equal (inferior_ptid, null_ptid))
 	ui_out_message (uiout, 0, "\n\
 No selected thread.  See `help thread'.\n");
     }
 }
 
-/* Print information about currently known threads 
+/* See gdbthread.h.  */
 
-   Optional ARG is a thread id, or list of thread ids.
+void
+print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
+{
+  print_thread_info_1 (uiout, requested_threads, 1, pid, 0);
+}
+
+/* Implementation of the "info threads" command.
 
    Note: this has the drawback that it _really_ switches
          threads, which frees the frame cache.  A no-side
@@ -1318,7 +1381,7 @@ No selected thread.  See `help thread'.\n");
 static void
 info_threads_command (char *arg, int from_tty)
 {
-  print_thread_info (current_uiout, arg, -1);
+  print_thread_info_1 (current_uiout, arg, 0, -1, 0);
 }
 
 /* See gdbthread.h.  */
@@ -1590,12 +1653,23 @@ make_cleanup_restore_current_thread (void)
 
 /* See gdbthread.h.  */
 
+int
+show_inferior_qualified_tids (void)
+{
+  return (inferior_list->next != NULL || inferior_list->num != 1);
+}
+
+/* See gdbthread.h.  */
+
 const char *
 print_thread_id (struct thread_info *thr)
 {
   char *s = get_print_cell ();
 
-  xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->num);
+  if (show_inferior_qualified_tids ())
+    xsnprintf (s, PRINT_CELL_SIZE, "%d.%d", thr->inf->num, thr->per_inf_num);
+  else
+    xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->per_inf_num);
   return s;
 }
 
@@ -1604,18 +1678,24 @@ print_thread_id (struct thread_info *thr)
 
 static int tp_array_compar_ascending;
 
-/* Sort an array for struct thread_info pointers by their NUM, order is
-   determined by TP_ARRAY_COMPAR_ASCENDING.  */
+/* Sort an array for struct thread_info pointers by thread ID (first
+   by inferior number, and then by per-inferior thread number).  The
+   order is determined by TP_ARRAY_COMPAR_ASCENDING.  */
 
 static int
 tp_array_compar (const void *ap_voidp, const void *bp_voidp)
 {
-  const struct thread_info *const *ap
-    = (const struct thread_info * const*) ap_voidp;
-  const struct thread_info *const *bp
-    = (const struct thread_info * const*) bp_voidp;
+  const struct thread_info *a = *(const struct thread_info * const *) ap_voidp;
+  const struct thread_info *b = *(const struct thread_info * const *) bp_voidp;
+
+  if (a->inf->num != b->inf->num)
+    {
+      return ((a->inf->num > b->inf->num) - (a->inf->num < b->inf->num)
+	      * (tp_array_compar_ascending ? +1 : -1));
+    }
 
-  return ((((*ap)->num > (*bp)->num) - ((*ap)->num < (*bp)->num))
+  return (((a->per_inf_num > b->per_inf_num)
+	   - (a->per_inf_num < b->per_inf_num))
 	  * (tp_array_compar_ascending ? +1 : -1));
 }
 
@@ -1710,7 +1790,7 @@ thread_apply_command (char *tidlist, int from_tty)
   char *cmd;
   struct cleanup *old_chain;
   char *saved_cmd;
-  struct get_number_or_range_state state;
+  struct tid_range_parser parser;
 
   if (tidlist == NULL || *tidlist == '\000')
     error (_("Please specify a thread ID list"));
@@ -1725,33 +1805,44 @@ thread_apply_command (char *tidlist, int from_tty)
   saved_cmd = xstrdup (cmd);
   old_chain = make_cleanup (xfree, saved_cmd);
 
-  init_number_or_range (&state, tidlist);
-  while (!state.finished && state.string < cmd)
-    {
-      struct thread_info *tp;
-      int start;
-
-      start = get_number_or_range (&state);
+  make_cleanup_restore_current_thread ();
 
-      make_cleanup_restore_current_thread ();
+  tid_range_parser_init (&parser, tidlist, current_inferior ()->num);
+  while (!tid_range_parser_finished (&parser)
+	 && tid_range_parser_string (&parser) < cmd)
+    {
+      struct thread_info *tp = NULL;
+      struct inferior *inf;
+      int inf_num, thr_num;
 
-      tp = find_thread_id (start);
+      tid_range_parser_get_tid (&parser, &inf_num, &thr_num);
+      inf = find_inferior_id (inf_num);
+      if (inf != NULL)
+	tp = find_thread_id (inf, thr_num);
+      if (tp == NULL)
+	{
+	  if (show_inferior_qualified_tids ()
+	      || tid_range_parser_qualified (&parser))
+	    warning (_("Unknown thread %d.%d"), inf_num, thr_num);
+	  else
+	    warning (_("Unknown thread %d"), thr_num);
+	  continue;
+	}
 
-      if (!tp)
-	warning (_("Unknown thread %d."), start);
-      else if (!thread_alive (tp))
-	warning (_("Thread %d has terminated."), start);
-      else
+      if (!thread_alive (tp))
 	{
-	  switch_to_thread (tp->ptid);
+	  warning (_("Thread %s has terminated."), print_thread_id (tp));
+	  continue;
+	}
 
-	  printf_filtered (_("\nThread %d (%s):\n"), tp->num,
-			   target_pid_to_str (inferior_ptid));
-	  execute_command (cmd, from_tty);
+      switch_to_thread (tp->ptid);
 
-	  /* Restore exact command used previously.  */
-	  strcpy (cmd, saved_cmd);
-	}
+      printf_filtered (_("\nThread %s (%s):\n"), print_thread_id (tp),
+		       target_pid_to_str (inferior_ptid));
+      execute_command (cmd, from_tty);
+
+      /* Restore exact command used previously.  */
+      strcpy (cmd, saved_cmd);
     }
 
   do_cleanups (old_chain);
@@ -1872,30 +1963,42 @@ show_print_thread_events (struct ui_file *file, int from_tty,
 }
 
 static int
-do_captured_thread_select (struct ui_out *uiout, void *tidstr)
+do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
 {
-  int num;
+  const char *tidstr = tidstr_v;
   struct thread_info *tp;
 
-  num = value_as_long (parse_and_eval ((const char *) tidstr));
-
-  tp = find_thread_id (num);
+  if (ui_out_is_mi_like_p (uiout))
+    {
+      int num = value_as_long (parse_and_eval (tidstr));
 
-  if (!tp)
-    error (_("Thread ID %d not known."), num);
+      tp = find_thread_global_id (num);
+      if (tp == NULL)
+	error (_("Thread ID %d not known."), num);
+    }
+  else
+    {
+      tp = parse_thread_id (tidstr, NULL);
+      gdb_assert (tp != NULL);
+    }
 
   if (!thread_alive (tp))
-    error (_("Thread ID %d has terminated."), num);
+    error (_("Thread ID %s has terminated."), tidstr);
 
   switch_to_thread (tp->ptid);
 
   annotate_thread_changed ();
 
-  ui_out_text (uiout, "[Switching to thread ");
-  ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp));
-  ui_out_text (uiout, " (");
-  ui_out_text (uiout, target_pid_to_str (inferior_ptid));
-  ui_out_text (uiout, ")]");
+  if (ui_out_is_mi_like_p (uiout))
+    ui_out_field_int (uiout, "new-thread-id", inferior_thread ()->global_num);
+  else
+    {
+      ui_out_text (uiout, "[Switching to thread ");
+      ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp));
+      ui_out_text (uiout, " (");
+      ui_out_text (uiout, target_pid_to_str (inferior_ptid));
+      ui_out_text (uiout, ")]");
+    }
 
   /* Note that we can't reach this with an exited thread, due to the
      thread_alive check above.  */
@@ -1949,8 +2052,9 @@ update_thread_list (void)
   update_threads_executing ();
 }
 
-/* Return a new value for the selected thread's id.  Return a value of 0 if
-   no thread is selected, or no threads exist.  */
+/* Return a new value for the selected thread's per-inferior thread
+   number.  Return a value of 0 if no thread is selected, or no
+   threads exist.  */
 
 static struct value *
 thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
@@ -1959,7 +2063,7 @@ thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
   struct thread_info *tp = find_thread_ptid (inferior_ptid);
 
   return value_from_longest (builtin_type (gdbarch)->builtin_int,
-			     (tp ? tp->num : 0));
+			     (tp ? tp->per_inf_num : 0));
 }
 
 /* Commands with a prefix of `thread'.  */
@@ -1982,8 +2086,8 @@ _initialize_thread (void)
   add_info ("threads", info_threads_command, 
 	    _("Display currently known threads.\n\
 Usage: info threads [ID]...\n\
-Optional arguments are thread IDs with spaces between.\n\
-If no arguments, all threads are displayed."));
+If ID is given, it is a space-separated list of IDs of threads to display.\n\
+Otherwise, all threads are displayed."));
 
   add_prefix_cmd ("thread", class_run, thread_command, _("\
 Use this command to switch between threads.\n\
diff --git a/gdb/tid-parse.c b/gdb/tid-parse.c
new file mode 100644
index 0000000..7c30733
--- /dev/null
+++ b/gdb/tid-parse.c
@@ -0,0 +1,267 @@
+/* TID parsing for GDB, the GNU debugger.
+
+   Copyright (C) 2015-2016 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 "tid-parse.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include <ctype.h>
+
+/* See tid-parse.h.  */
+
+void ATTRIBUTE_NORETURN
+invalid_thread_id_error (const char *string)
+{
+  error (_("Invalid thread ID: %s"), string);
+}
+
+/* See tid-parse.h.  */
+
+struct thread_info *
+parse_thread_id (const char *tidstr, const char **end)
+{
+  const char *number = tidstr;
+  const char *dot, *p1;
+  struct thread_info *tp;
+  struct inferior *inf;
+  int thr_num;
+  int explicit_inf_id = 0;
+
+  dot = strchr (number, '.');
+
+  if (dot != NULL)
+    {
+      /* Parse number to the left of the dot.  */
+      int inf_num;
+
+      p1 = number;
+      inf_num = get_number_trailer (&p1, '.');
+      if (inf_num == 0)
+	invalid_thread_id_error (number);
+
+      inf = find_inferior_id (inf_num);
+      if (inf == NULL)
+	error (_("No inferior number '%d'"), inf_num);
+
+      explicit_inf_id = 1;
+      p1 = dot + 1;
+    }
+  else
+    {
+      inf = current_inferior ();
+
+      p1 = number;
+    }
+
+  thr_num = get_number_const (&p1);
+  if (thr_num == 0)
+    invalid_thread_id_error (number);
+
+  ALL_THREADS (tp)
+    {
+      if (ptid_get_pid (tp->ptid) == inf->pid
+	  && tp->per_inf_num == thr_num)
+	break;
+    }
+
+  if (tp == NULL)
+    {
+      if (show_inferior_qualified_tids () || explicit_inf_id)
+	error (_("Unknown thread %d.%d."), inf->num, thr_num);
+      else
+	error (_("Unknown thread %d."), thr_num);
+    }
+
+  if (end != NULL)
+    *end = p1;
+
+  return tp;
+}
+
+/* See tid-parse.h.  */
+
+void
+tid_range_parser_init (struct tid_range_parser *parser, const char *tidlist,
+		       int default_inferior)
+{
+  parser->state = TID_RANGE_STATE_INFERIOR;
+  parser->string = tidlist;
+  parser->inf_num = 0;
+  parser->qualified = 0;
+  parser->default_inferior = default_inferior;
+}
+
+/* See tid-parse.h.  */
+
+int
+tid_range_parser_finished (struct tid_range_parser *parser)
+{
+  switch (parser->state)
+    {
+    case TID_RANGE_STATE_INFERIOR:
+      return *parser->string == '\0';
+    case TID_RANGE_STATE_THREAD_RANGE:
+      return parser->range_parser.finished;
+    }
+
+  gdb_assert_not_reached (_("unhandled state"));
+}
+
+/* See tid-parse.h.  */
+
+const char *
+tid_range_parser_string (struct tid_range_parser *parser)
+{
+  switch (parser->state)
+    {
+    case TID_RANGE_STATE_INFERIOR:
+      return parser->string;
+    case TID_RANGE_STATE_THREAD_RANGE:
+      return parser->range_parser.string;
+    }
+
+  gdb_assert_not_reached (_("unhandled state"));
+}
+
+/* See tid-parse.h.  */
+
+void
+tid_range_parser_skip (struct tid_range_parser *parser)
+{
+  gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE)
+	      && parser->range_parser.in_range);
+
+  tid_range_parser_init (parser, parser->range_parser.end_ptr,
+			 parser->default_inferior);
+}
+
+/* See tid-parse.h.  */
+
+int
+tid_range_parser_qualified (struct tid_range_parser *parser)
+{
+  return parser->qualified;
+}
+
+/* See tid-parse.h.  */
+
+int
+tid_range_parser_get_tid_range (struct tid_range_parser *parser, int *inf_num,
+				int *thr_start, int *thr_end)
+{
+  if (parser->state == TID_RANGE_STATE_INFERIOR)
+    {
+      const char *p;
+      const char *space;
+
+      space = skip_to_space (parser->string);
+
+      p = parser->string;
+      while (p < space && *p != '.')
+	p++;
+      if (p < space)
+	{
+	  const char *dot = p;
+
+	  /* Parse number to the left of the dot.  */
+	  p = parser->string;
+	  parser->inf_num = get_number_trailer (&p, '.');
+	  if (parser->inf_num == 0)
+	    invalid_thread_id_error (parser->string);
+
+	  parser->qualified = 1;
+	  p = dot + 1;
+
+	  if (isspace (*p))
+	    invalid_thread_id_error (parser->string);
+	}
+      else
+	{
+	  parser->inf_num = parser->default_inferior;
+	  parser->qualified = 0;
+	  p = parser->string;
+	}
+
+      init_number_or_range (&parser->range_parser, p);
+      parser->state = TID_RANGE_STATE_THREAD_RANGE;
+    }
+
+  *inf_num = parser->inf_num;
+  *thr_start = get_number_or_range (&parser->range_parser);
+  if (*thr_start == 0)
+    invalid_thread_id_error (parser->string);
+
+  /* If we successfully parsed a thread number or finished parsing a
+     thread range, switch back to assuming the next TID is
+     inferior-qualified.  */
+  if (parser->range_parser.end_ptr == NULL
+      || parser->range_parser.string == parser->range_parser.end_ptr)
+    {
+      parser->state = TID_RANGE_STATE_INFERIOR;
+      parser->string = parser->range_parser.string;
+
+      if (thr_end != NULL)
+	*thr_end = *thr_start;
+    }
+
+  /* If we're midway through a range, and the caller wants the end
+     value, return it and skip to the end of the range.  */
+  if (thr_end != NULL && parser->state == TID_RANGE_STATE_THREAD_RANGE)
+    {
+      *thr_end = parser->range_parser.end_value;
+      tid_range_parser_skip (parser);
+    }
+
+  return (*inf_num != 0 && *thr_start != 0);
+}
+
+/* See tid-parse.h.  */
+
+void
+tid_range_parser_get_tid (struct tid_range_parser *parser,
+			  int *inf_num, int *thr_num)
+{
+  tid_range_parser_get_tid_range (parser, inf_num, thr_num, NULL);
+}
+
+/* See tid-parse.h.  */
+
+int
+tid_is_in_list (const char *list, int default_inferior,
+		int inf_num, int thr_num)
+{
+  struct tid_range_parser parser;
+
+  if (list == NULL || *list == '\0')
+    return 1;
+
+  tid_range_parser_init (&parser, list, default_inferior);
+  while (!tid_range_parser_finished (&parser))
+    {
+      int tmp_inf, tmp_thr_start, tmp_thr_end;
+
+      if (!tid_range_parser_get_tid_range (&parser, &tmp_inf,
+					   &tmp_thr_start, &tmp_thr_end))
+	invalid_thread_id_error (parser.string);
+      if (tmp_inf == inf_num
+	  && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
+	return 1;
+    }
+  return 0;
+}
diff --git a/gdb/tid-parse.h b/gdb/tid-parse.h
new file mode 100644
index 0000000..3c79c08
--- /dev/null
+++ b/gdb/tid-parse.h
@@ -0,0 +1,148 @@
+/* TID parsing for GDB, the GNU debugger.
+   Copyright (C) 2015-2016 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 TID_PARSE_H
+#define TID_PARSE_H
+
+#include "cli/cli-utils.h"
+
+struct thread_info;
+
+/* Issue an invalid thread ID error, pointing at STRING, the invalid
+   ID.  */
+extern void ATTRIBUTE_NORETURN invalid_thread_id_error (const char *string);
+
+/* Parse TIDSTR as a per-inferior thread ID, in either INF_NUM.THR_NUM
+   or THR_NUM form.  In the latter case, the missing INF_NUM is filled
+   in from the current inferior.  If ENDPTR is not NULL,
+   parse_thread_id stores the address of the first character after the
+   thread ID.  Either a valid thread is returned, or an error is
+   thrown.  */
+struct thread_info *parse_thread_id (const char *tidstr, const char **end);
+
+/* The possible states of the tid range parser's state machine.  */
+enum tid_range_state
+{
+  /* Parsing the inferior number.  */
+  TID_RANGE_STATE_INFERIOR,
+
+  /* Parsing the thread number or thread number range.  */
+  TID_RANGE_STATE_THREAD_RANGE,
+};
+
+/* An object of this type is passed to tid_range_parser_get_tid.  It
+   must be initialized by calling tid_range_parser_init.  This type is
+   defined here so that it can be stack-allocated, but all members
+   should be treated as opaque.  */
+struct tid_range_parser
+{
+  /* What sub-component are we expecting.  */
+  enum tid_range_state state;
+
+  /* The string being parsed.  When parsing has finished, this points
+     past the last parsed token.  */
+  const char *string;
+
+  /* The range parser state when we're parsing the thread number
+     sub-component.  */
+  struct get_number_or_range_state range_parser;
+
+  /* Last inferior number returned.  */
+  int inf_num;
+
+  /* True if the TID last parsed was explicitly inferior-qualified.
+     IOW, whether the spec specified an inferior number
+     explicitly.  */
+  int qualified;
+
+  /* The inferior number to assume if the TID is not qualified.  */
+  int default_inferior;
+};
+
+/* Initialize a tid_range_parser for use with
+   tid_range_parser_get_tid.  TIDLIST is the string to be parsed.
+   DEFAULT_INFERIOR is the inferior number to assume if a
+   non-qualified thread ID is found.  */
+extern void tid_range_parser_init (struct tid_range_parser *parser,
+				   const char *tidlist,
+				   int default_inferior);
+
+/* Parse a thread ID or a thread range list.
+
+   A range will be of the form
+   <inferior_num>.<thread_number1>-<thread_number1> and will represent
+   all the threads of inferior inferior_num with number between
+   thread_number1 and thread_number2, inclusive.  <inferior_num> can
+   also be omitted, as in <thread_number1>-<thread_number1>, in which
+   case GDB infers the inferior number from the current inferior.
+
+   While processing a thread ID range list, this function is called
+   iteratively; at each call it will return (in the INF_NUM and
+   THR_NUM output parameters) the next thread in the range.
+
+   At the beginning of parsing a thread range, the char pointer
+   PARSER->string will be advanced past <thread_number1> and left
+   pointing at the '-' token.  Subsequent calls will not advance the
+   pointer until the range is completed.  The call that completes the
+   range will advance the pointer past <thread_number2>.  */
+extern void tid_range_parser_get_tid (struct tid_range_parser *parser,
+				      int *inf_num, int *thr_num);
+
+/* Like tid_range_parser_get_tid, but return a thread range per call,
+   rather then a single thread.
+
+   If the next element in the list is a single thread ID, then
+   *THR_START and *THR_END are set to the same value.  E.g.:
+
+     list: "1.2 3.4-6"
+     1st call: *INF_NUM=1; *THR_START=2; *THR_END=2
+     2nd call: *INF_NUM=3; *THR_START=4; *THR_END=4
+
+
+   Returns true if parsed a thread/range successfully, false
+   otherwise.  */
+extern int tid_range_parser_get_tid_range (struct tid_range_parser *parser,
+					   int *inf_num,
+					   int *thr_start, int *thr_end);
+
+/* Returns non-zero if parsing has completed.  */
+extern int tid_range_parser_finished (struct tid_range_parser *parser);
+
+/* Return the string being parsed.  When parsing has finished, this
+   points past the last parsed token.  */
+const char *tid_range_parser_string (struct tid_range_parser *parser);
+
+/* When parsing a range, advance past the final token in the range.  */
+extern void tid_range_parser_skip (struct tid_range_parser *parser);
+
+/* True if the TID last parsed was explicitly inferior-qualified.
+   IOW, whether the spec specified an inferior number explicitly.  */
+extern int tid_range_parser_qualified (struct tid_range_parser *parser);
+
+/* Accept a string-form list of thread IDs such as is accepted by
+   tid_range_parser_get_tid.  Return TRUE if the INF_NUM.THR.NUM
+   thread is in the list.  DEFAULT_INFERIOR is the inferior number to
+   assume if a non-qualified thread ID is found in the list.
+
+   By definition, an empty list includes all threads.  This is to be
+   interpreted as typing a command such as "info threads" with no
+   arguments.  */
+extern int tid_is_in_list (const char *list, int default_inferior,
+			   int inf_num, int thr_num);
+
+#endif /* TID_PARSE_H */
diff --git a/gdb/varobj.c b/gdb/varobj.c
index d47bdc2..8116a5b 100644
--- a/gdb/varobj.c
+++ b/gdb/varobj.c
@@ -78,7 +78,7 @@ struct varobj_root
      not NULL.  */
   struct frame_id frame;
 
-  /* The thread ID that this varobj_root belong to.  This field
+  /* The global thread ID that this varobj_root belongs to.  This field
      is only valid if valid_block is not NULL.
      When not 0, indicates which thread 'frame' belongs to.
      When 0, indicates that the thread list was empty when the varobj_root
@@ -380,7 +380,7 @@ varobj_create (char *objname,
 	    error (_("Failed to find the specified frame"));
 
 	  var->root->frame = get_frame_id (fi);
-	  var->root->thread_id = pid_to_thread_id (inferior_ptid);
+	  var->root->thread_id = ptid_to_global_thread_id (inferior_ptid);
 	  old_id = get_frame_id (get_selected_frame (NULL));
 	  select_frame (fi);	 
 	}
@@ -2363,8 +2363,9 @@ value_of_root_1 (struct varobj **var_handle)
     }
   else
     {
-      ptid_t ptid = thread_id_to_pid (var->root->thread_id);
-      if (in_thread_list (ptid))
+      ptid_t ptid = global_thread_id_to_ptid (var->root->thread_id);
+
+      if (!ptid_equal (minus_one_ptid, ptid))
 	{
 	  switch_to_thread (ptid);
 	  within_scope = check_scope (var);
