diff --git a/gdb/NEWS b/gdb/NEWS
index 9ca7f49..1d22da6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,45 @@
 
 *** Changes since GDB 7.10
 
+* Per-inferior thread IDs
+
+  Thread numbers are now per inferior instead of global.  If you're
+  debugging multiple inferiors, GDB displays thread IDs using an
+  expanded 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)
+
+  Commands that accept thread IDs now accept the expanded form as
+  well:
+
+     (gdb) thread 2.1
+     [Switching to thread 2.1 (Thread 0x7ffff7fc2740 (LWP 8157))] (running)
+     (gdb)
+
+  As consequence, thread IDs as visible in the $_thread convenience
+  variable 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 IDs in
+  previous releases.
+
+  For backwards compatibility, MI's thread IDs always refer to the
+  global ID.
+
+* You can use "info threads -gid" to display the global thread ID of
+  all threads.
+
+* The new convenience variable $_gthread holds the global number of
+  the current thread.
+
+* The new convenience variable $_inferior holds the number of the
+  current inferior.
+
 * Record btrace now supports non-stop mode.
 
 * Support for tracepoints on aarch64-linux was added in GDBserver.
@@ -141,6 +180,14 @@ show remote exec-event-feature-packet
      format.  It outputs data in hexadecimal format with zero-padding on the
      left.
 
+* Python Scripting
+
+  ** gdb.InferiorThread objects have a new attribute "global_num",
+     which refers to the thread's global thread ID.  The existing
+     "num" attribute now refers to the thread's per-inferior ID.
+  ** gdb.InferiorThread objects have a new attribute "inferior", which
+     is the Inferior object the thread belongs to.
+
 *** Changes in GDB 7.10
 
 * Support for process record-replay and reverse debugging on aarch64*-linux*
diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c
index e2194dd..5743a88 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 f105042..1453d9c 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3144,7 +3144,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,13 +3244,13 @@ 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_id && user_breakpoint_p (b))
 	{
 	  b->disposition = disp_del_at_next_stop;
 
 	  printf_filtered (_("\
-Thread-specific breakpoint %d deleted - thread %d no longer in the thread list.\n"),
-			  b->number, tp->num);
+Thread-specific breakpoint %d deleted - thread %s no longer in the thread list.\n"),
+			   b->number, print_thread_id (tp));
 
 	  /* Hide it from the user.  */
 	  b->number = 0;
@@ -5447,7 +5447,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)))
 
     {
@@ -6516,7 +6516,14 @@ print_one_breakpoint_location (struct breakpoint *b,
     {
       /* FIXME should make an annotation for this.  */
       ui_out_text (uiout, "\tstop only in thread ");
-      ui_out_field_int (uiout, "thread", b->thread);
+      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");
     }
   
@@ -7572,7 +7579,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_id;
 
   /* To avoid having to rescan all objfile symbols at every step,
      we maintain a list of continually-inserted but always disabled
@@ -7641,7 +7648,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.  */
 
@@ -7671,7 +7678,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_id)
       {
 	struct breakpoint *dummy_b = b->related_breakpoint;
 
@@ -8891,7 +8898,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);
 
@@ -9565,14 +9572,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.
@@ -9621,14 +9620,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_id;
 	  tok = tmptok;
 	}
       else if (toklen >= 1 && strncmp (tok, "task", toklen) == 0)
@@ -11133,25 +11132,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);
 
-	      /* Check if the thread actually exists.  */
-	      if (!valid_thread_id (thread))
-		invalid_thread_id_error (thread);
+	      thread = thr->global_id;
 	    }
 	  else if (toklen == 4 && startswith (tok, "mask"))
 	    {
@@ -11692,7 +11689,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
   resolve_sal_pc (&sal);
 
   tp = inferior_thread ();
-  thread = tp->num;
+  thread = tp->global_id;
 
   old_chain = make_cleanup (null_cleanup, NULL);
 
@@ -11742,7 +11739,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_id,
+			    location_breakpoint, caller_breakpoint);
   tp->thread_fsm = &sm->thread_fsm;
 
   discard_cleanups (old_chain);
@@ -13364,7 +13362,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;
@@ -14545,7 +14543,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
@@ -15056,7 +15054,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_id, gdbarch);
     }
 
   sal = find_pc_line (pc, 0);
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 35431cb..01bc427 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -1043,7 +1043,8 @@ btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
   if (!target_supports_btrace (conf->format))
     error (_("Target does not support branch tracing."));
 
-  DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("enable thread %s (%s)", print_thread_id (tp),
+	 target_pid_to_str (tp->ptid));
 
   tp->btrace.target = target_enable_btrace (tp->ptid, conf);
 
@@ -1075,7 +1076,8 @@ btrace_disable (struct thread_info *tp)
   if (btp->target == NULL)
     return;
 
-  DEBUG ("disable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("disable thread %s (%s)", print_thread_id (tp),
+	 target_pid_to_str (tp->ptid));
 
   target_disable_btrace (btp->target);
   btp->target = NULL;
@@ -1094,7 +1096,8 @@ btrace_teardown (struct thread_info *tp)
   if (btp->target == NULL)
     return;
 
-  DEBUG ("teardown thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("teardown thread %s (%s)", print_thread_id (tp),
+	 target_pid_to_str (tp->ptid));
 
   target_teardown_btrace (btp->target);
   btp->target = NULL;
@@ -1268,7 +1271,8 @@ btrace_fetch (struct thread_info *tp)
   struct cleanup *cleanup;
   int errcode;
 
-  DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("fetch thread %s (%s)", print_thread_id (tp),
+	 target_pid_to_str (tp->ptid));
 
   btinfo = &tp->btrace;
   tinfo = btinfo->target;
@@ -1340,7 +1344,8 @@ btrace_clear (struct thread_info *tp)
   struct btrace_thread_info *btinfo;
   struct btrace_function *it, *trash;
 
-  DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+  DEBUG ("clear thread %s (%s)", print_thread_id (tp),
+	 target_pid_to_str (tp->ptid));
 
   /* Make sure btrace frames that may hold a pointer into the branch
      trace data are destroyed.  */
diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index 5c6338b..828c895 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 ad46581..9ee8f09 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/common/print-utils.c b/gdb/common/print-utils.c
index 7eba07d..a8914b1 100644
--- a/gdb/common/print-utils.c
+++ b/gdb/common/print-utils.c
@@ -22,14 +22,13 @@
 /* Temporary storage using circular buffer.  */
 
 #define NUMCELLS 16
-#define CELLSIZE 50
 
 /* Return the next entry in the circular buffer.  */
 
