• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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