emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] trunk r113123: Use C99-style flexible array members if ava


From: Paul Eggert
Subject: [Emacs-diffs] trunk r113123: Use C99-style flexible array members if available.
Date: Fri, 21 Jun 2013 20:11:50 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 113123
revision-id: address@hidden
parent: address@hidden
committer: Paul Eggert <address@hidden>
branch nick: trunk
timestamp: Fri 2013-06-21 13:11:44 -0700
message:
  Use C99-style flexible array members if available.
  
  This avoids some subtle aliasing issues, which typically
  aren't a problem with GCC but may be a problem elsewhere.
  * lib-src/ebrowse.c (struct member, struct alias, struct sym):
  Use FLEXIBLE_ARRAY_MEMBER.
  (add_sym, add_member, make_namespace, register_namespace_alias):
  Use offsetof (struct, flex_array_member), not sizeof (struct), as
  that ports better to pre-C99 non-GCC.
  * src/alloc.c (sdata): New typedef, replacing the old struct sdata.
  It is a struct if GC_CHECK_STRING_BYTES, a union otherwise.
  In either case, it uses a flexible array member rather than
  the old struct hack.  All uses changed.
  (SDATA_NBYTES, sweep_strings) [!GC_CHECK_STRING_BYTES]:
  Adjust to sdata reorganization.
  * src/alloc.c (VBLOCK_BYTES_MIN, allocate_vectorlike, Fgarbage_collect):
  Use offsetof (struct, flex_array_member), not sizeof (struct), as
  that ports better to pre-C99 non-GCC.
  * src/chartab.c (Fmake_char_table, make_sub_char_table, copy_char_table):
  Use CHAR_TABLE_STANDARD_SLOTS rather than its definition,
  as the latter has changed.
  * src/conf_post.h (FLEXIBLE_ARRAY_MEMBER): Move here from w32.c,
  and port better to pre-C99 GCC.
  * src/image.c (struct xpm_cached_color):
  * src/lisp.h (struct Lisp_Vector, struct Lisp_Bool_Vector)
  (struct Lisp_Char_Table, struct Lisp_Sub_Char_Table):
  Use FLEXIBLE_ARRAY_MEMBER.
  * src/lisp.h (string_bytes) [GC_CHECK_STRING_BYTES]:
  Move decl to top level so it gets checked against implementation.
  (CHAR_TABLE_STANDARD_SLOTS): Adjust to struct Lisp_Char_Table change.
  * src/w32.c (FLEXIBLE_ARRAY_MEMBER): Move to conf_post.h.
modified:
  lib-src/ChangeLog              changelog-20091113204419-o5vbwnq5f7feedwu-1608
  lib-src/ebrowse.c              ebrowse.c-20091113204419-o5vbwnq5f7feedwu-1798
  src/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1438
  src/alloc.c                    alloc.c-20091113204419-o5vbwnq5f7feedwu-252
  src/chartab.c                  chartab.c-20091113204419-o5vbwnq5f7feedwu-8539
  src/conf_post.h                conf_post.h-20120730211826-q0qbxxwh2emw52hd-1
  src/image.c                    image.c-20091113204419-o5vbwnq5f7feedwu-2969
  src/lisp.h                     lisp.h-20091113204419-o5vbwnq5f7feedwu-253
  src/w32.c                      w32.c-20091113204419-o5vbwnq5f7feedwu-808
=== modified file 'lib-src/ChangeLog'
--- a/lib-src/ChangeLog 2013-06-19 20:10:57 +0000
+++ b/lib-src/ChangeLog 2013-06-21 20:11:44 +0000
@@ -1,3 +1,12 @@
+2013-06-21  Paul Eggert  <address@hidden>
+
+       Use C99-style flexible array members if available.
+       * ebrowse.c (struct member, struct alias, struct sym):
+       Use FLEXIBLE_ARRAY_MEMBER.
+       (add_sym, add_member, make_namespace, register_namespace_alias):
+       Use offsetof (struct, flex_array_member), not sizeof (struct), as
+       that ports better to pre-C99 non-GCC.
+
 2013-05-29  Eli Zaretskii  <address@hidden>
 
        * Makefile.in (mostlyclean): Remove *.res files.

=== modified file 'lib-src/ebrowse.c'
--- a/lib-src/ebrowse.c 2013-01-01 09:11:05 +0000
+++ b/lib-src/ebrowse.c 2013-06-21 20:11:44 +0000
@@ -237,7 +237,7 @@
   char *def_regexp;            /* Regular expression matching definition.  */
   const char *def_filename;    /* File name of definition.  */
   int def_pos;                 /* Buffer position of definition.  */
-  char name[1];                        /* Member name.  */
+  char name[FLEXIBLE_ARRAY_MEMBER]; /* Member name.  */
 };
 
 /* Structures of this type are used to connect class structures with
@@ -256,7 +256,7 @@
   struct alias *next;          /* Next in list.  */
   struct sym *namesp;          /* Namespace in which defined.  */
   struct link *aliasee;                /* List of aliased namespaces 
(A::B::C...).  */
-  char name[1];                        /* Alias name.  */
+  char name[FLEXIBLE_ARRAY_MEMBER]; /* Alias name.  */
 };
 
 /* The structure used to describe a class in the symbol table,
@@ -280,7 +280,7 @@
   const char *filename;                /* File in which it can be found.  */
   const char *sfilename;       /* File in which members can be found.  */
   struct sym *namesp;          /* Namespace in which defined. .  */
-  char name[1];                 /* Name of the class.  */
+  char name[FLEXIBLE_ARRAY_MEMBER]; /* Name of the class.  */
 };
 
 /* Experimental: Print info for `--position-info'.  We print
@@ -567,8 +567,8 @@
          puts (name);
        }
 
-      sym = (struct sym *) xmalloc (sizeof *sym + strlen (name));
-      memset (sym, 0, sizeof *sym);
+      sym = xmalloc (offsetof (struct sym, name) + strlen (name) + 1);
+      memset (sym, 0, offsetof (struct sym, name));
       strcpy (sym->name, name);
       sym->namesp = scope;
       sym->next = class_table[h];
@@ -852,7 +852,8 @@
 static struct member *
 add_member (struct sym *cls, char *name, int var, int sc, unsigned int hash)
 {
-  struct member *m = (struct member *) xmalloc (sizeof *m + strlen (name));
+  struct member *m = xmalloc (offsetof (struct member, name)
+                             + strlen (name) + 1);
   struct member **list;
   struct member *p;
   struct member *prev;
@@ -962,8 +963,8 @@
 static struct sym *
 make_namespace (char *name, struct sym *context)
 {
-  struct sym *s = (struct sym *) xmalloc (sizeof *s + strlen (name));
-  memset (s, 0, sizeof *s);
+  struct sym *s = xmalloc (offsetof (struct sym, name) + strlen (name) + 1);
+  memset (s, 0, offsetof (struct sym, name));
   strcpy (s->name, name);
   s->next = all_namespaces;
   s->namesp = context;
@@ -1046,7 +1047,7 @@
     if (streq (new_name, al->name) && (al->namesp == current_namespace))
       return;
 
-  al = (struct alias *) xmalloc (sizeof *al + strlen (new_name));
+  al = xmalloc (offsetof (struct alias, name) + strlen (new_name) + 1);
   strcpy (al->name, new_name);
   al->next = namespace_alias_table[h];
   al->namesp = current_namespace;

=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2013-06-20 18:59:08 +0000
+++ b/src/ChangeLog     2013-06-21 20:11:44 +0000
@@ -1,3 +1,31 @@
+2013-06-21  Paul Eggert  <address@hidden>
+
+       Use C99-style flexible array members if available.
+       This avoids some subtle aliasing issues, which typically
+       aren't a problem with GCC but may be a problem elsewhere.
+       * alloc.c (sdata): New typedef, replacing the old struct sdata.
+       It is a struct if GC_CHECK_STRING_BYTES, a union otherwise.
+       In either case, it uses a flexible array member rather than
+       the old struct hack.  All uses changed.
+       (SDATA_NBYTES, sweep_strings) [!GC_CHECK_STRING_BYTES]:
+       Adjust to sdata reorganization.
+       * alloc.c (VBLOCK_BYTES_MIN, allocate_vectorlike, Fgarbage_collect):
+       Use offsetof (struct, flex_array_member), not sizeof (struct), as
+       that ports better to pre-C99 non-GCC.
+       * chartab.c (Fmake_char_table, make_sub_char_table, copy_char_table):
+       Use CHAR_TABLE_STANDARD_SLOTS rather than its definition,
+       as the latter has changed.
+       * conf_post.h (FLEXIBLE_ARRAY_MEMBER): Move here from w32.c,
+       and port better to pre-C99 GCC.
+       * image.c (struct xpm_cached_color):
+       * lisp.h (struct Lisp_Vector, struct Lisp_Bool_Vector)
+       (struct Lisp_Char_Table, struct Lisp_Sub_Char_Table):
+       Use FLEXIBLE_ARRAY_MEMBER.
+       * lisp.h (string_bytes) [GC_CHECK_STRING_BYTES]:
+       Move decl to top level so it gets checked against implementation.
+       (CHAR_TABLE_STANDARD_SLOTS): Adjust to struct Lisp_Char_Table change.
+       * w32.c (FLEXIBLE_ARRAY_MEMBER): Move to conf_post.h.
+
 2013-06-20  Paul Eggert  <address@hidden>
 
        * syntax.c: Integer cleanups.

=== modified file 'src/alloc.c'
--- a/src/alloc.c       2013-06-20 14:47:46 +0000
+++ b/src/alloc.c       2013-06-21 20:11:44 +0000
@@ -1260,7 +1260,7 @@
    When a Lisp_String is freed during GC, it is put back on
    string_free_list, and its `data' member and its sdata's `string'
    pointer is set to null.  The size of the string is recorded in the
-   `u.nbytes' member of the sdata.  So, sdata structures that are no
+   `n.nbytes' member of the sdata.  So, sdata structures that are no
    longer used, can be easily recognized, and it's easy to compact the
    sblocks of small strings which we do in compact_small_strings.  */
 
