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