1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 // Windows implementation for gpr threads.
20 #include <grpc/support/port_platform.h>
21
22 #ifdef GPR_WINDOWS
23
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/sync.h>
26 #include <grpc/support/thd_id.h>
27 #include <grpc/support/time.h>
28 #include <string.h>
29
30 #include "absl/log/check.h"
31 #include "absl/log/log.h"
32 #include "src/core/util/crash.h"
33 #include "src/core/util/memory.h"
34 #include "src/core/util/thd.h"
35
36 namespace {
37 class ThreadInternalsWindows;
38 struct thd_info {
39 ThreadInternalsWindows* thread;
40 void (*body)(void* arg); // body of a thread
41 void* arg; // argument to a thread
42 HANDLE join_event; // the join event
43 bool joinable; // whether it is joinable
44 };
45
46 thread_local struct thd_info* g_thd_info;
47
48 class ThreadInternalsWindows
49 : public grpc_core::internal::ThreadInternalsInterface {
50 public:
ThreadInternalsWindows(void (* thd_body)(void * arg),void * arg,bool * success,const grpc_core::Thread::Options & options)51 ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success,
52 const grpc_core::Thread::Options& options)
53 : started_(false) {
54 gpr_mu_init(&mu_);
55 gpr_cv_init(&ready_);
56
57 HANDLE handle;
58 info_ = (struct thd_info*)gpr_malloc(sizeof(*info_));
59 info_->thread = this;
60 info_->body = thd_body;
61 info_->arg = arg;
62 info_->join_event = nullptr;
63 info_->joinable = options.joinable();
64 if (info_->joinable) {
65 info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
66 if (info_->join_event == nullptr) {
67 gpr_free(info_);
68 *success = false;
69 return;
70 }
71 }
72
73 if (options.stack_size() != 0) {
74 // Windows will round up the given stack_size value to nearest page.
75 handle = CreateThread(nullptr, options.stack_size(), thread_body, info_,
76 0, nullptr);
77 } else {
78 handle = CreateThread(nullptr, 64 * 1024, thread_body, info_, 0, nullptr);
79 }
80
81 if (handle == nullptr) {
82 destroy_thread();
83 *success = false;
84 } else {
85 CloseHandle(handle);
86 *success = true;
87 }
88 }
89
~ThreadInternalsWindows()90 ~ThreadInternalsWindows() override {
91 gpr_mu_destroy(&mu_);
92 gpr_cv_destroy(&ready_);
93 }
94
Start()95 void Start() override {
96 gpr_mu_lock(&mu_);
97 started_ = true;
98 gpr_cv_signal(&ready_);
99 gpr_mu_unlock(&mu_);
100 }
101
Join()102 void Join() override {
103 DWORD ret = WaitForSingleObject(info_->join_event, INFINITE);
104 CHECK(ret == WAIT_OBJECT_0);
105 destroy_thread();
106 }
107
108 private:
thread_body(void * v)109 static DWORD WINAPI thread_body(void* v) {
110 g_thd_info = static_cast<thd_info*>(v);
111 gpr_mu_lock(&g_thd_info->thread->mu_);
112 while (!g_thd_info->thread->started_) {
113 gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_,
114 gpr_inf_future(GPR_CLOCK_MONOTONIC));
115 }
116 gpr_mu_unlock(&g_thd_info->thread->mu_);
117 if (!g_thd_info->joinable) {
118 delete g_thd_info->thread;
119 g_thd_info->thread = nullptr;
120 }
121 g_thd_info->body(g_thd_info->arg);
122 if (g_thd_info->joinable) {
123 BOOL ret = SetEvent(g_thd_info->join_event);
124 CHECK(ret);
125 } else {
126 gpr_free(g_thd_info);
127 }
128 return 0;
129 }
130
destroy_thread()131 void destroy_thread() {
132 if (info_ != nullptr && info_->joinable) {
133 CloseHandle(info_->join_event);
134 }
135 gpr_free(info_);
136 }
137
138 gpr_mu mu_;
139 gpr_cv ready_;
140 bool started_;
141 thd_info* info_;
142 };
143
144 } // namespace
145
146 namespace grpc_core {
147
Signal(gpr_thd_id,int)148 void Thread::Signal(gpr_thd_id /* tid */, int /* sig */) {
149 // TODO(hork): Implement
150 VLOG(2) << "Thread signals are not supported on Windows.";
151 }
152
Kill(gpr_thd_id)153 void Thread::Kill(gpr_thd_id /* tid */) {
154 // TODO(hork): Implement
155 VLOG(2) << "Thread::Kill is not supported on Windows.";
156 }
157
Thread(const char *,void (* thd_body)(void * arg),void * arg,bool * success,const Options & options)158 Thread::Thread(const char* /* thd_name */, void (*thd_body)(void* arg),
159 void* arg, bool* success, const Options& options)
160 : options_(options) {
161 bool outcome = false;
162 impl_ = new ThreadInternalsWindows(thd_body, arg, &outcome, options);
163 if (outcome) {
164 state_ = ALIVE;
165 } else {
166 state_ = FAILED;
167 delete impl_;
168 impl_ = nullptr;
169 }
170
171 if (success != nullptr) {
172 *success = outcome;
173 }
174 }
175
176 } // namespace grpc_core
177
gpr_thd_currentid(void)178 gpr_thd_id gpr_thd_currentid(void) {
179 return reinterpret_cast<gpr_thd_id>(g_thd_info);
180 }
181
182 #endif // GPR_WINDOWS
183