@@ -1274,10 +1274,12 @@
 
 #define LARGE_STRING_BYTES 1024
 
-/* Structure describing string memory sub-allocated from an sblock.
+/* Struct or union describing string memory sub-allocated from an sblock.
    This is where the contents of Lisp strings are stored.  */
 
-struct sdata
+#ifdef GC_CHECK_STRING_BYTES
+
+typedef struct
 {
   /* Back-pointer to the string this sdata belongs to.  If null, this
      structure is free, and the NBYTES member of the union below
@@ -1287,34 +1289,42 @@
      contents.  */
   struct Lisp_String *string;
 
-#ifdef GC_CHECK_STRING_BYTES
-
   ptrdiff_t nbytes;
-  unsigned char data[1];
+  unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+} sdata;
 
 #define SDATA_NBYTES(S)        (S)->nbytes
 #define SDATA_DATA(S)  (S)->data
 #define SDATA_SELECTOR(member) member
 
-#else /* not GC_CHECK_STRING_BYTES */
-
-  union
-  {
-    /* When STRING is non-null.  */
-    unsigned char data[1];
-
-    /* When STRING is null.  */
+#else
+
+typedef union
+{
+  struct Lisp_String *string;
+
+  /* When STRING is non-null.  */
+  struct
+  {
+    struct Lisp_String *string;
+    unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+  } u;
+
+  /* When STRING is null.  */
+  struct
+  {
+    struct Lisp_String *string;
     ptrdiff_t nbytes;
-  } u;
+  } n;
+} sdata;
 
-#define SDATA_NBYTES(S)        (S)->u.nbytes
+#define SDATA_NBYTES(S)        (S)->n.nbytes
 #define SDATA_DATA(S)  (S)->u.data
 #define SDATA_SELECTOR(member) u.member
 
 #endif /* not GC_CHECK_STRING_BYTES */
 
-#define SDATA_DATA_OFFSET offsetof (struct sdata, SDATA_SELECTOR (data))
-};
+#define SDATA_DATA_OFFSET offsetof (sdata, SDATA_SELECTOR (data))
 
 
 /* Structure describing a block of memory which is sub-allocated to
@@ -1329,10 +1339,10 @@
 
   /* Pointer to the next free sdata block.  This points past the end
      of the sblock if there isn't any space left in this block.  */
-  struct sdata *next_free;
+  sdata *next_free;
 
   /* Start of data.  */
