]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Formalize VThreadBase "Stable" and "Kernel" IDs
authorOliver Kurth <okurth@vmware.com>
Fri, 15 Sep 2017 18:23:49 +0000 (11:23 -0700)
committerOliver Kurth <okurth@vmware.com>
Fri, 15 Sep 2017 18:23:49 +0000 (11:23 -0700)
Unrelated to open-vm-tools.

open-vm-tools/lib/include/vthreadBase.h
open-vm-tools/lib/misc/vthreadBase.c

index ff14cc3334dc1a6316fe21950b33e5cc25952907..f111eabf527a1a405916823048c8a7e6e3306d33 100644 (file)
@@ -174,6 +174,8 @@ Bool VThreadBase_IsInSignal(void);
 void VThreadBase_SetIsInSignal(Bool isInSignal);
 #endif
 
+uint64 VThreadBase_GetKernelID(void);
+
 #endif /* VMM */
 
 #if defined(__cplusplus)
index ca2e79b22ee779f278585392da5910e97426de5c..37d2c3548857e394e40a7d76d2e688c662d1fda1 100644 (file)
@@ -651,6 +651,62 @@ VThreadBaseGetAndInitBase(void)
 }
 
 
+#if 0
+// Disabled until used in upcoming change
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseGetStableID --
+ *
+ *      Stable thread-id function.
+ *
+ *      In contrast to VThreadBase_GetKernelID below, this stable ID is safe
+ *      for caching. Unfortunately, it tends to not be human readable,
+ *      is not understood by the kernel, and makes no sense passed to
+ *      any other process.
+ *
+ *      Win32 was always safe; for POSIX, we instead make use of the fact
+ *      that pthread_t values (by definition) have to be stable across process
+ *      fork. That is:
+ *         pthread_t before = pthread_self();
+ *         fork();
+ *         pthread_t after = pthread_self();
+ *         ret = pthread_equal(before, after);  <---- POSIX requires equality
+ *      POSIX leaves the exact mechanism unspecified, but in practice
+ *      most POSIX OSes make pthread_t a pointer and make use of the fact that
+ *      the address space is fully cloned so the pointer will not change.
+ *      (An exception is Solaris, which uses integer LWP indexes but
+ *      clones the per-process LWP table at fork).
+ *
+ *      The assumption above is technically non-portable, as POSIX also
+ *      permits pthread_t to be a structure. We do not support any OS
+ *      which uses a structure definition.
+ *
+ * Results:
+ *      Some sort of stable ID.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static uintptr_t
+VThreadBaseGetStableID(void)
+{
+#if defined _WIN32
+   return GetCurrentThreadId();
+#elif defined __sun__
+   /* pthread_t is a uint_t index into a per-process LWP table */
+   return pthread_self();
+#else
+   /* pthread_t is (hopefully) an opaque pointer type */
+   return (uintptr_t)(void *)pthread_self();
+#endif
+}
+#endif
+
+
 /*
  *-----------------------------------------------------------------------------
  *
@@ -684,17 +740,46 @@ VThreadBase_CurID(void)
 /*
  *-----------------------------------------------------------------------------
  *
- * VThreadBaseNativeID --
- *
- *      Native thread-id function.
- *
- *      Most OSes tend to use 'small' numbers (32-bit in practice),
- *      these IDs are more readable in log files than e.g. a pointer.
- *
- *      TODO: merge with Util_GetCurrentThreadId()
+ * VThreadBase_GetKernelID --
+ *
+ *      Native thread-id function. USE WITH GREAT CAUTION.
+ *
+ *      Details below. In short, the ID returned by this function is both
+ *      'pretty' (tends to be short, e.g. readable as a decimal) and 'native'
+ *      in that it is useful for correlating with kernel threads.
+ *      However, this ID is not fork-safe on at least Linux.
+ *
+ *      In practice, VThreadBase chooses to use this ID for thread names only.
+ *
+ *      Most POSIX: With most modern threading implementations, threads are
+ *        "lightweight processes" (LWP), so any native TID changes after
+ *        a fork(). Which leads to pthread_atfork() - you can find out that TID
+ *        changed, but it's up to you to fix up all cached copies.
+ *        (A clever soul might suggest just continuing to use the old TID.
+ *        That clever soul is not so clever, having forgotten that POSIX OSes
+ *        recycle LWPs so all it takes is a couple of forks for you to have a
+ *        cached TID on one thread match the native TID on another thread. Hope
+ *        you didn't need that TID for correctness!).
+ *        The good news is nearly all POSIX has a pthread NP API (non-portable)
+ *        to provide the right thing.
+ *      Linux (glibc): is the exception to "nearly all". The *only* way to get a
+ *        system ID is via gettid() syscall. Which is a syscall and thus
+ *        expensive relative to every other OS. This code implements the pthread
+ *        NP wrapper that glibc *should* have.
+ *      Windows: good news. Not having a fork() API means the 'pretty'
+ *        ID returned here is stable forever. No special cases.
+ *      Solaris: good news. Solaris implements the LWP namespace *per process*,
+ *        which it clones on fork, meaning the forked child still gets the
+ *        same LWP IDs. Likely a legacy of SunOS which had forkall().
+ *
+ *      Obviously, specific mechanisms for obtaining native IDs are *highly*
+ *      non-portable, as indicated by the _np suffixes.
+ *
+ *      TODO: merge with Util_GetCurrentThreadId(), or delete that function
+ *      because anybody using it probably hasn't read the above disclaimer.
  *
  * Results:
- *      Some sort of ID.
+ *      Some sort of system ID (e.g. LWP, Task, ...)
  *
  * Side effects:
  *      None.
@@ -702,30 +787,58 @@ VThreadBase_CurID(void)
  *-----------------------------------------------------------------------------
  */
 
