blob: ebe1910d42fcf18ca89d18d85cdf8ee2e6c6fac3 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef KUDU_UTIL_THREADLOCAL_H_
#define KUDU_UTIL_THREADLOCAL_H_
// Block-scoped static thread local implementation.
//
// Usage is similar to a C++11 thread_local. The BLOCK_STATIC_THREAD_LOCAL macro
// defines a thread-local pointer to the specified type, which is lazily
// instantiated by any thread entering the block for the first time. The
// constructor for the type T is invoked at macro execution time, as expected,
// and its destructor is invoked when the corresponding thread's Runnable
// returns, or when the thread exits.
//
// Inspired by Poco <http://pocoproject.org/docs/Poco.ThreadLocal.html>,
// Andrew Tomazos <http://stackoverflow.com/questions/12049684/>, and
// the C++11 thread_local API.
//
// Example usage:
//
// // Invokes a 3-arg constructor on SomeClass:
// BLOCK_STATIC_THREAD_LOCAL(SomeClass, instance, arg1, arg2, arg3);
// instance->DoSomething();
//
#define BLOCK_STATIC_THREAD_LOCAL(T, t, ...) \
static __thread T* t; \
do { \
if (PREDICT_FALSE(t == NULL)) { \
t = new T(__VA_ARGS__); \
threadlocal::internal::AddDestructor(threadlocal::internal::Destroy<T>, t); \
} \
} while (false)
// Class-scoped static thread local implementation.
//
// Very similar in implementation to the above block-scoped version, but
// requires a bit more syntax and vigilance to use properly.
//
// DECLARE_STATIC_THREAD_LOCAL(Type, instance_var_) must be placed in the
// class header, as usual for variable declarations.
//
// Because these variables are static, they must also be defined in the impl
// file with DEFINE_STATIC_THREAD_LOCAL(Type, Classname, instance_var_),
// which is very much like defining any static member, i.e. int Foo::member_.
//
// Finally, each thread must initialize the instance before using it by calling
// INIT_STATIC_THREAD_LOCAL(Type, instance_var_, ...). This is a cheap
// call, and may be invoked at the top of any method which may reference a
// thread-local variable.
//
// Due to all of these requirements, you should probably declare TLS members
// as private.
//
// Example usage:
//
// // foo.h
// #include "kudu/utils/file.h"
// class Foo {
// public:
// void DoSomething(std::string s);
// private:
// DECLARE_STATIC_THREAD_LOCAL(utils::File, file_);
// };
//
// // foo.cc
// #include "kudu/foo.h"
// DEFINE_STATIC_THREAD_LOCAL(utils::File, Foo, file_);
// void Foo::WriteToFile(std::string s) {
// // Call constructor if necessary.
// INIT_STATIC_THREAD_LOCAL(utils::File, file_, "/tmp/file_location.txt");
// file_->Write(s);
// }
// Goes in the class declaration (usually in a header file).
// dtor must be destructed _after_ t, so it gets defined first.
// Uses a mangled variable name for dtor since it must also be a member of the
// class.
#define DECLARE_STATIC_THREAD_LOCAL(T, t) \
static __thread T* t
// You must also define the instance in the .cc file.
#define DEFINE_STATIC_THREAD_LOCAL(T, Class, t) \
__thread T* Class::t
// Must be invoked at least once by each thread that will access t.
#define INIT_STATIC_THREAD_LOCAL(T, t, ...) \
do { \
if (PREDICT_FALSE(t == NULL)) { \
t = new T(__VA_ARGS__); \
threadlocal::internal::AddDestructor(threadlocal::internal::Destroy<T>, t); \
} \
} while (false)
// Internal implementation below.
namespace kudu {
namespace threadlocal {
namespace internal {
// Add a destructor to the list.
void AddDestructor(void (*destructor)(void*), void* arg);
// Destroy the passed object of type T.
template<class T>
static void Destroy(void* t) {
// With tcmalloc, this should be pretty cheap (same thread as new).
delete reinterpret_cast<T*>(t);
}
} // namespace internal
} // namespace threadlocal
} // namespace kudu
#endif // KUDU_UTIL_THREADLOCAL_H_