-static char *
-get_cell (void)
+char *
+get_print_cell (void)
 {
-  static char buf[NUMCELLS][CELLSIZE];
+  static char buf[NUMCELLS][PRINT_CELL_SIZE];
   static int cell = 0;
 
   if (++cell >= NUMCELLS)
@@ -43,7 +42,7 @@ decimal2str (char *sign, ULONGEST addr, int width)
   /* Steal code from valprint.c:print_decimal().  Should this worry
      about the real size of addr as the above does?  */
   unsigned long temp[3];
-  char *str = get_cell ();
+  char *str = get_print_cell ();
   int i = 0;
 
   do
@@ -62,14 +61,14 @@ decimal2str (char *sign, ULONGEST addr, int width)
   switch (i)
     {
     case 1:
-      xsnprintf (str, CELLSIZE, "%s%0*lu", sign, width, temp[0]);
+      xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu", sign, width, temp[0]);
       break;
     case 2:
-      xsnprintf (str, CELLSIZE, "%s%0*lu%09lu", sign, width,
+      xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu%09lu", sign, width,
 		 temp[1], temp[0]);
       break;
     case 3:
-      xsnprintf (str, CELLSIZE, "%s%0*lu%09lu%09lu", sign, width,
+      xsnprintf (str, PRINT_CELL_SIZE, "%s%0*lu%09lu%09lu", sign, width,
 		 temp[2], temp[1], temp[0]);
       break;
     default:
@@ -84,7 +83,7 @@ static char *
 octal2str (ULONGEST addr, int width)
 {
   unsigned long temp[3];
-  char *str = get_cell ();
+  char *str = get_print_cell ();
   int i = 0;
 
   do
@@ -104,15 +103,15 @@ octal2str (ULONGEST addr, int width)
     {
     case 1:
       if (temp[0] == 0)
-	xsnprintf (str, CELLSIZE, "%*o", width, 0);
+	xsnprintf (str, PRINT_CELL_SIZE, "%*o", width, 0);
       else
-	xsnprintf (str, CELLSIZE, "0%0*lo", width, temp[0]);
+	xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo", width, temp[0]);
       break;
     case 2:
-      xsnprintf (str, CELLSIZE, "0%0*lo%010lo", width, temp[1], temp[0]);
+      xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo%010lo", width, temp[1], temp[0]);
       break;
     case 3:
-      xsnprintf (str, CELLSIZE, "0%0*lo%010lo%010lo", width,
+      xsnprintf (str, PRINT_CELL_SIZE, "0%0*lo%010lo%010lo", width,
 		 temp[2], temp[1], temp[0]);
       break;
     default:
@@ -155,18 +154,18 @@ phex (ULONGEST l, int sizeof_l)
   switch (sizeof_l)
     {
     case 8:
-      str = get_cell ();
-      xsnprintf (str, CELLSIZE, "%08lx%08lx",
+      str = get_print_cell ();
+      xsnprintf (str, PRINT_CELL_SIZE, "%08lx%08lx",
 		 (unsigned long) (l >> thirty_two),
 		 (unsigned long) (l & 0xffffffff));
       break;
     case 4:
-      str = get_cell ();
-      xsnprintf (str, CELLSIZE, "%08lx", (unsigned long) l);
+      str = get_print_cell ();
+      xsnprintf (str, PRINT_CELL_SIZE, "%08lx", (unsigned long) l);
       break;
     case 2:
-      str = get_cell ();
-      xsnprintf (str, CELLSIZE, "%04x", (unsigned short) (l & 0xffff));
+      str = get_print_cell ();
+      xsnprintf (str, PRINT_CELL_SIZE, "%04x", (unsigned short) (l & 0xffff));
       break;
     default:
       str = phex (l, sizeof (l));
@@ -189,22 +188,22 @@ phex_nz (ULONGEST l, int sizeof_l)
       {
 	unsigned long high = (unsigned long) (l >> thirty_two);
 
-	str = get_cell ();
+	str = get_print_cell ();
 	if (high == 0)
-	  xsnprintf (str, CELLSIZE, "%lx",
+	  xsnprintf (str, PRINT_CELL_SIZE, "%lx",
 		     (unsigned long) (l & 0xffffffff));
 	else
-	  xsnprintf (str, CELLSIZE, "%lx%08lx", high,
+	  xsnprintf (str, PRINT_CELL_SIZE, "%lx%08lx", high,
 		     (unsigned long) (l & 0xffffffff));
 	break;
       }
     case 4:
-      str = get_cell ();
-      xsnprintf (str, CELLSIZE, "%lx", (unsigned long) l);
+      str = get_print_cell ();
+      xsnprintf (str, PRINT_CELL_SIZE, "%lx", (unsigned long) l);
       break;
     case 2:
-      str = get_cell ();
-      xsnprintf (str, CELLSIZE, "%x", (unsigned short) (l & 0xffff));
+      str = get_print_cell ();
+      xsnprintf (str, PRINT_CELL_SIZE, "%x", (unsigned short) (l & 0xffff));
       break;
     default:
       str = phex_nz (l, sizeof (l));
@@ -219,9 +218,9 @@ phex_nz (ULONGEST l, int sizeof_l)
 char *
 hex_string (LONGEST num)
 {
-  char *result = get_cell ();
+  char *result = get_print_cell ();
 
-  xsnprintf (result, CELLSIZE, "0x%s", phex_nz (num, sizeof (num)));
+  xsnprintf (result, PRINT_CELL_SIZE, "0x%s", phex_nz (num, sizeof (num)));
   return result;
 }
 
@@ -230,14 +229,14 @@ hex_string (LONGEST num)
 char *
 hex_string_custom (LONGEST num, int width)
 {
-  char *result = get_cell ();
-  char *result_end = result + CELLSIZE - 1;
+  char *result = get_print_cell ();
+  char *result_end = result + PRINT_CELL_SIZE - 1;
   const char *hex = phex_nz (num, sizeof (num));
   int hex_len = strlen (hex);
 
   if (hex_len > width)
     width = hex_len;
-  if (width + 2 >= CELLSIZE)
+  if (width + 2 >= PRINT_CELL_SIZE)
     internal_error (__FILE__, __LINE__, _("\
 hex_string_custom: insufficient space to store result"));
 
@@ -294,7 +293,7 @@ int_string (LONGEST val, int radix, int is_signed, int width,
 const char *
 core_addr_to_string (const CORE_ADDR addr)
 {
-  char *str = get_cell ();
+  char *str = get_print_cell ();
 
   strcpy (str, "0x");
   strcat (str, phex (addr, sizeof (addr)));
@@ -306,7 +305,7 @@ core_addr_to_string (const CORE_ADDR addr)
 const char *
 core_addr_to_string_nz (const CORE_ADDR addr)
 {
-  char *str = get_cell ();
+  char *str = get_print_cell ();
 
   strcpy (str, "0x");
   strcat (str, phex_nz (addr, sizeof (addr)));
@@ -318,8 +317,9 @@ core_addr_to_string_nz (const CORE_ADDR addr)
 const char *
 host_address_to_string_1 (const void *addr)
 {
-  char *str = get_cell ();
+  char *str = get_print_cell ();
 
-  xsnprintf (str, CELLSIZE, "0x%s", phex_nz ((uintptr_t) addr, sizeof (addr)));
+  xsnprintf (str, PRINT_CELL_SIZE, "0x%s",
+	     phex_nz ((uintptr_t) addr, sizeof (addr)));
   return str;
 }
diff --git a/gdb/common/print-utils.h b/gdb/common/print-utils.h
index 49bc09a..f045d71 100644
--- a/gdb/common/print-utils.h
+++ b/gdb/common/print-utils.h
@@ -71,4 +71,8 @@ extern const char *host_address_to_string_1 (const void *addr);
 #define host_address_to_string(ADDR) \
   host_address_to_string_1 ((const void *) (ADDR))
 
+extern char *get_print_cell (void);
+
+#define PRINT_CELL_SIZE 50
+
 #endif /* COMMON_CELLS_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index bb68e21..5375047 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2658,6 +2658,12 @@ Make inferior number @var{infno} the current inferior.  The argument
 in the first field of the @samp{info inferiors} display.
 @end table
 
+@vindex $_inferior@r{, convenience variable}
+The debugger convenience variable @samp{$_inferior} contains the
+number of the current inferior.  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.
 
 You can get multiple executables into a debugging session via the
 @code{add-inferior} and @w{@code{clone-inferior}} commands.  On some
@@ -2880,10 +2886,31 @@ further qualifier.
 @c         multithread systems permit starting a program with multiple
 @c         threads ab initio?
 
-@cindex thread number
+@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 ID
+is unique between all threads of an inferior, but not unique between
+threads of different inferiors.
+
+You can refer to a given thread in an inferior using the
+@var{inferior-num}.@var{thread-num} syntax, 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}, 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.
+
+@cindex global thread number
+@cindex global thread identifier (GDB)
+In addition to a @emph{per-inferior} ID, each thread is also assigned
+a unique @emph{global} ID.  Unlike the former, 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
@@ -2899,7 +2926,11 @@ means to print information only about the specified thread or threads.
 
 @enumerate
 @item
-the thread number assigned by @value{GDBN}
+the per-inferior thread number assigned by @value{GDBN}
+
+@item
+the global thread number assigned by @value{GDBN}, if the @samp{-gid}
+option was specified.
 
 @item
 the target system's thread identifier (@var{systag})
@@ -2930,6 +2961,21 @@ For example,
     at threadtest.c:68
 @end smallexample
 
+If you're debugging multiple inferiors, @value{GDBN} displays thread
+IDs using an expanded @var{inferior-num}.@var{thread-num} format.
+
+If you specify the @samp{-gid} option, @value{GDBN} displays a column
+indicating each thread's global thread ID:
+
+@smallexample
+(@value{GDBP}) info threads
+  Id   GId  Target Id             Frame
+  1.1  1    process 35 thread 13  main (argc=1, argv=0x7ffffff8)
+  1.2  3    process 35 thread 23  0x34e5 in sigpause ()
+  1.3  4    process 35 thread 27  0x34e5 in sigpause ()
+* 2.1  2    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:
 
@@ -2944,10 +2990,10 @@ Display info on Solaris user threads.
 @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:
+argument @var{threadno} is the internal per-inferior @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:
 
 @smallexample
 (@value{GDBP}) thread 2
@@ -2962,11 +3008,19 @@ As with the @samp{[New @dots{}]} message, the form of the text after
 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.
+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.  See @xref{Convenience Vars,, Convenience Variables},
+for general information on convenience variables.
+
+@vindex $_gthread@r{, convenience variable}
+The debugger convenience variable @samp{$_gthread} contains the global
+number of the current thread, independent of the thread's containing
+inferior.  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
@@ -2975,9 +3029,10 @@ 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
+shown in the first field of the @samp{info threads} display, with or
+without a inferior qualifier (e.g., @samp{2.1} or @samp{1}); 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 apply a command to all threads in ascending order,
 type @kbd{thread apply all -ascending @var{command}}.
 
@@ -25357,8 +25412,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
@@ -25911,15 +25966,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
@@ -25973,8 +26029,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
@@ -26010,7 +26067,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}"
@@ -26271,7 +26328,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
@@ -26744,7 +26801,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
@@ -26834,7 +26892,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
@@ -27484,10 +27543,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 @emph{global} thread ID.  When
+printing information about all threads, also reports the global ID of
+the current thread.
 
 @subsubheading @value{GDBN} Command
 
@@ -27504,7 +27564,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.
@@ -27570,8 +27630,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.
@@ -27600,8 +27661,9 @@ current-thread-id="1",number-of-threads="3"
  -thread-select @var{threadnum}
 @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 num @var{threadnum} 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.
@@ -27670,7 +27732,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
@@ -28886,7 +28949,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
@@ -29067,8 +29130,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 a0147c1..1e6abbc 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 a2df254..f9fb665 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2995,7 +2995,11 @@ user-specified thread name.
 @end defvar
 
 @defvar InferiorThread.num
-ID of the thread, as assigned by GDB.
+The per-inferior ID of the thread, as assigned by GDB.
+@end defvar
+
+@defvar InferiorThread.global_num
+The global ID of the thread, as assigned by GDB.
 @end defvar
 
 @defvar InferiorThread.ptid
@@ -4638,9 +4642,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 2aacf5b..374c395 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 138d316..c978025 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 eb7f8b8..50dfffc 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -187,7 +187,33 @@ 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 ID's -- one global (GId), and one
+     per-inferior (Id):
+
+      (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 id.  It's exposed to MI,
+     Python/Scheme, visible with "info threads -gid", and is also what
+     the $_thread convenience variable is bound to.  */
+  int global_id;
+
+  /* The GDB thread Id unique in the inferior the thread belongs to.
+     Each inferior has its own number space for this ID.  */
+  int per_inf_id;
+
+  /* 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,27 +379,38 @@ 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 integer thread id (GDB's homegrown global 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 integer thread id (GDB's homegrown id, not the system's).  */
-extern int pid_to_thread_id (ptid_t ptid);
+extern int ptid_to_global_thread_id (ptid_t ptid);
+
+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 thread id (GDB's homegrown global
+   id, not the system's).  */
+extern int valid_global_thread_id (int thread);
+
+/* 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);
 
 /* 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);
+struct thread_info *find_thread_global_id (int num);
 
 /* Finds the first thread of the inferior given by PID.  If PID is -1,
    returns the first thread in the list.  */
@@ -402,6 +439,9 @@ extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
   for (T = thread_list; T; T = T->next) \
     if ((T)->state != THREAD_EXITED)
 
+#define ALL_THREADS(T)				\
+  for (T = thread_list; T; T = T->next) \
+
 /* Traverse all threads, including those that have THREAD_EXITED
    state.  Allows deleting the currently iterated thread.  */
 #define ALL_THREADS_SAFE(T, TMP)	\
@@ -500,7 +540,15 @@ 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 is not NULL, it's a list of GDB ids 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_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 b599e23..2922f48 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 ea689f5..2ed70b5 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_id;
 
   /* 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)
     {
@@ -1316,8 +1316,8 @@ signal_command (char *signum_exp, int from_tty)
 	    {
 	      if (!must_confirm)
 		printf_unfiltered (_("Note:\n"));
-	      printf_unfiltered (_("  Thread %d previously stopped with signal %s, %s.\n"),
-				 tp->num,
+	      printf_unfiltered (_("  Thread %s previously stopped with signal %s, %s.\n"),
+				 print_thread_id (tp),
 				 gdb_signal_to_name (tp->suspend.stop_signal),
 				 gdb_signal_to_string (tp->suspend.stop_signal));
 	      must_confirm = 1;
@@ -1325,10 +1325,10 @@ signal_command (char *signum_exp, int from_tty)
 	}
 
       if (must_confirm
-	  && !query (_("Continuing thread %d (the current thread) with specified signal will\n"
+	  && !query (_("Continuing thread %s (the current thread) with specified signal will\n"
 		       "still deliver the signals noted above to their respective threads.\n"
 		       "Continue anyway? "),
-		     inferior_thread ()->num))
+		     print_thread_id (inferior_thread ())))
 	error (_("Not confirmed."));
     }
 
@@ -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_id;
   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_id);
   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_id);
 
   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_id < lowest->per_inf_id)
 		    lowest = thread;
 		}
 	    }
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 157e236..b632a53 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -531,6 +531,15 @@ inferior_pid_to_str (int pid)
     return _("<null>");
 }
 
+static int
+should_print_inferior (const char *requested_inferiors, struct inferior *inf)
+{
+  if (requested_inferiors != NULL && *requested_inferiors != '\0')
+    return number_is_in_list (requested_inferiors, inf->num);
+  else
+    return 1;
+}
+
 /* Prints the list of inferiors and their details on UIOUT.  This is a
    version of 'info_inferior_command' suitable for use from MI.
 
@@ -548,7 +557,7 @@ print_inferior (struct ui_out *uiout, char *requested_inferiors)
   /* Compute number of inferiors we will print.  */
   for (inf = inferior_list; inf; inf = inf->next)
     {
-      if (!number_is_in_list (requested_inferiors, inf->num))
+      if (!should_print_inferior (requested_inferiors, inf))
 	continue;
 
       ++inf_count;
@@ -572,7 +581,7 @@ print_inferior (struct ui_out *uiout, char *requested_inferiors)
     {
       struct cleanup *chain2;
 
-      if (!number_is_in_list (requested_inferiors, inf->num))
+      if (!should_print_inferior (requested_inferiors, inf))
 	continue;
 
       chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -729,8 +738,8 @@ inferior_command (char *args, int from_tty)
 	  switch_to_thread (tp->ptid);
 	}
 
-      printf_filtered (_("[Switching to thread %d (%s)] "),
-		       pid_to_thread_id (inferior_ptid),
+      printf_filtered (_("[Switching to thread %s (%s)] "),
+		       print_thread_id (inferior_thread ()),
 		       target_pid_to_str (inferior_ptid));
     }
   else
@@ -989,6 +998,26 @@ show_print_inferior_events (struct ui_file *file, int from_tty,
   fprintf_filtered (file, _("Printing of inferior events is %s.\n"), value);
 }
 
+/* Return a new value for the selected inferior's id.  */
+
+static struct value *
+inferior_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
+			void *ignore)
+{
+  struct inferior *inf = current_inferior ();
+
+  return value_from_longest (builtin_type (gdbarch)->builtin_int, inf->num);
+}
+
+/* Implementation of `$_inferior' variable.  */
+
+static const struct internalvar_funcs inferior_funcs =
+{
+  inferior_id_make_value,
+  NULL,
+  NULL
+};
+
 
 
 void
@@ -1052,4 +1081,5 @@ Show printing of inferior events (e.g., inferior start and exit)."), NULL,
          show_print_inferior_events,
          &setprintlist, &showprintlist);
 
+  create_internalvar_type_lazy ("_inferior", &inferior_funcs, NULL);
 }
diff --git a/gdb/inferior.h b/gdb/inferior.h
index d3cf615..7e13910 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -304,6 +304,8 @@ struct inferior
   /* True if the PID was actually faked by GDB.  */
   int fake_pid_p;
 
+  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 f9bb411..a45b87f 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_id;
 	  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_id;
   inferior_thread ()->control.exception_resume_breakpoint = bp;
 }
 
@@ -7905,7 +7905,7 @@ print_signal_received_reason (struct ui_out *uiout, enum gdb_signal siggnal)
       ui_out_text (uiout, "\n[");
       ui_out_field_string (uiout, "thread-name",
 			   target_pid_to_str (t->ptid));
-      ui_out_field_fmt (uiout, "thread-id", "] #%d", t->num);
+      ui_out_field_fmt (uiout, "thread-id", "] #%d", t->global_id);
       ui_out_text (uiout, " stopped");
     }
   else
diff --git a/gdb/mi/mi-cmd-var.c b/gdb/mi/mi-cmd-var.c
index 643fd7a..d532797 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 b349df2..4eb16fa 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_id, 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_id, 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_id);
       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_id);
 	  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_id);
 
   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_id);
     }
 
   if (!running_result_record_printed && mi_proceeded)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 2b25a9c..89aaf98 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_id != 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_id);
 	      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 9c0b0e4..82cbc27 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 45a7d87..e91c5d2 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 e5db354..381235f 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,19 @@ 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_id);
+}
+
+/* Getter for InferiorThread.global_num.  */
+
+static PyObject *
+thpy_get_global_num (PyObject *self, void *closure)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  return PyLong_FromLong (thread_obj->thread->global_id);
 }
 
 /* Getter for InferiorThread.ptid  -> (pid, lwp, tid).
@@ -140,6 +154,18 @@ thpy_get_ptid (PyObject *self, void *closure)
   return gdbpy_create_ptid_object (thread_obj->thread->ptid);
 }
 
+/* Getter for InferiorThread.inferior -> Inferior.  */
+
+static PyObject *
+thpy_get_inferior (PyObject *self, void *ignore)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  return inferior_to_inferior_object (thread_obj->thread->inf);
+}
+
 /* Implementation of InferiorThread.switch ().
    Makes this the GDB selected thread.  */
 
