| // 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 |