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
21 #include <grpc/support/port_platform.h>
22
23 #ifdef GPR_WINDOWS
24
25 #include "src/core/lib/gprpp/thd.h"
26
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/log.h>
29 #include <grpc/support/thd_id.h>
30 #include <string.h>
31
32 #include "src/core/lib/gprpp/memory.h"
33
34 #if defined(_MSC_VER)
35 #define thread_local __declspec(thread)
36 #elif defined(__GNUC__)
37 #define thread_local __thread
38 #else
39 #error "Unknown compiler - please file a bug report"
40 #endif
41
42 namespace {
43 class ThreadInternalsWindows;
44 struct thd_info {
45 ThreadInternalsWindows* thread;
46 void (*body)(void* arg); /* body of a thread */
47 void* arg; /* argument to a thread */
48 HANDLE join_event; /* the join event */
49 bool joinable; /* whether it is joinable */
50 };
51
52 thread_local struct thd_info* g_thd_info;
53
54 class ThreadInternalsWindows
55 : public grpc_core::internal::ThreadInternalsInterface {
56 public:
ThreadInternalsWindows(void (* thd_body)(void * arg),void * arg,bool * success,const grpc_core::Thread::Options & options)57 ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success,
58 const grpc_core::Thread::Options& options)
59 : started_(false) {
60 gpr_mu_init(&mu_);
61 gpr_cv_init(&ready_);
62
63 HANDLE handle;
64 info_ = (struct thd_info*)gpr_malloc(sizeof(*info_));
65 info_->thread = this;
66 info_->body = thd_body;
67 info_->arg = arg;
68 info_->join_event = nullptr;
69 info_->joinable = options.joinable();
70 if (info_->joinable) {
71 info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
72 if (info_->join_event == nullptr) {
73 gpr_free(info_);
74 *success = false;
75 return;
76 }
77 }
78
79 if (options.stack_size() != 0) {
80 // Windows will round up the given stack_size value to nearest page.
81 handle = CreateThread(nullptr, options.stack_size(), thread_body, info_,
82 0, nullptr);
83 } else {
84 handle = CreateThread(nullptr, 64 * 1024, thread_body, info_, 0, nullptr);
85 }
86
87 if (handle == nullptr) {
88 destroy_thread();
89 *success = false;
90 } else {
91 CloseHandle(handle);
92 *success = true;
93 }
94 }
95
~ThreadInternalsWindows()96 ~ThreadInternalsWindows() override {
97 gpr_mu_destroy(&mu_);
98 gpr_cv_destroy(&ready_);
99 }
100
Start()101 void Start() override {
102 gpr_mu_lock(&mu_);
103 started_ = true;
104 gpr_cv_signal(&ready_);
105 gpr_mu_unlock(&mu_);
106 }
107
Join()108 void Join() override {
109 DWORD ret = WaitForSingleObject(info_->join_event, INFINITE);
110 GPR_ASSERT(ret == WAIT_OBJECT_0);
111 destroy_thread();
112 }
113
114 private:
thread_body(void * v)115 static DWORD WINAPI thread_body(void* v) {
116 g_thd_info = static_cast<thd_info*>(v);
117 gpr_mu_lock(&g_thd_info->thread->mu_);
118 while (!g_thd_info->thread->started_) {
119 gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_,
120 gpr_inf_future(GPR_CLOCK_MONOTONIC));
121 }
122 gpr_mu_unlock(&g_thd_info->thread->mu_);
123 if (!g_thd_info->joinable) {
124 delete g_thd_info->thread;
125 g_thd_info->thread = nullptr;
126 }
127 g_thd_info->body(g_thd_info->arg);
128 if (g_thd_info->joinable) {
129 BOOL ret = SetEvent(g_thd_info->join_event);
130 GPR_ASSERT(ret);
131 } else {
132 gpr_free(g_thd_info);
133 }
134 return 0;
135 }
136
destroy_thread()137 void destroy_thread() {
138 if (info_ != nullptr && info_->joinable) {
139 CloseHandle(info_->join_event);
140 }
141 gpr_free(info_);
142 }
143
144 gpr_mu mu_;
145 gpr_cv ready_;
146 bool started_;
147 thd_info* info_;
148 };
149
150 } // namespace
151
152 namespace grpc_core {
153
Thread(const char * thd_name,void (* thd_body)(void * arg),void * arg,bool * success,const Options & options)154 Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg,
155 bool* success, const Options& options)
156 : options_(options) {
157 bool outcome = false;
158 impl_ = new ThreadInternalsWindows(thd_body, arg, &outcome, options);
159 if (outcome) {
160 state_ = ALIVE;
161 } else {
162 state_ = FAILED;
163 delete impl_;
164 impl_ = nullptr;
165 }
166
167 if (success != nullptr) {
168 *success = outcome;
169 }
170 }
171
172 } // namespace grpc_core
173
gpr_thd_currentid(void)174 gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; }
175
176 #endif /* GPR_WINDOWS */
177