-static uint64
-VThreadBaseNativeID(void)
+#ifdef __linux__
+static pid_t
+vmw_pthread_getthreadid_np(void)
 {
-#if defined(_WIN32)
-   return GetCurrentThreadId();
-#elif defined(__APPLE__)
+   static __thread struct {
+      pid_t pid;
+      pid_t tid;
+   } cache = { 0, 0 };
+
+   /*
+    * Linux (well, glibc) gets TWO things wrong, but the combination makes
+    * a right, oddly enough. (1) There is no gettid function, because glibc
+    * people decided anybody who needs a system ID is wrong (glibc bug 6399)
+    * and should instead do a system call to get it. (2) they 'optimized'
+    * getpid() to cache its result (which depends on forking only via POSIX
+    * calls and not via syscalls), then decided they knew better than Linus
+    * when he told them this was wrong.
+    * BUT... the getpid cache can be used to make a sufficiently-correct and
+    * fast gettid cache.
+    */
+   if (UNLIKELY(cache.pid != getpid())) {
+      cache.pid = getpid();
+      cache.tid = syscall(__NR_gettid);
+   }
+   return cache.tid;
+}
+#endif
+
+uint64
+VThreadBase_GetKernelID(void)
+{
+#if defined _WIN32
+   return /* DWORD */ GetCurrentThreadId();
+#elif defined __APPLE__
    /* Available as of 10.6 */
-   uint64 hostTid;
+   uint64 hostTid;  // Mach Task ID
    pthread_threadid_np(NULL /* current thread */, &hostTid);
    return hostTid;
-#elif defined(__ANDROID__)
-   return gettid();
-#elif defined(VMX86_SERVER) || defined(__linux__)
-   return syscall(__NR_gettid);  // pid_t, 32-bit, is the LWP
-#elif defined(__sun__)
-   return pthread_self();  // uint_t, Solaris uses LWP as pthread_t
-#elif defined(__FreeBSD__)
+#elif defined __ANDROID__
+   return /* pid_t */ gettid();  // Thank you, Bionic.
+#elif defined __linux__
+   return vmw_pthread_getthreadid_np();
+#elif defined __sun__
+   return /* uint_t */ pthread_self();  // Solaris uses LWP as pthread_t
+#elif defined __FreeBSD__
 #  if 0
-   // Requires FreeBSD 9, we currently use FreeBSD 6.
-   return pthread_getthreadid_np();   // must include <pthread_np.h>
+   // Requires FreeBSD 9, we currently use FreeBSD 6. Include <pthread_np.h>
+   return /* int */ pthread_getthreadid_np();
 #  endif
+   // Best effort until FreeBSD header update
    return (uint64)(uintptr_t)(void *)pthread_self();
 #else
-   // pthread_self() is available, but cannot be safely cast...
 #  error "Unknown platform"
 #endif
 }
@@ -756,10 +869,10 @@ VThreadBaseSafeName(char *buf,   // OUT: new name
    /*
     * This function should not ASSERT, Panic or call a Str_
     * function (that can ASSERT or Panic), as the Panic handler is
-    * very likey to query the thread name and end up right back here.
+    * very likely to query the thread name and end up right back here.
     */
    snprintf(buf, len - 1 /* keep buffer NUL-terminated */,
-            "host-%"FMT64"u", VThreadBaseNativeID());
+            "host-%"FMT64"u", VThreadBase_GetKernelID());
 }