xref: /5.5.2/platform/src/cb_win32.cc (revision e8521d73)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2015 Couchbase, Inc.
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#include "config.h"
18
19#include <assert.h>
20#include <fcntl.h>
21#include <io.h>
22#include <phosphor/phosphor.h>
23#include <platform/cb_malloc.h>
24#include <platform/getopt.h>
25#include <platform/platform.h>
26#include <platform/strerror.h>
27#include <win32/getopt.h>
28#include <chrono>
29#include <cstdio>
30#include <thread>
31#include <vector>
32
33struct thread_execute {
34    cb_thread_main_func func;
35    void *argument;
36    // Windows doesn't support naming threads, but phosphor does
37    std::string thread_name;
38};
39
40static DWORD WINAPI platform_thread_wrap(LPVOID arg)
41{
42    auto *ctx = reinterpret_cast<struct thread_execute*>(arg);
43    assert(ctx);
44    PHOSPHOR_INSTANCE.registerThread(ctx->thread_name);
45    ctx->func(ctx->argument);
46    PHOSPHOR_INSTANCE.deregisterThread();
47    delete ctx;
48    return 0;
49}
50
51PLATFORM_PUBLIC_API
52int cb_create_thread(cb_thread_t *id,
53                     void (*func)(void *arg),
54                     void *arg,
55                     int detached)
56{
57    // Implemented in terms of cb_create_named_thread; without a name.
58    return cb_create_named_thread(id, func, arg, detached, NULL);
59}
60
61PLATFORM_PUBLIC_API int cb_create_named_thread(cb_thread_t* id,
62                                               void (*func)(void* arg),
63                                               void* arg,
64                                               int detached,
65                                               const char* name) {
66    HANDLE handle;
67
68    struct thread_execute *ctx;
69    try {
70        ctx = new struct thread_execute;
71    } catch (std::bad_alloc) {
72        return -1;
73    }
74
75    ctx->func = func;
76    ctx->argument = arg;
77    ctx->thread_name = (name == nullptr) ? "" : name;
78
79    handle = CreateThread(NULL, 0, platform_thread_wrap, ctx, 0, id);
80    if (handle == NULL) {
81        delete ctx;
82        return -1;
83    } else {
84        if (detached) {
85            CloseHandle(handle);
86        }
87    }
88
89    return 0;
90}
91
92PLATFORM_PUBLIC_API
93int cb_join_thread(cb_thread_t id)
94{
95    // We've seen problems where we've had global std::unique_ptr's which
96    // had to run destructors which waited for threads be run on a "random"
97    // thread causing a deadlock (the actual use was in memcached with the
98    // parent monitor thread). The C++ runtime just picked a random thread
99    // to run these destructors, and sometimes the parent monitor thread
100    // would run them. We've refactored the code in memcached so that object
101    // is no longer global, but to avoid facing the problem at a later time
102    // we should add a guard here. (and it doesn't make any sense from
103    // a logical perspective to wait for the current thread to be done ;-)
104    if (cb_thread_self() == id) {
105        throw std::runtime_error("cb_join_thread: can't try to join self");
106    }
107
108    HANDLE handle = OpenThread(SYNCHRONIZE, FALSE, id);
109    if (handle == NULL) {
110        return -1;
111    }
112    WaitForSingleObject(handle, INFINITE);
113    CloseHandle(handle);
114    return 0;
115}
116
117PLATFORM_PUBLIC_API
118cb_thread_t cb_thread_self(void)
119{
120    return GetCurrentThreadId();
121}
122
123PLATFORM_PUBLIC_API
124int cb_thread_equal(const cb_thread_t a, const cb_thread_t b)
125{
126    return a == b;
127}
128
129PLATFORM_PUBLIC_API
130int cb_set_thread_name(const char*)
131{
132    // Not implemented on WIN32
133    return -1;
134}
135
136PLATFORM_PUBLIC_API
137int cb_get_thread_name(char*, size_t)
138{
139    return -1;
140}
141
142PLATFORM_PUBLIC_API
143bool is_thread_name_supported(void)
144{
145    return false;
146}
147
148PLATFORM_PUBLIC_API
149void cb_mutex_initialize(cb_mutex_t *mutex)
150{
151    InitializeCriticalSection(mutex);
152}
153
154PLATFORM_PUBLIC_API
155void cb_mutex_destroy(cb_mutex_t *mutex)
156{
157    DeleteCriticalSection(mutex);
158}
159
160PLATFORM_PUBLIC_API
161void cb_mutex_enter(cb_mutex_t *mutex)
162{
163    EnterCriticalSection(mutex);
164}
165
166PLATFORM_PUBLIC_API
167int cb_mutex_try_enter(cb_mutex_t *mutex)
168{
169    return TryEnterCriticalSection(mutex) ? 0 : -1;
170}
171
172PLATFORM_PUBLIC_API
173void cb_mutex_exit(cb_mutex_t *mutex)
174{
175    LeaveCriticalSection(mutex);
176}
177
178PLATFORM_PUBLIC_API
179void cb_cond_initialize(cb_cond_t *cond)
180{
181    InitializeConditionVariable(cond);
182}
183
184PLATFORM_PUBLIC_API
185void cb_cond_destroy(cb_cond_t *cond)
186{
187    (void)cond;
188}
189
190PLATFORM_PUBLIC_API
191void cb_cond_wait(cb_cond_t *cond, cb_mutex_t *mutex)
192{
193    SleepConditionVariableCS(cond, mutex, INFINITE);
194}
195
196PLATFORM_PUBLIC_API
197void cb_cond_timedwait(cb_cond_t *cond, cb_mutex_t *mutex, unsigned int msec) {
198    SleepConditionVariableCS(cond, mutex, msec);
199}
200
201PLATFORM_PUBLIC_API
202void cb_cond_signal(cb_cond_t *cond)
203{
204    WakeConditionVariable(cond);
205}
206
207PLATFORM_PUBLIC_API
208void cb_cond_broadcast(cb_cond_t *cond)
209{
210    WakeAllConditionVariable(cond);
211}
212
213static const char *get_dll_name(const char *path, char *buffer)
214{
215    if (strstr(path, ".dll") != nullptr) {
216        return path;
217    }
218
219    strcpy(buffer, path);
220    char *ptr = strstr(buffer, ".so");
221    if (ptr != NULL) {
222        sprintf(ptr, ".dll");
223        return buffer;
224    }
225
226    strcat(buffer, ".dll");
227    return buffer;
228}
229
230PLATFORM_PUBLIC_API
231cb_dlhandle_t cb_dlopen(const char *library, char **errmsg)
232{
233    cb_dlhandle_t handle;
234
235    if (library == NULL) {
236        if (errmsg != NULL) {
237            *errmsg = cb_strdup("Open self is not supported");
238        }
239        return NULL;
240    }
241
242    std::vector<char> buffer(strlen(library) + 20, 0);
243
244    handle = LoadLibrary(get_dll_name(library, buffer.data()));
245    if (handle == NULL && errmsg != NULL) {
246        std::string reason = cb_strerror();
247        *errmsg = cb_strdup(reason.c_str());
248    }
249
250    return handle;
251}
252
253PLATFORM_PUBLIC_API
254void *cb_dlsym(cb_dlhandle_t handle, const char *symbol, char **errmsg)
255{
256    void *ret = GetProcAddress(reinterpret_cast<HMODULE>(handle), symbol);
257    if (ret == NULL && errmsg) {
258        std::string reason = cb_strerror();
259        *errmsg = cb_strdup(reason.c_str());
260    }
261
262    return ret;
263}
264
265PLATFORM_PUBLIC_API
266void cb_dlclose(cb_dlhandle_t handle)
267{
268    FreeLibrary(reinterpret_cast<HMODULE>(handle));
269}
270
271PLATFORM_PUBLIC_API
272void usleep(unsigned int useconds)
273{
274    std::this_thread::sleep_for(std::chrono::microseconds(useconds));
275}
276
277PLATFORM_PUBLIC_API
278int gettimeofday(struct timeval *tv, void *tz)
279{
280    FILETIME ft;
281    uint64_t usecs;
282    LARGE_INTEGER li;
283    uint64_t secs;
284
285    assert(tz == NULL); /* I don't support that right now */
286    assert(tv != NULL); /* I don't support that right now */
287
288    GetSystemTimeAsFileTime(&ft);
289    li.LowPart = ft.dwLowDateTime;
290    li.HighPart = ft.dwHighDateTime;
291    usecs = li.QuadPart;
292
293    // FILETIME is 100 nanosecs from from 1. jan 1601
294    // convert it to usecs..
295    usecs /= 10;
296
297    secs = usecs / 1000000;
298    tv->tv_usec = usecs % 1000000;
299
300    // gettimeofday use 1st january 1970, subtract the secs
301    // between the dates
302    secs -= 11644473600;
303    tv->tv_sec = (unsigned long)secs;
304
305    return 0;
306}
307
308PLATFORM_PUBLIC_API
309int platform_set_binary_mode(FILE *fp)
310{
311    return _setmode(_fileno(fp), _O_BINARY);
312}
313
314PLATFORM_PUBLIC_API
315void cb_rw_lock_initialize(cb_rwlock_t *rw)
316{
317    InitializeSRWLock(rw);
318}
319
320PLATFORM_PUBLIC_API
321void cb_rw_lock_destroy(cb_rwlock_t *rw)
322{
323    (void)rw;
324    // Nothing todo on windows
325}
326
327PLATFORM_PUBLIC_API
328int cb_rw_reader_enter(cb_rwlock_t *rw)
329{
330    AcquireSRWLockShared(rw);
331    return 0;
332}
333
334PLATFORM_PUBLIC_API
335int cb_rw_reader_exit(cb_rwlock_t *rw)
336{
337    ReleaseSRWLockShared(rw);
338    return 0;
339}
340
341PLATFORM_PUBLIC_API
342int cb_rw_writer_enter(cb_rwlock_t *rw)
343{
344    AcquireSRWLockExclusive(rw);
345    return 0;
346}
347
348PLATFORM_PUBLIC_API
349int cb_rw_writer_exit(cb_rwlock_t *rw)
350{
351    ReleaseSRWLockExclusive(rw);
352    return 0;
353}
354
355// Wrapper into cb::getopt (which we now unit tests on all platforms)
356PLATFORM_PUBLIC_API
357char* optarg;
358PLATFORM_PUBLIC_API
359int opterr;
360PLATFORM_PUBLIC_API
361int optind = 1;
362PLATFORM_PUBLIC_API
363int optopt;
364
365PLATFORM_PUBLIC_API
366int getopt_long(int argc,
367                char** argv,
368                const char* optstring,
369                const struct option* longopts,
370                int* longindex) {
371    auto ret = cb::getopt::getopt_long(
372            argc,
373            argv,
374            optstring,
375            reinterpret_cast<const cb::getopt::option*>(longopts),
376            longindex);
377    optarg = cb::getopt::optarg;
378    opterr = cb::getopt::opterr;
379    optind = cb::getopt::optind;
380    optopt = cb::getopt::optopt;
381    return ret;
382}
383
384PLATFORM_PUBLIC_API
385int getopt(int argc, char** argv, const char* optstring) {
386    auto ret = cb::getopt::getopt(argc, argv, optstring);
387    optarg = cb::getopt::optarg;
388    opterr = cb::getopt::opterr;
389    optind = cb::getopt::optind;
390    optopt = cb::getopt::optopt;
391    return ret;
392}
393