Text file
src/runtime/cgo/gcc_libinit_windows.c
1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 #ifdef __CYGWIN__
6 #error "don't use the cygwin compiler to build native Windows programs; use MinGW instead"
7 #endif
8
9 #define WIN32_LEAN_AND_MEAN
10 #include <windows.h>
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 #include "libcgo.h"
16 #include "libcgo_windows.h"
17
18 // Ensure there's one symbol marked __declspec(dllexport).
19 // If there are no exported symbols, the unfortunate behavior of
20 // the binutils linker is to also strip the relocations table,
21 // resulting in non-PIE binary. The other option is the
22 // --export-all-symbols flag, but we don't need to export all symbols
23 // and this may overflow the export table (#40795).
24 // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
25 __declspec(dllexport) int _cgo_dummy_export;
26
27 static volatile LONG runtime_init_once_gate = 0;
28 static volatile LONG runtime_init_once_done = 0;
29
30 static CRITICAL_SECTION runtime_init_cs;
31
32 static HANDLE runtime_init_wait;
33 static int runtime_init_done;
34
35 uintptr_t x_cgo_pthread_key_created;
36 void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t);
37
38 // Pre-initialize the runtime synchronization objects
39 void
40 _cgo_preinit_init() {
41 runtime_init_wait = CreateEvent(NULL, TRUE, FALSE, NULL);
42 if (runtime_init_wait == NULL) {
43 fprintf(stderr, "runtime: failed to create runtime initialization wait event.\n");
44 abort();
45 }
46
47 InitializeCriticalSection(&runtime_init_cs);
48 }
49
50 // Make sure that the preinit sequence has run.
51 void
52 _cgo_maybe_run_preinit() {
53 if (!InterlockedExchangeAdd(&runtime_init_once_done, 0)) {
54 if (InterlockedIncrement(&runtime_init_once_gate) == 1) {
55 _cgo_preinit_init();
56 InterlockedIncrement(&runtime_init_once_done);
57 } else {
58 // Decrement to avoid overflow.
59 InterlockedDecrement(&runtime_init_once_gate);
60 while(!InterlockedExchangeAdd(&runtime_init_once_done, 0)) {
61 Sleep(0);
62 }
63 }
64 }
65 }
66
67 void
68 x_cgo_sys_thread_create(unsigned long (__stdcall *func)(void*), void* arg) {
69 _cgo_beginthread(func, arg);
70 }
71
72 int
73 _cgo_is_runtime_initialized() {
74 int status;
75
76 EnterCriticalSection(&runtime_init_cs);
77 status = runtime_init_done;
78 LeaveCriticalSection(&runtime_init_cs);
79 return status;
80 }
81
82 uintptr_t
83 _cgo_wait_runtime_init_done(void) {
84 void (*pfn)(struct context_arg*);
85
86 _cgo_maybe_run_preinit();
87 while (!_cgo_is_runtime_initialized()) {
88 WaitForSingleObject(runtime_init_wait, INFINITE);
89 }
90 pfn = _cgo_get_context_function();
91 if (pfn != nil) {
92 struct context_arg arg;
93
94 arg.Context = 0;
95 (*pfn)(&arg);
96 return arg.Context;
97 }
98 return 0;
99 }
100
101 // Should not be used since x_cgo_pthread_key_created will always be zero.
102 void x_cgo_bindm(void* dummy) {
103 fprintf(stderr, "unexpected cgo_bindm on Windows\n");
104 abort();
105 }
106
107 void
108 x_cgo_notify_runtime_init_done(void* dummy) {
109 _cgo_maybe_run_preinit();
110
111 EnterCriticalSection(&runtime_init_cs);
112 runtime_init_done = 1;
113 LeaveCriticalSection(&runtime_init_cs);
114
115 if (!SetEvent(runtime_init_wait)) {
116 fprintf(stderr, "runtime: failed to signal runtime initialization complete.\n");
117 abort();
118 }
119 }
120
121 // The context function, used when tracing back C calls into Go.
122 static void (*cgo_context_function)(struct context_arg*);
123
124 // Sets the context function to call to record the traceback context
125 // when calling a Go function from C code. Called from runtime.SetCgoTraceback.
126 void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
127 EnterCriticalSection(&runtime_init_cs);
128 cgo_context_function = context;
129 LeaveCriticalSection(&runtime_init_cs);
130 }
131
132 // Gets the context function.
133 void (*(_cgo_get_context_function(void)))(struct context_arg*) {
134 void (*ret)(struct context_arg*);
135
136 EnterCriticalSection(&runtime_init_cs);
137 ret = cgo_context_function;
138 LeaveCriticalSection(&runtime_init_cs);
139 return ret;
140 }
141
142 void _cgo_beginthread(unsigned long (__stdcall *func)(void*), void* arg) {
143 int tries;
144 HANDLE thandle;
145
146 for (tries = 0; tries < 20; tries++) {
147 thandle = CreateThread(NULL, 0, func, arg, 0, NULL);
148 if (thandle == 0 && GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
149 // "Insufficient resources", try again in a bit.
150 //
151 // Note that the first Sleep(0) is a yield.
152 Sleep(tries); // milliseconds
153 continue;
154 } else if (thandle == 0) {
155 break;
156 }
157 CloseHandle(thandle);
158 return; // Success!
159 }
160
161 fprintf(stderr, "runtime: failed to create new OS thread (%lu)\n", GetLastError());
162 abort();
163 }
164
View as plain text