1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10 #include "port/win/win_thread.h"
11
12 #include <assert.h>
13 #include <process.h> // __beginthreadex
14 #include <windows.h>
15
16 #include <stdexcept>
17 #include <system_error>
18 #include <thread>
19
20 namespace ROCKSDB_NAMESPACE {
21 namespace port {
22
23 struct WindowsThread::Data {
24
25 std::function<void()> func_;
26 uintptr_t handle_;
27
DataROCKSDB_NAMESPACE::port::WindowsThread::Data28 Data(std::function<void()>&& func) :
29 func_(std::move(func)),
30 handle_(0) {
31 }
32
33 Data(const Data&) = delete;
34 Data& operator=(const Data&) = delete;
35
36 static unsigned int __stdcall ThreadProc(void* arg);
37 };
38
39
Init(std::function<void ()> && func)40 void WindowsThread::Init(std::function<void()>&& func) {
41
42 data_ = std::make_shared<Data>(std::move(func));
43 // We create another instance of std::shared_ptr to get an additional ref
44 // since we may detach and destroy this instance before the threadproc
45 // may start to run. We choose to allocate this additional ref on the heap
46 // so we do not need to synchronize and allow this thread to proceed
47 std::unique_ptr<std::shared_ptr<Data>> th_data(new std::shared_ptr<Data>(data_));
48
49 data_->handle_ = _beginthreadex(NULL,
50 0, // stack size
51 &Data::ThreadProc,
52 th_data.get(),
53 0, // init flag
54 &th_id_);
55
56 if (data_->handle_ == 0) {
57 throw std::system_error(std::make_error_code(
58 std::errc::resource_unavailable_try_again),
59 "Unable to create a thread");
60 }
61 th_data.release();
62 }
63
WindowsThread()64 WindowsThread::WindowsThread() :
65 data_(nullptr),
66 th_id_(0)
67 {}
68
69
~WindowsThread()70 WindowsThread::~WindowsThread() {
71 // Must be joined or detached
72 // before destruction.
73 // This is the same as std::thread
74 if (data_) {
75 if (joinable()) {
76 assert(false);
77 std::terminate();
78 }
79 data_.reset();
80 }
81 }
82
WindowsThread(WindowsThread && o)83 WindowsThread::WindowsThread(WindowsThread&& o) noexcept :
84 WindowsThread() {
85 *this = std::move(o);
86 }
87
operator =(WindowsThread && o)88 WindowsThread& WindowsThread::operator=(WindowsThread&& o) noexcept {
89
90 if (joinable()) {
91 assert(false);
92 std::terminate();
93 }
94
95 data_ = std::move(o.data_);
96
97 // Per spec both instances will have the same id
98 th_id_ = o.th_id_;
99
100 return *this;
101 }
102
joinable() const103 bool WindowsThread::joinable() const {
104 return (data_ && data_->handle_ != 0);
105 }
106
native_handle() const107 WindowsThread::native_handle_type WindowsThread::native_handle() const {
108 return reinterpret_cast<native_handle_type>(data_->handle_);
109 }
110
hardware_concurrency()111 unsigned WindowsThread::hardware_concurrency() {
112 return std::thread::hardware_concurrency();
113 }
114
join()115 void WindowsThread::join() {
116
117 if (!joinable()) {
118 assert(false);
119 throw std::system_error(
120 std::make_error_code(std::errc::invalid_argument),
121 "Thread is no longer joinable");
122 }
123
124 if (GetThreadId(GetCurrentThread()) == th_id_) {
125 assert(false);
126 throw std::system_error(
127 std::make_error_code(std::errc::resource_deadlock_would_occur),
128 "Can not join itself");
129 }
130
131 auto ret = WaitForSingleObject(reinterpret_cast<HANDLE>(data_->handle_),
132 INFINITE);
133 if (ret != WAIT_OBJECT_0) {
134 auto lastError = GetLastError();
135 assert(false);
136 throw std::system_error(static_cast<int>(lastError),
137 std::system_category(),
138 "WaitForSingleObjectFailed: thread join");
139 }
140
141 BOOL rc
142 #if defined(_MSC_VER)
143 = FALSE;
144 #else
145 __attribute__((__unused__));
146 #endif
147 rc = CloseHandle(reinterpret_cast<HANDLE>(data_->handle_));
148 assert(rc != 0);
149 data_->handle_ = 0;
150 }
151
detach()152 bool WindowsThread::detach() {
153
154 if (!joinable()) {
155 assert(false);
156 throw std::system_error(
157 std::make_error_code(std::errc::invalid_argument),
158 "Thread is no longer available");
159 }
160
161 BOOL ret = CloseHandle(reinterpret_cast<HANDLE>(data_->handle_));
162 data_->handle_ = 0;
163
164 return (ret != 0);
165 }
166
swap(WindowsThread & o)167 void WindowsThread::swap(WindowsThread& o) {
168 data_.swap(o.data_);
169 std::swap(th_id_, o.th_id_);
170 }
171
ThreadProc(void * arg)172 unsigned int __stdcall WindowsThread::Data::ThreadProc(void* arg) {
173 auto ptr = reinterpret_cast<std::shared_ptr<Data>*>(arg);
174 std::unique_ptr<std::shared_ptr<Data>> data(ptr);
175 (*data)->func_();
176 return 0;
177 }
178 } // namespace port
179 } // namespace ROCKSDB_NAMESPACE
180