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