blob: e55ca7450b1530a21007390bbccc2254f6c42bc9 [file] [log] [blame]
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root 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