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