@@ -282,9 +308,14 @@ 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 ID of the thread, as assigned by GDB.", NULL },
+  { "global_num", thpy_get_global_num, NULL,
+    "Global ID 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,
+    "The Inferior object this thread belongs to.", NULL },
 
   { NULL }
 };
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 661ce53..b3a7d6a 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_id))
       {
 	btrace_enable (tp, &record_btrace_conf);
 
@@ -438,8 +438,8 @@ record_btrace_info (struct target_ops *self)
     }
 
   printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) "
-		       "for thread %d (%s).\n"), insns, calls, gaps,
-		     tp->num, target_pid_to_str (tp->ptid));
+		       "for thread %s (%s).\n"), insns, calls, gaps,
+		     print_thread_id (tp), target_pid_to_str (tp->ptid));
 
   if (btrace_is_replaying (tp))
     printf_unfiltered (_("Replay in progress.  At instruction %u.\n"),
@@ -1864,7 +1864,7 @@ record_btrace_resume_thread (struct thread_info *tp,
 {
   struct btrace_thread_info *btinfo;
 
-  DEBUG ("resuming thread %d (%s): %x (%s)", tp->num,
+  DEBUG ("resuming thread %s (%s): %x (%s)", print_thread_id (tp),
 	 target_pid_to_str (tp->ptid), flag, btrace_thread_flag_to_str (flag));
 
   btinfo = &tp->btrace;
@@ -2131,7 +2131,8 @@ record_btrace_cancel_resume (struct thread_info *tp)
   if (flags == 0)
     return;
 
-  DEBUG ("cancel resume thread %d (%s): %x (%s)", tp->num,
+  DEBUG ("cancel resume thread %s (%s): %x (%s)",
+	 print_thread_id (tp),
 	 target_pid_to_str (tp->ptid), flags,
 	 btrace_thread_flag_to_str (flags));
 
@@ -2354,7 +2355,7 @@ record_btrace_step_thread (struct thread_info *tp)
   flags = btinfo->flags & (BTHR_MOVE | BTHR_STOP);
   btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
 
-  DEBUG ("stepping thread %d (%s): %x (%s)", tp->num,
+  DEBUG ("stepping thread %s (%s): %x (%s)", print_thread_id (tp),
 	 target_pid_to_str (tp->ptid), flags,
 	 btrace_thread_flag_to_str (flags));
 
@@ -2563,7 +2564,8 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid,
   /* We moved the replay position but did not update registers.  */
   registers_changed_ptid (eventing->ptid);
 
-  DEBUG ("wait ended by thread %d (%s): %s", eventing->num,
+  DEBUG ("wait ended by thread %s (%s): %s",
+	 print_thread_id (eventing),
 	 target_pid_to_str (eventing->ptid),
 	 target_waitstatus_to_string (status));
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 52c5df8..1bf58ef 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3880,7 +3880,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_id < lowest_stopped->per_inf_id)
 	lowest_stopped = thread;
 
       if (non_stop)
diff --git a/gdb/target.c b/gdb/target.c
index e5e8172..ef5f3cd 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 f879bc8..82ce744 100644
--- a/gdb/testsuite/gdb.base/break.exp
+++ b/gdb/testsuite/gdb.base/break.exp
@@ -590,7 +590,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.*" \
+    "Bad thread spec.*" \
     "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/default.exp b/gdb/testsuite/gdb.base/default.exp
index 4395c98..ff5c062 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -587,6 +587,8 @@ set show_conv_list \
 	{$_sdata = void} \
 	{$_siginfo = void} \
 	{$_thread = 0} \
+	{$_gthread = 0} \
+	{$_inferior = 1} \
 	{$_exception = <error: No frame selected>} \
 	{$_probe_argc = <error: No frame selected>} \
 	{$_probe_arg0 = <error: No frame selected>} \
diff --git a/gdb/testsuite/gdb.base/hbreak2.exp b/gdb/testsuite/gdb.base/hbreak2.exp
index 0815a96..1e29e00 100644
--- a/gdb/testsuite/gdb.base/hbreak2.exp
+++ b/gdb/testsuite/gdb.base/hbreak2.exp
@@ -347,7 +347,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.*" \
+    "Bad thread spec.*" \
     "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 c363be4..0cac4ec 100644
--- a/gdb/testsuite/gdb.base/sepdebug.exp
+++ b/gdb/testsuite/gdb.base/sepdebug.exp
@@ -389,7 +389,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.*" \
+    "Bad thread spec.*" \
     "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 6d767c2..5d1af33 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" "Bad thread spec.*" "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 fcb745f..942b646 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" "Bad thread spec '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" "Bad thread spec 'thread'"
+gdb_test "break thread task" "Bad thread spec 'task'"
+gdb_test "break thread if" "Bad thread spec '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/base.exp b/gdb/testsuite/gdb.multi/base.exp
index 0c74d98..4cabef0 100644
--- a/gdb/testsuite/gdb.multi/base.exp
+++ b/gdb/testsuite/gdb.multi/base.exp
@@ -44,12 +44,16 @@ if { [build_executable ${testfile}.exp ${exec3} "${srcfile3}" {debug}] == -1 } {
 
 clean_restart ${exec1}
 
+gdb_test {print $_inferior} " = 1"
+
 # Add an empty inferior, switch to it, and load a main executable into
 # it.
 gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2"
 gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
 gdb_test "file ${binfile2}" ".*" "load ${exec2} file in inferior 2"
 
+gdb_test {print $_inferior} " = 2" "print \$_inferior after switching"
+
 # Add a new inferior and load a main executable into it in one
 # command.
 gdb_test "add-inferior -exec ${binfile3}" \
diff --git a/gdb/testsuite/gdb.multi/info-threads.exp b/gdb/testsuite/gdb.multi/info-threads.exp
index c00b8d7..b949e17 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/per-inferior-tids.c b/gdb/testsuite/gdb.multi/per-inferior-tids.c
new file mode 100644
index 0000000..9e77031
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/per-inferior-tids.c
@@ -0,0 +1,40 @@
+/* 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>
+
+void *
+thread_function (void *arg)
+{
+  while (1)
+    sleep (1);
+}
+
+int
+main (void)
+{
+  pthread_t child_thread;
+  int i;
+
+  alarm (300);
+
+  pthread_create (&child_thread, NULL, thread_function, NULL);
+  pthread_join (child_thread, NULL);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.multi/per-inferior-tids.exp b/gdb/testsuite/gdb.multi/per-inferior-tids.exp
new file mode 100644
index 0000000..e892758
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/per-inferior-tids.exp
@@ -0,0 +1,153 @@
+# 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/>.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {pthreads debug}] } {
+    return -1
+}
+
+clean_restart ${testfile}
+
+if { ![runto_main] } then {
+    return -1
+}
+
+# "info threads" while there's only inferior 1 should show
+# single-number thread IDs.
+with_test_prefix "single inferior" {
+    gdb_test "info threads" " 1 .* main .* at .*$srcfile:.*"
+
+    gdb_test "thread" "Current thread is 1 .*"
+}
+
+# "info threads" while there are multiple inferior should show
+# expanded 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.
+    gdb_test "info threads" " 1\.1 .* main .* at .*$srcfile:.*"
+
+    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.
+    gdb_test "info threads" \
+	[multi_line \
+	     "  1\.1 .* main .* at .*$srcfile:.*" \
+	     "\\* 2\.1 .* main .* at .*$srcfile:.*"] \
+	"info threads show inferior numbers"
+
+    gdb_test "thread" "Current thread is 2\.1 .*" \
+	"switch to thread using extended thread ID"
+
+    gdb_breakpoint "thread_function"
+
+    gdb_continue_to_breakpoint "once"
+    gdb_test "inferior 1" "Switching to inferior 1 .*"
+    gdb_continue_to_breakpoint "twice"
+
+    gdb_test "info threads" \
+	[multi_line \
+	     "  1\.1 .*" \
+	     "\\* 1\.2 .* thread_function .* at .*$srcfile:.*" \
+	     "  2\.1 .*" \
+	     "  2\.2 .* thread_function .* at .*$srcfile:.*"] \
+	"info threads again"
+
+    # Same, but show the global ID.
+    gdb_test "info threads -gid" \
+	[multi_line \
+	     "  1\.1 +1 +.*" \
+	     "\\* 1\.2 +4 +.* thread_function .* at .*$srcfile:.*" \
+	     "  2\.1 +2 +.*" \
+	     "  2\.2 +3 +.* thread_function .* at .*$srcfile:.*"]
+
+    # Confirm the convenience variables show the expected numbers.
+    gdb_test "p \$_thread == 2" " = 1"
+    gdb_test "p \$_gthread == 4" " = 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\\."
+
+    # If Python is configured, check that InferiorThread.global_num
+    # returns the expected number.
+    if { ![skip_python_tests] } {
+	gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
+	gdb_test "python print ('result = %s' % t0.num)" " = 2" "test InferiorThread.num"
+	gdb_test "python print ('result = %s' % t0.global_num)" " = 4" "test InferiorThread.global_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.
+    gdb_test "info threads" \
+	[multi_line \
+	     "\\* 1 .*" \
+	     "  2 .* thread_function .* at .*$srcfile:.*"]
+
+    gdb_test "thread" "Current thread is 1 .*"
+}
+
+# Add another inferior and remove inferior 1.  Since even though
+# there's a single inferior, it's number is not 1, GDB should show
+# extended thread IDs that include the inferior number.
+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.
+    gdb_test "info threads" \
+	[multi_line \
+	     "\\* 1\.1 .*" \
+	     "  1\.2 .* thread_function .* at .*$srcfile:.*"] \
+	"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 show the inferior number.
+    gdb_test "info threads" " 3\.1 .* main .* at .*$srcfile:.*" \
+	"info threads with single inferior"
+
+    gdb_test "thread" "Current thread is 3\.1 .*" "thread again"
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.exp b/gdb/testsuite/gdb.python/py-infthread.exp
index 182254b..c28903f 100644
--- a/gdb/testsuite/gdb.python/py-infthread.exp
+++ b/gdb/testsuite/gdb.python/py-infthread.exp
@@ -41,9 +41,13 @@ if ![runto_main] then {
 
 gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
 gdb_test "python print (t0)" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>" "verify InferiorThread object"
-gdb_test "python print ('result = %s' % t0.num)" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print ('result = %s' % t0.num)" " = 1" "test InferiorThread.num"
+gdb_test "python print ('result = %s' % t0.global_num)" " = 1" "test InferiorThread.global_num"
 gdb_test "python print ('result = %s' % str (t0.ptid))" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
 
+gdb_py_test_silent_cmd "python i0 = t0.inferior" "test InferiorThread.inferior" 1
+gdb_test "python print ('result = %s' % i0.num)" " = 1" "test Inferior.num"
+
 gdb_py_test_silent_cmd "python name = gdb.selected_thread().name" \
     "get supplied name of current thread" 1
 gdb_py_test_silent_cmd "python gdb.selected_thread().name = 'hibob'" \
diff --git a/gdb/testsuite/gdb.threads/thread-specific.exp b/gdb/testsuite/gdb.threads/thread-specific.exp
index d975eed..bba0833 100644
--- a/gdb/testsuite/gdb.threads/thread-specific.exp
+++ b/gdb/testsuite/gdb.threads/thread-specific.exp
@@ -66,7 +66,10 @@ clean_restart ${binfile}
 gdb_test_no_output "set print sevenbit-strings"
 gdb_test_no_output "set width 0"
 
+# As this test only runs a single inferior, $_thread and $_gthread
+# should match throughout.
 gdb_test {print $_thread} ".* = 0" "thread var when not running"
+gdb_test {print $_gthread} ".* = 0" "gthread var when not running"
 
 runto_main
 
@@ -82,6 +85,7 @@ if {[llength $threads] == 0} {
 }
 
 gdb_test {print $_thread} ".* = [lindex $threads 0]" "thread var in main"
+gdb_test {print $_gthread} ".* = [lindex $threads 0]" "gthread var in main"
 
 gdb_test_multiple "break $line thread [lindex $threads 0]" \
   "breakpoint $line main thread" {
@@ -120,6 +124,7 @@ if { $this_breakpoint != -1 } {
 
 if { $this_thread != -1 } {
     gdb_test {print $_thread} ".* = $this_thread" "thread var at break"
+    gdb_test {print $_gthread} ".* = $this_thread" "gthread var at break"
 } else {
     untested "thread var at break"
 }
diff --git a/gdb/thread.c b/gdb/thread.c
index 7d2232f..919e867 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -182,7 +182,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_id);
 
   bpstat_clear (&tp->control.stop_bpstat);
 
@@ -230,12 +230,18 @@ init_thread_list (void)
    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_id = ++highest_thread_num;
+  tp->inf = inf;
+  tp->per_inf_id = ++inf->highest_thread_num;
 
   if (thread_list == NULL)
     thread_list = tp;
@@ -260,6 +266,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 +285,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 +309,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 +489,24 @@ delete_thread_silent (ptid_t ptid)
 }
 
 struct thread_info *
-find_thread_id (int num)
+find_thread_global_id (int id)
 {
   struct thread_info *tp;
 
   for (tp = thread_list; tp; tp = tp->next)
-    if (tp->num == num)
+    if (tp->global_id == id)
+      return tp;
+
+  return NULL;
+}
+
+static struct thread_info *
+find_thread_id (struct inferior *inf, int id)
+{
+  struct thread_info *tp;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (tp->inf == inf && tp->per_inf_id == id)
       return tp;
 
   return NULL;
@@ -548,38 +568,38 @@ thread_count (void)
 }
 
 int
-valid_thread_id (int num)
+valid_global_thread_id (int id)
 {
   struct thread_info *tp;
 
   for (tp = thread_list; tp; tp = tp->next)
-    if (tp->num == num)
+    if (tp->global_id == 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_id;
 
   return 0;
 }
 
 ptid_t
-thread_id_to_pid (int num)
+global_thread_id_to_ptid (int num)
 {
-  struct thread_info *thread = find_thread_id (num);
+  struct thread_info *thread = find_thread_global_id (num);
 
   if (thread)
     return thread->ptid;
   else
-    return pid_to_ptid (-1);
+    return minus_one_ptid;
 }
 
 int
@@ -604,7 +624,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_id < ret->global_id)
 	ret = tp;
 
   return ret;
@@ -688,10 +708,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_id;
 
       num++;
-      ui_out_field_int (uiout, "thread-id", tp->num);
+      ui_out_field_int (uiout, "thread-id", tp->global_id);
     }
 
   do_cleanups (cleanup_chain);
@@ -1105,25 +1125,51 @@ 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)
+static int
+should_print_thread (const char *requested_threads, int global_ids,
+		     int pid, struct thread_info *thr)
+{
+  if (pid != -1 && ptid_get_pid (thr->ptid) != pid
+      && requested_threads != NULL && *requested_threads != '\0')
+    error (_("Requested thread not found in requested process"));
+
+  if (requested_threads != NULL && *requested_threads != '\0')
+    {
+      int id = global_ids ? thr->global_id : thr->per_inf_id;
+
+      if (number_is_in_list (requested_threads, id))
+	{
+	  if (pid != -1 && ptid_get_pid (thr->ptid) != pid)
+	    error (_("Requested thread not found in requested process"));
+
+	  return 1;
+	}
+
+      return 0;
+    }
+  else if (pid != -1)
+    return ptid_get_pid (thr->ptid) == pid;
+  else if (thr->state == THREAD_EXITED)
+    return 0;
+  else
+    return 1;
+}
+
+/* Like print_thread_info, but in addition, GLOBAL_IDS specified
+   whether REQUESTED_THREADS indicated 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;
 
   update_thread_list ();
   current_ptid = inferior_ptid;
@@ -1142,13 +1188,7 @@ 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, global_ids, pid, tp))
 	    continue;
 
 	  ++n_threads;
@@ -1165,34 +1205,37 @@ 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);
     }
 
+  ALL_INFERIORS (inf)
+    {
+
   for (tp = thread_list; tp; tp = tp->next)
     {
       struct cleanup *chain2;
       int core;
 
-      if (!number_is_in_list (requested_threads, tp->num))
+      if (inf->pid != ptid_get_pid (tp->ptid))
 	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_id;
 
-      if (tp->state == THREAD_EXITED)
+      if (!should_print_thread (requested_threads, global_ids, pid, tp))
 	continue;
 
       chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -1213,7 +1256,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_id);
 
       /* For the CLI, we stuff everything into the target-id field.
 	 This is a gross hack to make the output come out looking
@@ -1282,6 +1329,7 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 
       do_cleanups (chain2);
     }
+    }
 
   /* Restores the current thread and the frame selected before
      the "info threads" command.  */
@@ -1289,27 +1337,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
+The current thread <Thread ID %s> has terminated.  See `help thread'.\n",
+			print_thread_id (inferior_thread ()));
+      else if (thread_list != NULL
 	       && ptid_equal (current_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.  */
+
+void
+print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
+{
+  print_thread_info_1 (uiout, requested_threads, 1, pid, 0);
+}
 
-   Optional ARG is a thread id, or list of thread ids.
+/* 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 +1374,16 @@ No selected thread.  See `help thread'.\n");
 static void
 info_threads_command (char *arg, int from_tty)
 {
-  print_thread_info (current_uiout, arg, -1);
+  int show_global_ids = 0;
+
+  if (arg != NULL
+      && check_for_argument (&arg, "-gid", sizeof ("-gid") - 1))
+    {
+      arg = skip_spaces (arg);
+      show_global_ids = 1;
+    }
+
+  print_thread_info_1 (current_uiout, arg, 0, -1, show_global_ids);
 }
 
 /* See gdbthread.h.  */
@@ -1593,18 +1658,24 @@ make_cleanup_restore_current_thread (void)
 
 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, and then by per-inferior ID).  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;
 
-  return ((((*ap)->num > (*bp)->num) - ((*ap)->num < (*bp)->num))
+  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 (((a->per_inf_id > b->per_inf_id)
+	   - (a->per_inf_id < b->per_inf_id))
 	  * (tp_array_compar_ascending ? +1 : -1));
 }
 
@@ -1678,8 +1749,8 @@ thread_apply_all_command (char *cmd, int from_tty)
         if (thread_alive (tp_array[k]))
           {
             switch_to_thread (tp_array[k]->ptid);
-            printf_filtered (_("\nThread %d (%s):\n"), 
-			     tp_array[k]->num,
+            printf_filtered (_("\nThread %s (%s):\n"),
+			     print_thread_id (tp_array[k]),
 			     target_pid_to_str (inferior_ptid));
             execute_command (cmd, from_tty);
 
@@ -1691,6 +1762,18 @@ thread_apply_all_command (char *cmd, int from_tty)
   do_cleanups (old_chain);
 }
 
+const char *
+print_thread_id (struct thread_info *thr)
+{
+  char *s = get_print_cell ();
+
+  if (inferior_list->next != NULL || inferior_list->num != 1)
+    xsnprintf (s, PRINT_CELL_SIZE, "%d.%d", thr->inf->num, thr->per_inf_id);
+  else
+    xsnprintf (s, PRINT_CELL_SIZE, "%d", thr->per_inf_id);
+  return s;
+}
+
 static void
 thread_apply_command (char *tidlist, int from_tty)
 {
@@ -1722,7 +1805,7 @@ thread_apply_command (char *tidlist, int from_tty)
 
       make_cleanup_restore_current_thread ();
 
-      tp = find_thread_id (start);
+      tp = find_thread_id (current_inferior (), start);
 
       if (!tp)
 	warning (_("Unknown thread %d."), start);
@@ -1732,7 +1815,7 @@ thread_apply_command (char *tidlist, int from_tty)
 	{
 	  switch_to_thread (tp->ptid);
 
-	  printf_filtered (_("\nThread %d (%s):\n"), tp->num,
+	  printf_filtered (_("\nThread %s (%s):\n"), print_thread_id (tp),
 			   target_pid_to_str (inferior_ptid));
 	  execute_command (cmd, from_tty);
 
@@ -1757,13 +1840,15 @@ thread_command (char *tidstr, int from_tty)
 
       if (target_has_stack)
 	{
+	  struct thread_info *tp = inferior_thread ();
+
 	  if (is_exited (inferior_ptid))
-	    printf_filtered (_("[Current thread is %d (%s) (exited)]\n"),
-			     pid_to_thread_id (inferior_ptid),
+	    printf_filtered (_("[Current thread is %s (%s) (exited)]\n"),
+			     print_thread_id (tp),
 			     target_pid_to_str (inferior_ptid));
 	  else
-	    printf_filtered (_("[Current thread is %d (%s)]\n"),
-			     pid_to_thread_id (inferior_ptid),
+	    printf_filtered (_("[Current thread is %s (%s)]\n"),
+			     print_thread_id (tp),
 			     target_pid_to_str (inferior_ptid));
 	}
       else
@@ -1812,32 +1897,32 @@ thread_find_command (char *arg, int from_tty)
     {
       if (tp->name != NULL && re_exec (tp->name))
 	{
-	  printf_filtered (_("Thread %d has name '%s'\n"),
-			   tp->num, tp->name);
+	  printf_filtered (_("Thread %s has name '%s'\n"),
+			   print_thread_id (tp), tp->name);
 	  match++;
 	}
 
       tmp = target_thread_name (tp);
       if (tmp != NULL && re_exec (tmp))
 	{
-	  printf_filtered (_("Thread %d has target name '%s'\n"),
-			   tp->num, tmp);
+	  printf_filtered (_("Thread %s has target name '%s'\n"),
+			   print_thread_id (tp), tmp);
 	  match++;
 	}
 
       tmp = target_pid_to_str (tp->ptid);
       if (tmp != NULL && re_exec (tmp))
 	{
-	  printf_filtered (_("Thread %d has target id '%s'\n"),
-			   tp->num, tmp);
+	  printf_filtered (_("Thread %s has target id '%s'\n"),
+			   print_thread_id (tp), tmp);
 	  match++;
 	}
 
       tmp = target_extra_thread_info (tp);
       if (tmp != NULL && re_exec (tmp))
 	{
-	  printf_filtered (_("Thread %d has extra info '%s'\n"),
-			   tp->num, tmp);
+	  printf_filtered (_("Thread %s has extra info '%s'\n"),
+			   print_thread_id (tp), tmp);
 	  match++;
 	}
     }
@@ -1856,31 +1941,105 @@ show_print_thread_events (struct ui_file *file, int from_tty,
                     value);
 }
 
-static int
-do_captured_thread_select (struct ui_out *uiout, void *tidstr)
+struct thread_info *
+parse_thread_id (const char *tidstr, const char **end)
 {
-  int num;
+  const char *number = tidstr;
+  const char *dot, *p1;
   struct thread_info *tp;
+  struct inferior *inf;
+  int thr_num;
+  int explicit_inf_id = 0;
 
-  num = value_as_long (parse_and_eval ((const char *) tidstr));
+  dot = strchr (number, '.');
 
-  tp = find_thread_id (num);
+  if (dot != NULL)
+    {
+      /* Parse number to the left of the dot.  */
+      int inf_num;
 
-  if (!tp)
-    error (_("Thread ID %d not known."), num);
+      p1 = number;
+      inf_num = get_number_trailer (&p1, '.');
+      if (inf_num == 0)
+	error (_("Bad thread spec '%s'"), 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)
+    error (_("Bad thread spec '%s'"), number);
+
+  ALL_THREADS (tp)
+    {
+      if (ptid_get_pid (tp->ptid) == inf->pid
+	  && tp->per_inf_id == thr_num)
+	break;
+    }
+
+  if (tp == NULL)
+    {
+      if (inferior_list->next != NULL || inferior_list->num != 1
+	  || 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;
+}
+
+static int
+do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
+{
+  const char *tidstr = tidstr_v;
+  struct thread_info *tp;
+
+  if (ui_out_is_mi_like_p (uiout))
+    {
+      int num = value_as_long (parse_and_eval (tidstr));
+
+      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_int (uiout, "new-thread-id", pid_to_thread_id (inferior_ptid));
-  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_id);
+  else
+    {
+      ui_out_text (uiout, "[Switching to thread ");
+      ui_out_text (uiout, print_thread_id (inferior_thread ()));
+      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.  */
@@ -1934,17 +2093,44 @@ 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 id.  Return a value of
+   0 if no thread is selected.  If GLOBAL is true, return the thread's
+   global ID.  Otherwise return the per-inferior ID.  */
 
 static struct value *
-thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
-		      void *ignore)
+thread_id_make_value_helper (struct gdbarch *gdbarch, int global)
 {
   struct thread_info *tp = find_thread_ptid (inferior_ptid);
+  int int_val;
+
+  if (tp == NULL)
+    int_val = 0;
+  else if (global)
+    int_val = tp->global_id;
+  else
+    int_val = tp->per_inf_id;
 
-  return value_from_longest (builtin_type (gdbarch)->builtin_int,
-			     (tp ? tp->num : 0));
+  return value_from_longest (builtin_type (gdbarch)->builtin_int, int_val);
+}
+
+/* Return a new value for the selected thread's id.  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,
+                      void *ignore)
+{
+  return thread_id_make_value_helper (gdbarch, 0);
+}
+
+/* Return a new value for the selected thread's global id.  Return a
+   value of 0 if no thread is selected, or no threads exist.  */
+
+static struct value *
+global_thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
+                             void *ignore)
+{
+  return thread_id_make_value_helper (gdbarch, 1);
 }
 
 /* Commands with a prefix of `thread'.  */
@@ -1959,6 +2145,15 @@ static const struct internalvar_funcs thread_funcs =
   NULL
 };
 
+/* Implementation of `gthread' variable.  */
+
+static const struct internalvar_funcs gthread_funcs =
+{
+  global_thread_id_make_value,
+  NULL,
+  NULL
+};
+
 void
 _initialize_thread (void)
 {
@@ -1966,9 +2161,10 @@ _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."));
+Usage: info threads [-gid] [ID]...\n\
+-gid: Show global thread IDs.\n\
+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\
@@ -2011,6 +2207,7 @@ Show printing of thread events (such as thread start and exit)."), NULL,
          &setprintlist, &showprintlist);
 
   create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
+  create_internalvar_type_lazy ("_gthread", &gthread_funcs, NULL);
 
   observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
 }
diff --git a/gdb/varobj.c b/gdb/varobj.c
index 0b19d84..8118cea 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 belong 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);