-  struct sdata first_data;
+  sdata first_data;
 };
 
 /* Number of Lisp strings in a string_block structure.  The 1020 is
@@ -1388,7 +1398,7 @@
    a pointer to the `u.data' member of its sdata structure; the
    structure starts at a constant offset in front of that.  */
 
-#define SDATA_OF_STRING(S) ((struct sdata *) ((S)->data - SDATA_DATA_OFFSET))
+#define SDATA_OF_STRING(S) ((sdata *) ((S)->data - SDATA_DATA_OFFSET))
 
 
 #ifdef GC_CHECK_STRING_OVERRUN
@@ -1487,7 +1497,7 @@
 static void
 check_sblock (struct sblock *b)
 {
-  struct sdata *from, *end, *from_end;
+  sdata *from, *end, *from_end;
 
   end = b->next_free;
 
@@ -1501,7 +1511,7 @@
         same as the one recorded in the sdata structure.  */
       nbytes = SDATA_SIZE (from->string ? string_bytes (from->string)
                           : SDATA_NBYTES (from));
-      from_end = (struct sdata *) ((char *) from + nbytes + GC_STRING_EXTRA);
+      from_end = (sdata *) ((char *) from + nbytes + GC_STRING_EXTRA);
     }
 }
 
@@ -1631,7 +1641,7 @@
 allocate_string_data (struct Lisp_String *s,
                      EMACS_INT nchars, EMACS_INT nbytes)
 {
-  struct sdata *data, *old_data;
+  sdata *data, *old_data;
   struct sblock *b;
   ptrdiff_t needed, old_nbytes;
 
@@ -1701,7 +1711,7 @@
     b = current_sblock;
 
   data = b->next_free;
-  b->next_free = (struct sdata *) ((char *) data + needed + GC_STRING_EXTRA);
+  b->next_free = (sdata *) ((char *) data + needed + GC_STRING_EXTRA);
 
   MALLOC_UNBLOCK_INPUT;
 
@@ -1772,7 +1782,7 @@
              else
                {
                  /* String is dead.  Put it on the free-list.  */
-                 struct sdata *data = SDATA_OF_STRING (s);
+                 sdata *data = SDATA_OF_STRING (s);
 
                  /* Save the size of S in its sdata so that we know
                     how large that is.  Reset the sdata's string
@@ -1781,7 +1791,7 @@
                  if (string_bytes (s) != SDATA_NBYTES (data))
                    emacs_abort ();
 #else
-                 data->u.nbytes = STRING_BYTES (s);
+                 data->n.nbytes = STRING_BYTES (s);
 #endif
                  data->string = NULL;
 
@@ -1862,13 +1872,13 @@
 compact_small_strings (void)
 {
   struct sblock *b, *tb, *next;
-  struct sdata *from, *to, *end, *tb_end;
-  struct sdata *to_end, *from_end;
+  sdata *from, *to, *end, *tb_end;
+  sdata *to_end, *from_end;
 
   /* TB is the sblock we copy to, TO is the sdata within TB we copy
      to, and TB_END is the end of TB.  */
   tb = oldest_sblock;
-  tb_end = (struct sdata *) ((char *) tb + SBLOCK_SIZE);
+  tb_end = (sdata *) ((char *) tb + SBLOCK_SIZE);
   to = &tb->first_data;
 
   /* Step through the blocks from the oldest to the youngest.  We
@@ -1897,7 +1907,7 @@
          eassert (nbytes <= LARGE_STRING_BYTES);
 
          nbytes = SDATA_SIZE (nbytes);
-         from_end = (struct sdata *) ((char *) from + nbytes + 
GC_STRING_EXTRA);
+         from_end = (sdata *) ((char *) from + nbytes + GC_STRING_EXTRA);
 
 #ifdef GC_CHECK_STRING_OVERRUN
          if (memcmp (string_overrun_cookie,
@@ -1910,14 +1920,14 @@
          if (s)
            {
              /* If TB is full, proceed with the next sblock.  */
-             to_end = (struct sdata *) ((char *) to + nbytes + 
GC_STRING_EXTRA);
+             to_end = (sdata *) ((char *) to + nbytes + GC_STRING_EXTRA);
              if (to_end > tb_end)
                {
                  tb->next_free = to;
                  tb = tb->next;
-                 tb_end = (struct sdata *) ((char *) tb + SBLOCK_SIZE);
+                 tb_end = (sdata *) ((char *) tb + SBLOCK_SIZE);
                  to = &tb->first_data;
-                 to_end = (struct sdata *) ((char *) to + nbytes + 
GC_STRING_EXTRA);
+                 to_end = (sdata *) ((char *) to + nbytes + GC_STRING_EXTRA);
                }
 
              /* Copy, and update the string's `data' pointer.  */
@@ -2581,7 +2591,7 @@
 
 /* Size of the minimal vector allocated from block.  */
 
-#define VBLOCK_BYTES_MIN vroundup (sizeof (struct Lisp_Vector))
+#define VBLOCK_BYTES_MIN vroundup (header_size + sizeof (Lisp_Object))
 
 /* Size of the largest vector allocated from block.  */
 
@@ -2938,7 +2948,8 @@
       else
        {
          struct large_vector *lv
-           = lisp_malloc (sizeof (*lv) + (len - 1) * word_size,
+           = lisp_malloc ((offsetof (struct large_vector, v.contents)
+                           + len * word_size),
                           MEM_TYPE_VECTORLIKE);
          lv->next.vector = large_vectors;
          large_vectors = lv;
@@ -5416,7 +5427,8 @@
     total[4] = list3 (Qstring_bytes, make_number (1),
                      bounded_number (total_string_bytes));
 
-    total[5] = list3 (Qvectors, make_number (sizeof (struct Lisp_Vector)),
+    total[5] = list3 (Qvectors,
+                     make_number (header_size + sizeof (Lisp_Object)),
                      bounded_number (total_vectors));
 
     total[6] = list4 (Qvector_slots, make_number (word_size),

=== modified file 'src/chartab.c'
--- a/src/chartab.c     2013-06-17 06:03:19 +0000
+++ b/src/chartab.c     2013-06-21 20:11:44 +0000
@@ -128,7 +128,7 @@
       n_extras = XINT (n);
     }
 
-  size = VECSIZE (struct Lisp_Char_Table) - 1 + n_extras;
+  size = CHAR_TABLE_STANDARD_SLOTS + n_extras;
   vector = Fmake_vector (make_number (size), init);
   XSETPVECTYPE (XVECTOR (vector), PVEC_CHAR_TABLE);
   set_char_table_parent (vector, Qnil);
@@ -141,7 +141,7 @@
 make_sub_char_table (int depth, int min_char, Lisp_Object defalt)
 {
   Lisp_Object table;
-  int size = VECSIZE (struct Lisp_Sub_Char_Table) - 1 + chartab_size[depth];
+  int size = CHAR_TABLE_STANDARD_SLOTS + chartab_size[depth];
 
   table = Fmake_vector (make_number (size), defalt);
   XSETPVECTYPE (XVECTOR (table), PVEC_SUB_CHAR_TABLE);
@@ -207,7 +207,7 @@
        ? copy_sub_char_table (XCHAR_TABLE (table)->contents[i])
        : XCHAR_TABLE (table)->contents[i]));
   set_char_table_ascii (copy, char_table_ascii (copy));
-  size -= VECSIZE (struct Lisp_Char_Table) - 1;
+  size -= CHAR_TABLE_STANDARD_SLOTS;
   for (i = 0; i < size; i++)
     set_char_table_extras (copy, i, XCHAR_TABLE (table)->extras[i]);
 

=== modified file 'src/conf_post.h'
--- a/src/conf_post.h   2013-06-18 18:36:13 +0000
+++ b/src/conf_post.h   2013-06-21 20:11:44 +0000
@@ -243,6 +243,17 @@
 #define INLINE_HEADER_BEGIN _GL_INLINE_HEADER_BEGIN
 #define INLINE_HEADER_END _GL_INLINE_HEADER_END
 
+/* To use the struct hack with N elements, declare the struct like this:
+     struct s { ...; t name[FLEXIBLE_ARRAY_MEMBER]; };
+   and allocate (offsetof (struct s, name) + N * sizeof (t)) bytes.  */
+#if 199901 <= __STDC_VERSION__
+# define FLEXIBLE_ARRAY_MEMBER
+#elif __GNUC__ && !defined __STRICT_ANSI__
+# define FLEXIBLE_ARRAY_MEMBER 0
+#else
+# define FLEXIBLE_ARRAY_MEMBER 1
+#endif
+
 /* Use this to suppress gcc's `...may be used before initialized' warnings. */
 #ifdef lint
 /* Use CODE only if lint checking is in effect.  */

=== modified file 'src/image.c'
--- a/src/image.c       2013-05-12 19:17:04 +0000
+++ b/src/image.c       2013-06-21 20:11:44 +0000
@@ -3057,7 +3057,7 @@
   XColor color;
 
   /* Color name.  */
-  char name[1];
+  char name[FLEXIBLE_ARRAY_MEMBER];
 };
 
 /* The hash table used for the color cache, and its bucket vector

=== modified file 'src/lisp.h'
--- a/src/lisp.h        2013-06-20 14:47:46 +0000
+++ b/src/lisp.h        2013-06-21 20:11:44 +0000
@@ -1073,16 +1073,20 @@
 {
   return XSTRING (string)->size;
 }
+
+#ifdef GC_CHECK_STRING_BYTES
+extern ptrdiff_t string_bytes (struct Lisp_String *);
+#endif
 LISP_INLINE ptrdiff_t
 STRING_BYTES (struct Lisp_String *s)
 {
 #ifdef GC_CHECK_STRING_BYTES
-  extern ptrdiff_t string_bytes (struct Lisp_String *);
   return string_bytes (s);
 #else
   return s->size_byte < 0 ? s->size : s->size_byte;
 #endif
 }
+
 LISP_INLINE ptrdiff_t
 SBYTES (Lisp_Object string)
 {
@@ -1136,7 +1140,7 @@
 struct Lisp_Vector
   {
     struct vectorlike_header header;
-    Lisp_Object contents[1];
+    Lisp_Object contents[FLEXIBLE_ARRAY_MEMBER];
   };
 
 /* A boolvector is a kind of vectorlike, with contents are like a string.  */
@@ -1149,7 +1153,7 @@
     /* This is the size in bits.  */
     EMACS_INT size;
     /* This contains the actual bits, packed into bytes.  */
-    unsigned char data[1];
+    unsigned char data[FLEXIBLE_ARRAY_MEMBER];
   };
 
 /* Some handy constants for calculating sizes
@@ -1272,7 +1276,7 @@
     Lisp_Object contents[(1 << CHARTAB_SIZE_BITS_0)];
 
     /* These hold additional data.  It is a vector.  */
-    Lisp_Object extras[1];
+    Lisp_Object extras[FLEXIBLE_ARRAY_MEMBER];
   };
 
 struct Lisp_Sub_Char_Table
@@ -1293,7 +1297,7 @@
     Lisp_Object min_char;
 
     /* Use set_sub_char_table_contents to set this.  */
-    Lisp_Object contents[1];
+    Lisp_Object contents[FLEXIBLE_ARRAY_MEMBER];
   };
 
 LISP_INLINE Lisp_Object
@@ -1366,7 +1370,7 @@
    slots.  */
 enum CHAR_TABLE_STANDARD_SLOTS
   {
-    CHAR_TABLE_STANDARD_SLOTS = VECSIZE (struct Lisp_Char_Table) - 1
+    CHAR_TABLE_STANDARD_SLOTS = PSEUDOVECSIZE (struct Lisp_Char_Table, extras)
   };
 
 /* Return the number of "extra" slots in the char table CT.  */

=== modified file 'src/w32.c'
--- a/src/w32.c 2013-06-03 17:15:44 +0000
+++ b/src/w32.c 2013-06-21 20:11:44 +0000
@@ -3785,12 +3785,6 @@
 
 /* Caching SID and account values for faster lokup.  */
 
-#ifdef __GNUC__
-# define FLEXIBLE_ARRAY_MEMBER
-#else
-# define FLEXIBLE_ARRAY_MEMBER 1
-#endif
-
 struct w32_id {
   unsigned rid;
   struct w32_id *next;


reply via email to

[Prev in Thread] Current Thread [Next in Thread]