Bug 753119 - Add ThreadLocal to mfbt - r=jwalden
authorEhsan Akhgari <ehsan.akhgari@gmail.com>
Wed, 09 May 2012 16:54:33 -0400
changeset 94366 fdac756d438b86fadd9cbf0597d79cb18b006b3f
parent 94365 a726c2e6cde7032058b1df2bcb7b0e9c90a8632d
child 94367 e11498dfd15cb196bfc875404451c7f8adc2e0c5
push idunknown
push userunknown
push dateunknown
reviewersjwalden
bugs753119
milestone15.0a1
Bug 753119 - Add ThreadLocal to mfbt - r=jwalden ... because nobody likes to use the NSPR TLS support.
mfbt/ThreadLocal.h
mfbt/exported_headers.mk
tools/profiler/Makefile.in
tools/profiler/TableTicker.cpp
tools/profiler/sps_sampler.h
tools/profiler/thread_helper.h
new file mode 100644
--- /dev/null
+++ b/mfbt/ThreadLocal.h
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Cross-platform lightweight thread local data wrappers
+
+#ifndef mozilla_TLS_h_
+#define mozilla_TLS_h_
+
+#if defined(XP_WIN)
+// This file will get included in any file that wants to add a profiler mark.
+// In order to not bring <windows.h> together we could include windef.h and
+// winbase.h which are sufficient to get the prototypes for the Tls* functions.
+// # include <windef.h>
+// # include <winbase.h>
+// Unfortunately, even including these headers causes us to add a bunch of ugly
+// stuff to our namespace e.g #define CreateEvent CreateEventW
+extern "C" {
+__declspec(dllimport) void * __stdcall TlsGetValue(unsigned long);
+__declspec(dllimport) int __stdcall TlsSetValue(unsigned long, void *);
+__declspec(dllimport) unsigned long __stdcall TlsAlloc();
+}
+#else
+#  include <pthread.h>
+#  include <signal.h>
+#endif
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+
+// sig_safe_t denotes an atomic type which can be read or stored in a single
+// instruction.  This means that data of this type is safe to be manipulated
+// from a signal handler, or other similar asynchronous execution contexts.
+#if defined(XP_WIN)
+typedef unsigned long sig_safe_t;
+#else
+typedef sig_atomic_t sig_safe_t;
+#endif
+
+/*
+ * Thread Local Storage helpers.
+ *
+ * Usage:
+ *
+ * Only static-storage-duration (e.g. global variables, or static class members)
+ * objects of this class should be instantiated. This class relies on
+ * zero-initialization, which is implicit for static-storage-duration objects.
+ * It doesn't have a custom default constructor, to avoid static initializers.
+ *
+ * API usage:
+ *
+ * // Create a TLS item
+ * mozilla::ThreadLocal<int> tlsKey;
+ * if (!tlsKey.init()) {
+ *   // deal with the error
+ * }
+ *
+ * // Set the TLS value
+ * tlsKey.set(123);
+ *
+ * // Get the TLS value
+ * int value = tlsKey.get();
+ */
+template <typename T>
+class ThreadLocal
+{
+#if defined(XP_WIN)
+    typedef unsigned long key_t;
+#else
+    typedef pthread_key_t key_t;
+#endif
+
+  public:
+    MOZ_WARN_UNUSED_RESULT inline bool init();
+
+    inline T* get() const;
+
+    inline bool set(const T* value);
+
+    bool initialized() const {
+      return inited;
+    }
+
+  private:
+    key_t key;
+    bool inited;
+};
+
+template <typename T>
+inline bool
+ThreadLocal<T>::init() {
+  MOZ_ASSERT(!initialized());
+#ifdef XP_WIN
+  key = TlsAlloc();
+  inited = key != 0xFFFFFFFFUL; // TLS_OUT_OF_INDEXES
+#else
+  inited = !pthread_key_create(&key, NULL);
+#endif
+  return inited;
+}
+
+template <typename T>
+inline T*
+ThreadLocal<T>::get() const {
+  MOZ_ASSERT(initialized());
+#ifdef XP_WIN
+  return reinterpret_cast<T*>(TlsGetValue(key));
+#else
+  return reinterpret_cast<T*>(pthread_getspecific(key));
+#endif
+}
+
+template <typename T>
+inline bool
+ThreadLocal<T>::set(const T* value) {
+  MOZ_ASSERT(initialized());
+#ifdef XP_WIN
+  return TlsSetValue(key, const_cast<T*>(value));
+#else
+  return !pthread_setspecific(key, value);
+#endif
+}
+
+} // namespace mozilla
+
+#endif // mozilla_TLS_h_
--- a/mfbt/exported_headers.mk
+++ b/mfbt/exported_headers.mk
@@ -51,11 +51,12 @@ EXPORTS_mozilla += \
   HashFunctions.h \
   Likely.h \
   LinkedList.h \
   MSStdInt.h \
   RangedPtr.h \
   RefPtr.h \
   Scoped.h \
   StandardInteger.h \
+  ThreadLocal.h \
   Types.h \
   Util.h \
   $(NULL)
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -39,18 +39,17 @@
 
 DEPTH       = ../..
 topsrcdir	  = @top_srcdir@
 srcdir      = @srcdir@
 VPATH       = $(srcdir)
 
 include $(DEPTH)/config/autoconf.mk
 
-EXPORTS = sampler.h \
-  thread_helper.h
+EXPORTS = sampler.h
 
 ifdef MOZ_ENABLE_PROFILER_SPS
 EXPORTS += \
   sps_sampler.h \
   shared-libraries.h \
   $(NULL)
 
 LOCAL_INCLUDES += \
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -97,18 +97,18 @@ using namespace mozilla;
  #endif
 #endif
 
 #if _MSC_VER
  #define snprintf _snprintf
 #endif
 
 
-mozilla::tls::key pkey_stack;
-mozilla::tls::key pkey_ticker;
+mozilla::ThreadLocal<ProfileStack> pkey_stack;
+mozilla::ThreadLocal<TableTicker> pkey_ticker;
 // We need to track whether we've been initialized otherwise
 // we end up using pkey_stack without initializing it.
 // Because pkey_stack is totally opaque to us we can't reuse
 // it as the flag itself.
 bool stack_key_initialized;
 
 TimeStamp sLastTracerEvent;
 
