Bug 753119 - Add ThreadLocal to mfbt - r=jwalden
authorEhsan Akhgari <ehsan.akhgari@gmail.com>
Wed, 09 May 2012 16:54:33 -0400
changeset 98420 fdac756d438b86fadd9cbf0597d79cb18b006b3f
parent 98419 a726c2e6cde7032058b1df2bcb7b0e9c90a8632d
child 98421 e11498dfd15cb196bfc875404451c7f8adc2e0c5
push id1116
push userlsblakk@mozilla.com
push dateMon, 16 Jul 2012 19:38:18 +0000
treeherdermozilla-beta@95f959a8b4dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs753119
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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