Summary: introduce new methods into a public threadpool interface, - allow submission of std::functions as they allow greater flexibility. - add Joining methods to the implementation to join scheduled and submitted jobs with an option to cancel jobs that did not start executing. - Remove ugly `#ifdefs` between pthread and std implementation, make it uniform. - introduce pimpl for a drop in replacement of the implementation - Introduce rocksdb::port::Thread typedef which is a replacement for std::thread. On Posix Thread defaults as before std::thread. - Implement WindowsThread that allocates memory in a more controllable manner than windows std::thread with a replaceable implementation. - should be no functionality changes. Closes https://github.com/facebook/rocksdb/pull/1823 Differential Revision: D4492902 Pulled By: siying fbshipit-source-id: c74cb11main
parent
1aaa898cf1
commit
0a4cdde50a
@ -0,0 +1,166 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "port/win/win_thread.h" |
||||
|
||||
#include <assert.h> |
||||
#include <process.h> // __beginthreadex |
||||
#include <Windows.h> |
||||
|
||||
#include <stdexcept> |
||||
#include <system_error> |
||||
#include <thread> |
||||
|
||||
namespace rocksdb { |
||||
namespace port { |
||||
|
||||
struct WindowsThread::Data { |
||||
|
||||
std::function<void()> func_; |
||||
uintptr_t handle_; |
||||
|
||||
Data(std::function<void()>&& func) : |
||||
func_(std::move(func)), |
||||
handle_(0) { |
||||
} |
||||
|
||||
Data(const Data&) = delete; |
||||
Data& operator=(const Data&) = delete; |
||||
|
||||
static unsigned int __stdcall ThreadProc(void* arg); |
||||
}; |
||||
|
||||
|
||||
void WindowsThread::Init(std::function<void()>&& func) { |
||||
|
||||
data_.reset(new Data(std::move(func))); |
||||
|
||||
data_->handle_ = _beginthreadex(NULL, |
||||
0, // stack size
|
||||
&Data::ThreadProc, |
||||
data_.get(), |
||||
0, // init flag
|
||||
&th_id_); |
||||
|
||||
if (data_->handle_ == 0) { |
||||
throw std::system_error(std::make_error_code( |
||||
std::errc::resource_unavailable_try_again), |
||||
"Unable to create a thread"); |
||||
} |
||||
} |
||||
|
||||
WindowsThread::WindowsThread() : |
||||
data_(nullptr), |
||||
th_id_(0) |
||||
{} |
||||
|
||||
|
||||
WindowsThread::~WindowsThread() { |
||||
// Must be joined or detached
|
||||
// before destruction.
|
||||
// This is the same as std::thread
|
||||
if (data_) { |
||||
if (joinable()) { |
||||
assert(false); |
||||
std::terminate(); |
||||
} |
||||
data_.reset(); |
||||
} |
||||
} |
||||
|
||||
WindowsThread::WindowsThread(WindowsThread&& o) noexcept : |
||||
WindowsThread() { |
||||
*this = std::move(o); |
||||
} |
||||
|
||||
WindowsThread& WindowsThread::operator=(WindowsThread&& o) noexcept { |
||||
|
||||
if (joinable()) { |
||||
assert(false); |
||||
std::terminate(); |
||||
} |
||||
|
||||
data_ = std::move(o.data_); |
||||
|
||||
// Per spec both instances will have the same id
|
||||
th_id_ = o.th_id_; |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
bool WindowsThread::joinable() const { |
||||
return (data_ && data_->handle_ != 0); |
||||
} |
||||
|
||||
WindowsThread::native_handle_type WindowsThread::native_handle() const { |
||||
return reinterpret_cast<native_handle_type>(data_->handle_); |
||||
} |
||||
|
||||
unsigned WindowsThread::hardware_concurrency() { |
||||
return std::thread::hardware_concurrency(); |
||||
} |
||||
|
||||
void WindowsThread::join() { |
||||
|
||||
if (!joinable()) { |
||||
assert(false); |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::invalid_argument), |
||||
"Thread is no longer joinable"); |
||||
} |
||||
|
||||
if (GetThreadId(GetCurrentThread()) == th_id_) { |
||||
assert(false); |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::resource_deadlock_would_occur), |
||||
"Can not join itself"); |
||||
} |
||||
|
||||
auto ret = WaitForSingleObject(reinterpret_cast<HANDLE>(data_->handle_), |
||||
INFINITE); |
||||
if (ret != WAIT_OBJECT_0) { |
||||
auto lastError = GetLastError(); |
||||
assert(false); |
||||
throw std::system_error(static_cast<int>(lastError), |
||||
std::system_category(), |
||||
"WaitForSingleObjectFailed"); |
||||
} |
||||
|
||||
CloseHandle(reinterpret_cast<HANDLE>(data_->handle_)); |
||||
data_->handle_ = 0; |
||||
} |
||||
|
||||
bool WindowsThread::detach() { |
||||
|
||||
if (!joinable()) { |
||||
assert(false); |
||||
throw std::system_error( |
||||
std::make_error_code(std::errc::invalid_argument), |
||||
"Thread is no longer available"); |
||||
} |
||||
|
||||
BOOL ret = CloseHandle(reinterpret_cast<HANDLE>(data_->handle_)); |
||||
data_->handle_ = 0; |
||||
|
||||
return (ret == TRUE); |
||||
} |
||||
|
||||
void WindowsThread::swap(WindowsThread& o) { |
||||
data_.swap(o.data_); |
||||
std::swap(th_id_, o.th_id_); |
||||
} |
||||
|
||||
unsigned int __stdcall WindowsThread::Data::ThreadProc(void* arg) { |
||||
auto data = reinterpret_cast<WindowsThread::Data*>(arg); |
||||
data->func_(); |
||||
_endthreadex(0); |
||||
return 0; |
||||
} |
||||
} // namespace port
|
||||
} // namespace rocksdb
|
@ -0,0 +1,121 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
#include <functional> |
||||
#include <type_traits> |
||||
|
||||
namespace rocksdb { |
||||
namespace port { |
||||
|
||||
// This class is a replacement for std::thread
|
||||
// 2 reasons we do not like std::thread:
|
||||
// -- is that it dynamically allocates its internals that are automatically
|
||||
// freed when the thread terminates and not on the destruction of the
|
||||
// object. This makes it difficult to control the source of memory
|
||||
// allocation
|
||||
// - This implements Pimpl so we can easily replace the guts of the
|
||||
// object in our private version if necessary.
|
||||
class WindowsThread { |
||||
|
||||
struct Data; |
||||
|
||||
std::unique_ptr<Data> data_; |
||||
unsigned int th_id_; |
||||
|
||||
void Init(std::function<void()>&&); |
||||
|
||||
public: |
||||
|
||||
typedef void* native_handle_type; |
||||
|
||||
// Construct with no thread
|
||||
WindowsThread(); |
||||
|
||||
// Template constructor
|
||||
//
|
||||
// This templated constructor accomplishes several things
|
||||
//
|
||||
// - Allows the class as whole to be not a template
|
||||
//
|
||||
// - take "universal" references to support both _lvalues and _rvalues
|
||||
//
|
||||
// - because this constructor is a catchall case in many respects it
|
||||
// may prevent us from using both the default __ctor, the move __ctor.
|
||||
// Also it may circumvent copy __ctor deletion. To work around this
|
||||
// we make sure this one has at least one argument and eliminate
|
||||
// it from the overload selection when WindowsThread is the first
|
||||
// argument.
|
||||
//
|
||||
// - construct with Fx(Ax...) with a variable number of types/arguments.
|
||||
//
|
||||
// - Gathers together the callable object with its arguments and constructs
|
||||
// a single callable entity
|
||||
//
|
||||
// - Makes use of std::function to convert it to a specification-template
|
||||
// dependent type that both checks the signature conformance to ensure
|
||||
// that all of the necessary arguments are provided and allows pimpl
|
||||
// implementation.
|
||||
template<class Fn, |
||||
class... Args, |
||||
class = typename std::enable_if< |
||||
!std::is_same<typename std::decay<Fn>::type, |
||||
WindowsThread>::value>::type> |
||||
explicit WindowsThread(Fn&& fx, Args&&... ax) : |
||||
WindowsThread() { |
||||
|
||||
// Use binder to create a single callable entity
|
||||
auto binder = std::bind(std::forward<Fn>(fx), |
||||
std::forward<Args>(ax)...); |
||||
// Use std::function to take advantage of the type erasure
|
||||
// so we can still hide implementation within pimpl
|
||||
// This also makes sure that the binder signature is compliant
|
||||
std::function<void()> target = binder; |
||||
|
||||
Init(std::move(target)); |
||||
} |
||||
|
||||
|
||||
~WindowsThread(); |
||||
|
||||
WindowsThread(const WindowsThread&) = delete; |
||||
|
||||
WindowsThread& operator=(const WindowsThread&) = delete; |
||||
|
||||
WindowsThread(WindowsThread&&) noexcept; |
||||
|
||||
WindowsThread& operator=(WindowsThread&&) noexcept; |
||||
|
||||
bool joinable() const; |
||||
|
||||
unsigned int get_id() const { return th_id_; } |
||||
|
||||
native_handle_type native_handle() const; |
||||
|
||||
static unsigned hardware_concurrency(); |
||||
|
||||
void join(); |
||||
|
||||
bool detach(); |
||||
|
||||
void swap(WindowsThread&); |
||||
}; |
||||
} // namespace port
|
||||
} // namespace rocksdb
|
||||
|
||||
namespace std { |
||||
inline |
||||
void swap(rocksdb::port::WindowsThread& th1,
|
||||
rocksdb::port::WindowsThread& th2) { |
||||
th1.swap(th2); |
||||
} |
||||
} // namespace std
|
||||
|
Loading…
Reference in new issue