/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include "folly/ThreadLocal.h"
+#include <folly/ThreadLocal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
-#include "folly/Benchmark.h"
+#include <folly/Benchmark.h>
using namespace folly;
EXPECT_FALSE(tl);
}
+TEST(ThreadLocalPtr, TestRelease) {
+ Widget::totalVal_ = 0;
+ ThreadLocalPtr<Widget> w;
+ std::unique_ptr<Widget> wPtr;
+ std::thread([&w, &wPtr]() {
+ w.reset(new Widget());
+ w.get()->val_ += 10;
+
+ wPtr.reset(w.release());
+ }).join();
+ EXPECT_EQ(0, Widget::totalVal_);
+ wPtr.reset();
+ EXPECT_EQ(10, Widget::totalVal_);
+}
+
+TEST(ThreadLocalPtr, CreateOnThreadExit) {
+ Widget::totalVal_ = 0;
+ ThreadLocal<Widget> w;
+ ThreadLocalPtr<int> tl;
+
+ std::thread([&] {
+ tl.reset(new int(1), [&] (int* ptr, TLPDestructionMode mode) {
+ delete ptr;
+ // This test ensures Widgets allocated here are not leaked.
+ ++w.get()->val_;
+ ThreadLocal<Widget> wl;
+ ++wl.get()->val_;
+ });
+ }).join();
+ EXPECT_EQ(2, Widget::totalVal_);
+}
+
// Test deleting the ThreadLocalPtr object
TEST(ThreadLocalPtr, CustomDeleter2) {
Widget::totalVal_ = 0;
TEST(ThreadLocal, InterleavedDestructors) {
Widget::totalVal_ = 0;
- ThreadLocal<Widget>* w = NULL;
+ std::unique_ptr<ThreadLocal<Widget>> w;
int wVersion = 0;
const int wVersionMax = 2;
int thIter = 0;
{
std::lock_guard<std::mutex> g(lock);
thIterPrev = thIter;
- delete w;
- w = new ThreadLocal<Widget>();
+ w.reset(new ThreadLocal<Widget>());
++wVersion;
}
while (true) {
} // namespace
-#if FOLLY_HAVE_STD__THIS_THREAD__SLEEP_FOR
+#if FOLLY_HAVE_STD_THIS_THREAD_SLEEP_FOR
TEST(ThreadLocal, Stress) {
constexpr size_t numFillObjects = 250;
std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
} // namespace
+#ifdef FOLLY_HAVE_PTHREAD_ATFORK
TEST(ThreadLocal, Fork) {
EXPECT_EQ(1, ptr->value()); // ensure created
EXPECT_EQ(1, totalValue());
EXPECT_EQ(1, totalValue());
}
+#endif
+
+struct HoldsOneTag2 {};
+
+TEST(ThreadLocal, Fork2) {
+ // A thread-local tag that was used in the parent from a *different* thread
+ // (but not the forking thread) would cause the child to hang in a
+ // ThreadLocalPtr's object destructor. Yeah.
+ ThreadLocal<HoldsOne, HoldsOneTag2> p;
+ {
+ // use tag in different thread
+ std::thread t([&p] { p.get(); });
+ t.join();
+ }
+ pid_t pid = fork();
+ if (pid == 0) {
+ {
+ ThreadLocal<HoldsOne, HoldsOneTag2> q;
+ q.get();
+ }
+ _exit(0);
+ } else if (pid > 0) {
+ int status;
+ EXPECT_EQ(pid, waitpid(pid, &status, 0));
+ EXPECT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+ } else {
+ EXPECT_TRUE(false) << "fork failed";
+ }
+}
// Simple reference implementation using pthread_get_specific
template<typename T>
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
- google::ParseCommandLineFlags(&argc, &argv, true);
- google::SetCommandLineOptionWithMode(
- "bm_max_iters", "100000000", google::SET_FLAG_IF_DEFAULT
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ gflags::SetCommandLineOptionWithMode(
+ "bm_max_iters", "100000000", gflags::SET_FLAG_IF_DEFAULT
);
if (FLAGS_benchmark) {
folly::runBenchmarks();