@@ -387,17 +387,17 @@ std::string GetSharedLibraryInfoString()
  * This is an event used to save the profile on the main thread
  * to be sure that it is not being modified while saving.
  */
 class SaveProfileTask : public nsRunnable {
 public:
   SaveProfileTask() {}
 
   NS_IMETHOD Run() {
-    TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
+    TableTicker *t = pkey_ticker.get();
 
     char buff[MAXPATHLEN];
 #ifdef ANDROID
   #define FOLDER "/sdcard/"
 #elif defined(XP_WIN)
   #define FOLDER "%TEMP%\\"
 #else
   #define FOLDER "/tmp/"
@@ -685,26 +685,24 @@ std::ostream& operator<<(std::ostream& s
   } else {
     stream << entry.mTagName << "-" << entry.mTagData << "\n";
   }
   return stream;
 }
 
 void mozilla_sampler_init()
 {
-  // TODO linux port: Use TLS with ifdefs
-  if (!mozilla::tls::create(&pkey_stack) ||
-      !mozilla::tls::create(&pkey_ticker)) {
+  if (!pkey_stack.init() || !pkey_ticker.init()) {
     LOG("Failed to init.");
     return;
   }
   stack_key_initialized = true;
 
   ProfileStack *stack = new ProfileStack();
-  mozilla::tls::set(pkey_stack, stack);
+  pkey_stack.set(stack);
 
 #if defined(USE_LIBUNWIND) && defined(ANDROID)
   // Only try debug_frame and exidx unwinding
   putenv("UNW_ARM_UNWIND_METHOD=5");
 
   // Allow the profiler to be started and stopped using signals
   OS::RegisterStartStopHandlers();
 
@@ -730,46 +728,46 @@ void mozilla_sampler_deinit()
   mozilla_sampler_stop();
   // We can't delete the Stack because we can be between a
   // sampler call_enter/call_exit point.
   // TODO Need to find a safe time to delete Stack
 }
 
 void mozilla_sampler_save()
 {
-  TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
+  TableTicker *t = pkey_ticker.get();
   if (!t) {
     return;
   }
 
   t->RequestSave();
   // We're on the main thread already so we don't
   // have to wait to handle the save request.
   t->HandleSaveRequest();
 }
 
 char* mozilla_sampler_get_profile()
 {
-  TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
+  TableTicker *t = pkey_ticker.get();
   if (!t) {
     return NULL;
   }
 
   std::stringstream profile;
   profile << *(t->GetPrimaryThreadProfile());
 
   std::string profileString = profile.str();
   char *rtn = (char*)malloc( (profileString.length() + 1) * sizeof(char) );
   strcpy(rtn, profileString.c_str());
   return rtn;
 }
 
 JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
 {
-  TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
+  TableTicker *t = pkey_ticker.get();
   if (!t) {
     return NULL;
   }
 
   return t->ToJSObject(aCx);
 }
 
 
@@ -785,44 +783,44 @@ const char** mozilla_sampler_get_feature
 
   return features;
 }
 
 // Values are only honored on the first start
 void mozilla_sampler_start(int aProfileEntries, int aInterval,
                            const char** aFeatures, uint32_t aFeatureCount)
 {
-  ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
+  ProfileStack *stack = pkey_stack.get();
   if (!stack) {
     ASSERT(false);
     return;
   }
 
   mozilla_sampler_stop();
 
   TableTicker *t = new TableTicker(aInterval, aProfileEntries, stack,
                                    aFeatures, aFeatureCount);
-  mozilla::tls::set(pkey_ticker, t);
+  pkey_ticker.set(t);
   t->Start();
 }
 
 void mozilla_sampler_stop()
 {
-  TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
+  TableTicker *t = pkey_ticker.get();
   if (!t) {
     return;
   }
 
   t->Stop();
-  mozilla::tls::set(pkey_ticker, (ProfileStack*)NULL);
+  pkey_ticker.set(NULL);
 }
 
 bool mozilla_sampler_is_active()
 {
-  TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
+  TableTicker *t = pkey_ticker.get();
   if (!t) {
     return false;
   }
 
   return t->IsActive();
 }
 
 double sResponsivenessTimes[100];
--- a/tools/profiler/sps_sampler.h
+++ b/tools/profiler/sps_sampler.h
@@ -33,27 +33,30 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <stdlib.h>
 #include <signal.h>
-#include "thread_helper.h"
+#include "mozilla/ThreadLocal.h"
 #include "nscore.h"
 #include "jsapi.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Util.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
-extern mozilla::tls::key pkey_stack;
-extern mozilla::tls::key pkey_ticker;
+struct ProfileStack;
+class TableTicker;
+
+extern mozilla::ThreadLocal<ProfileStack> pkey_stack;
+extern mozilla::ThreadLocal<TableTicker> pkey_ticker;
 extern bool stack_key_initialized;
 
 #ifndef SAMPLE_FUNCTION_NAME
 # ifdef __GNUC__
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # elif defined(_MSC_VER)
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # else
@@ -275,17 +278,17 @@ public:
 
 inline void* mozilla_sampler_call_enter(const char *aInfo)
 {
   // check if we've been initialized to avoid calling pthread_getspecific
   // with a null pkey_stack which will return undefined results.
   if (!stack_key_initialized)
     return NULL;
 
-  ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
+  ProfileStack *stack = pkey_stack.get();
   // we can't infer whether 'stack' has been initialized
   // based on the value of stack_key_intiailized because
   // 'stack' is only intialized when a thread is being
   // profiled.
   if (!stack) {
     return stack;
   }
   stack->push(aInfo);
@@ -304,15 +307,15 @@ inline void mozilla_sampler_call_exit(vo
     return;
 
   ProfileStack *stack = (ProfileStack*)aHandle;
   stack->pop();
 }
 
 inline void mozilla_sampler_add_marker(const char *aMarker)
 {
-  ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
+  ProfileStack *stack = pkey_stack.get();
   if (!stack) {
     return;
   }
   stack->addMarker(aMarker);
 }
 
deleted file mode 100644
--- a/tools/profiler/thread_helper.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Ehsan Akhgari <ehsan@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Cross-platform lightweight thread local data wrappers
-
-#ifndef MOZ_THREAD_HELPER_H
-#define MOZ_THREAD_HELPER_H
-
-#if defined(XP_WIN)
-  // This file will get included in any file that wants to add
-  // a profiler mark. In order to not bring <windows.h> together
-  // we could include windef.h and winbase.h which are sufficient
-  // to get the prototypes for the Tls* functions.
-  // # include <windef.h>
-  // # include <winbase.h>
-  // Unfortunately, even including these headers causes
-  // us to add a bunch of ugly to our namespace. e.g #define CreateEvent CreateEventW
-extern "C" {
-__declspec(dllimport) void * __stdcall TlsGetValue(unsigned long);
-__declspec(dllimport) int __stdcall TlsSetValue(unsigned long, void *);
-__declspec(dllimport) unsigned long __stdcall TlsAlloc();
-}
-#else
-# include <pthread.h>
-# include <signal.h>
-#endif
-
-namespace mozilla {
-
-#if defined(XP_WIN)
-typedef unsigned long sig_safe_t;
-#else
-typedef sig_atomic_t sig_safe_t;
-#endif
-
-namespace tls {
-
-#if defined(XP_WIN)
-
-typedef unsigned long key;
-
-template <typename T>
-inline T* get(key mykey) {
-  return (T*) TlsGetValue(mykey);
-}
-
-template <typename T>
-inline bool set(key mykey, const T* value) {
-  return TlsSetValue(mykey, const_cast<T*>(value));
-}
-
-inline bool create(key* mykey) {
-  key newkey = TlsAlloc();
-  if (newkey == (unsigned long)0xFFFFFFFF /* TLS_OUT_OF_INDEXES */) {
-    return false;
-  }
-  *mykey = newkey;
-  return true;
-}
-
-#else
-
-typedef pthread_key_t key;
-
-template <typename T>
-inline T* get(key mykey) {
-  return (T*) pthread_getspecific(mykey);
-}
-
-template <typename T>
-inline bool set(key mykey, const T* value) {
-  return !pthread_setspecific(mykey, value);
-}
-
-inline bool create(key* mykey) {
-  return !pthread_key_create(mykey, NULL);
-}
-
-#endif
-
-}
-
-}
- 
-#endif // MOZ_THREAD_HELPER_H
\ No newline at end of file