Use portability for SYS_gettid
[folly.git] / folly / Singleton.cpp
index 1029097146aa295c0b6633ebcfd5ac328eb8bfc0..0468679772946a1071de998d5642459e379264b0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include <folly/Singleton.h>
 
+#include <atomic>
+#include <cstdio>
+#include <cstdlib>
+#include <sstream>
 #include <string>
 
+#include <folly/FileUtil.h>
+#include <folly/ScopeGuard.h>
+
 namespace folly {
 
 namespace detail {
 
 constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
 
+[[noreturn]] void singletonWarnDoubleRegistrationAndAbort(
+    const TypeDescriptor& type) {
+  // Not using LOG(FATAL) or std::cerr because they may not be initialized yet.
+  std::ostringstream o;
+  o << "Double registration of singletons of the same "
+    << "underlying type; check for multiple definitions "
+    << "of type folly::Singleton<" << type.name() << ">" << std::endl;
+  auto s = o.str();
+  writeFull(STDERR_FILENO, s.data(), s.size());
+  std::abort();
+}
 }
 
 namespace {
@@ -56,6 +74,114 @@ FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
 
 SingletonVault::~SingletonVault() { destroyInstances(); }
 
+void SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {
+  RWSpinLock::ReadHolder rh(&stateMutex_);
+
+  stateCheck(SingletonVaultState::Running);
+
+  if (UNLIKELY(registrationComplete_)) {
+    throw std::logic_error(
+      "Registering singleton after registrationComplete().");
+  }
+
+  RWSpinLock::ReadHolder rhMutex(&mutex_);
+  CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
+              std::logic_error);
+
+  RWSpinLock::UpgradedHolder wh(&mutex_);
+  singletons_[entry->type()] = entry;
+}
+
+void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
+  RWSpinLock::ReadHolder rh(&stateMutex_);
+
+  stateCheck(SingletonVaultState::Running);
+
+  if (UNLIKELY(registrationComplete_)) {
+    throw std::logic_error(
+        "Registering for eager-load after registrationComplete().");
+  }
+
+  RWSpinLock::ReadHolder rhMutex(&mutex_);
+  CHECK_THROW(singletons_.find(entry->type()) != singletons_.end(),
+              std::logic_error);
+
+  RWSpinLock::UpgradedHolder wh(&mutex_);
+  eagerInitSingletons_.insert(entry);
+}
+
+void SingletonVault::registrationComplete() {
+  std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
+
+  RWSpinLock::WriteHolder wh(&stateMutex_);
+
+  stateCheck(SingletonVaultState::Running);
+
+  if (type_ == Type::Strict) {
+    for (const auto& p : singletons_) {
+      if (p.second->hasLiveInstance()) {
+        throw std::runtime_error(
+            "Singleton created before registration was complete.");
+      }
+    }
+  }
+
+  registrationComplete_ = true;
+}
+
+void SingletonVault::doEagerInit() {
+  std::unordered_set<detail::SingletonHolderBase*> singletonSet;
+  {
+    RWSpinLock::ReadHolder rh(&stateMutex_);
+    stateCheck(SingletonVaultState::Running);
+    if (UNLIKELY(!registrationComplete_)) {
+      throw std::logic_error("registrationComplete() not yet called");
+    }
+    singletonSet = eagerInitSingletons_; // copy set of pointers
+  }
+
+  for (auto *single : singletonSet) {
+    single->createInstance();
+  }
+}
+
+void SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {
+  std::unordered_set<detail::SingletonHolderBase*> singletonSet;
+  {
+    RWSpinLock::ReadHolder rh(&stateMutex_);
+    stateCheck(SingletonVaultState::Running);
+    if (UNLIKELY(!registrationComplete_)) {
+      throw std::logic_error("registrationComplete() not yet called");
+    }
+    singletonSet = eagerInitSingletons_; // copy set of pointers
+  }
+
+  auto countdown = std::make_shared<std::atomic<size_t>>(singletonSet.size());
+  for (auto* single : singletonSet) {
+    // countdown is retained by shared_ptr, and will be alive until last lambda
+    // is done.  notifyBaton is provided by the caller, and expected to remain
+    // present (if it's non-nullptr).  singletonSet can go out of scope but
+    // its values, which are SingletonHolderBase pointers, are alive as long as
+    // SingletonVault is not being destroyed.
+    exe.add([=] {
+      // decrement counter and notify if requested, whether initialization
+      // was successful, was skipped (already initialized), or exception thrown.
+      SCOPE_EXIT {
+        if (--(*countdown) == 0) {
+          if (done != nullptr) {
+            done->post();
+          }
+        }
+      };
+      // if initialization is in progress in another thread, don't try to init
+      // here.  Otherwise the current thread will block on 'createInstance'.
+      if (!single->creationStarted()) {
+        single->createInstance();
+      }
+    });
+  }
+}
+
 void SingletonVault::destroyInstances() {
   RWSpinLock::WriteHolder state_wh(&stateMutex_);
 
@@ -102,7 +228,9 @@ void SingletonVault::reenableInstances() {
 }
 
 void SingletonVault::scheduleDestroyInstances() {
-  RequestContext::saveContext();
+  // Add a dependency on folly::ThreadLocal to make sure all its static
+  // singletons are initalized first.
+  threadlocal_detail::StaticMeta<void>::instance();
 
   class SingletonVaultDestructor {
    public: