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