Skip to content

Commit e942880

Browse files
author
Brian J. Cardiff
authored
Update bdwgc 8.0.4 + mt patch (#47)
* Upgrade bdw-gc v8.0.4 * Include upstream patch for thread stackbottom
1 parent 9070531 commit e942880

File tree

7 files changed

+685
-165
lines changed

7 files changed

+685
-165
lines changed

linux/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ ENV CFLAGS="-fPIC -pipe ${release:+-O2}"
1313
# Build libgc
1414
ARG gc_version
1515
ARG libatomic_ops_version
16-
COPY files/feature-thread-stackbottom.patch /tmp/
16+
COPY files/feature-thread-stackbottom-upstream.patch /tmp/
1717
RUN git clone https://github.com/ivmai/bdwgc \
1818
&& cd bdwgc \
1919
&& git checkout ${gc_version} \
2020
&& git clone https://github.com/ivmai/libatomic_ops \
2121
&& (cd libatomic_ops && git checkout ${libatomic_ops_version}) \
2222
\
23-
&& patch -p1 < /tmp/feature-thread-stackbottom.patch \
23+
&& patch -p1 < /tmp/feature-thread-stackbottom-upstream.patch \
2424
\
2525
&& ./autogen.sh \
2626
&& ./configure --disable-debug --disable-shared --enable-large-config \

linux/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ PREVIOUS_CRYSTAL_RELEASE_LINUX64_TARGZ ?= ## url to crystal-{version}-{package}
2020
PREVIOUS_CRYSTAL_RELEASE_LINUX32_TARGZ ?= ## url to crystal-{version}-{package}-linux-i686.tar.gz
2121

2222
SHARDS_VERSION = v0.8.1
23-
GC_VERSION = v7.6.12
23+
GC_VERSION = v8.0.4
2424
LIBATOMIC_OPS_VERSION = v7.6.10
2525

2626
OUTPUT_DIR = build
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
From 5668de71107022a316ee967162bc16c10754b9ce Mon Sep 17 00:00:00 2001
2+
From: "Brian J. Cardiff" <[email protected]>
3+
Date: Mon, 6 May 2019 12:06:12 +0300
4+
Subject: [PATCH] Add API functions to get and set the stack bottom of each
5+
thread
6+
7+
Issue #277 (bdwgc).
8+
9+
This API is useful to support coroutines.
10+
11+
* include/gc.h (GC_get_my_stackbottom, GC_set_stackbottom): New API
12+
function declaration.
13+
* misc.c [!THREADS] (GC_set_stackbottom, GC_get_my_stackbottom): New
14+
function definition.
15+
* pthread_support.c [GC_PTHREADS && !GC_WIN32_THREADS]
16+
(GC_set_stackbottom, GC_get_my_stackbottom): Likewise.
17+
* win32_threads.c [GC_WIN32_THREADS] (GC_set_stackbottom,
18+
GC_get_my_stackbottom): Likewise.
19+
* tests/test.c (struct thr_hndl_sb_s): Define.
20+
* tests/test.c (set_stackbottom): New function (which calls
21+
GC_set_stackbottom).
22+
* tests/test.c (run_one_test): Define thr_hndl_sb local variable;
23+
call GC_get_my_stackbottom() and set_stackbottom().
24+
* win32_threads.c [GC_WIN32_THREADS && I386] (struct GC_Thread_Rep):
25+
Add initial_stack_base field.
26+
* win32_threads.c [GC_WIN32_THREADS && I386] (GC_record_stack_base,
27+
GC_call_with_gc_active): Set initial_stack_base field.
28+
* win32_threads.c [GC_WIN32_THREADS && I386] (GC_push_stack_for): Handle
29+
the case when GetThreadContext() might return stale register values,
30+
thread stack_base != initial_stack_base but the stack is not inside
31+
the TIB stack (use context.Esp but call WARN); add TODO.
32+
---
33+
include/gc.h | 24 +++++++++++++++
34+
misc.c | 24 +++++++++++++++
35+
pthread_support.c | 56 +++++++++++++++++++++++++++++++++++
36+
tests/test.c | 16 ++++++++++
37+
win32_threads.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++-
38+
5 files changed, 193 insertions(+), 1 deletion(-)
39+
40+
diff --git a/include/gc.h b/include/gc.h
41+
index d318cf3f..a7164c34 100644
42+
--- a/include/gc.h
43+
+++ b/include/gc.h
44+
@@ -1578,6 +1578,30 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */,
45+
GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *)
46+
GC_ATTR_NONNULL(1);
47+
48+
+/* Fill in the GC_stack_base structure with the cold end (bottom) of */
49+
+/* the stack of the current thread (or coroutine). */
50+
+/* Unlike GC_get_stack_base, it retrieves the value stored in the */
51+
+/* collector (which is initially set by the collector upon the thread */
52+
+/* is started or registered manually but it could be later updated by */
53+
+/* client using GC_set_stackbottom). Returns the GC-internal non-NULL */
54+
+/* handle of the thread which could be passed to GC_set_stackbottom */
55+
+/* later. It is assumed that the collector is already initialized and */
56+
+/* the thread is registered. Acquires the GC lock to avoid data races. */
57+
+GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *)
58+
+ GC_ATTR_NONNULL(1);
59+
+
60+
+/* Set the cool end of the user (coroutine) stack of the specified */
61+
+/* thread. The GC thread handle is either the one returned by */
62+
+/* GC_get_my_stackbottom or NULL (the latter designates the current */
63+
+/* thread). The caller should hold the GC lock (e.g. using */
64+
+/* GC_call_with_alloc_lock). Also, the function could be used for */
65+
+/* setting GC_stackbottom value (the bottom of the primordial thread) */
66+
+/* before the collector is initialized (the GC lock is not needed to be */
67+
+/* acquired in this case). */
68+
+GC_API void GC_CALL GC_set_stackbottom(void * /* gc_thread_handle */,
69+
+ const struct GC_stack_base *)
70+
+ GC_ATTR_NONNULL(2);
71+
+
72+
/* The following routines are primarily intended for use with a */
73+
/* preprocessor which inserts calls to check C pointer arithmetic. */
74+
/* They indicate failure by invoking the corresponding _print_proc. */
75+
diff --git a/misc.c b/misc.c
76+
index 5b473569..322422fb 100644
77+
--- a/misc.c
78+
+++ b/misc.c
79+
@@ -2200,6 +2200,30 @@ STATIC void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED)
80+
GC_blocked_sp = NULL;
81+
}
82+
83+
+ GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle,
84+
+ const struct GC_stack_base *sb)
85+
+ {
86+
+ GC_ASSERT(sb -> mem_base != NULL);
87+
+ GC_ASSERT(NULL == gc_thread_handle || &GC_stackbottom == gc_thread_handle);
88+
+ GC_ASSERT(NULL == GC_blocked_sp
89+
+ && NULL == GC_traced_stack_sect); /* for now */
90+
+ (void)gc_thread_handle;
91+
+
92+
+ GC_stackbottom = (char *)sb->mem_base;
93+
+# ifdef IA64
94+
+ GC_register_stackbottom = (ptr_t)sb->reg_base;
95+
+# endif
96+
+ }
97+
+
98+
+ GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb)
99+
+ {
100+
+ GC_ASSERT(GC_is_initialized);
101+
+ sb -> mem_base = GC_stackbottom;
102+
+# ifdef IA64
103+
+ sb -> reg_base = GC_register_stackbottom;
104+
+# endif
105+
+ return &GC_stackbottom; /* gc_thread_handle */
106+
+ }
107+
#endif /* !THREADS */
108+
109+
GC_API void * GC_CALL GC_do_blocking(GC_fn_type fn, void * client_data)
110+
diff --git a/pthread_support.c b/pthread_support.c
111+
index 6c138c1c..0b2af4ac 100644
112+
--- a/pthread_support.c
113+
+++ b/pthread_support.c
114+
@@ -1403,6 +1403,62 @@ GC_INNER void GC_do_blocking_inner(ptr_t data, void * context GC_ATTR_UNUSED)
115+
UNLOCK();
116+
}
117+
118+
+GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle,
119+
+ const struct GC_stack_base *sb)
120+
+{
121+
+ GC_thread t = (GC_thread)gc_thread_handle;
122+
+
123+
+ GC_ASSERT(sb -> mem_base != NULL);
124+
+ if (!EXPECT(GC_is_initialized, TRUE)) {
125+
+ GC_ASSERT(NULL == t);
126+
+ } else {
127+
+ GC_ASSERT(I_HOLD_LOCK());
128+
+ if (NULL == t) /* current thread? */
129+
+ t = GC_lookup_thread(pthread_self());
130+
+ GC_ASSERT((t -> flags & FINISHED) == 0);
131+
+ GC_ASSERT(!(t -> thread_blocked)
132+
+ && NULL == t -> traced_stack_sect); /* for now */
133+
+
134+
+ if ((t -> flags & MAIN_THREAD) == 0) {
135+
+ t -> stack_end = (ptr_t)sb->mem_base;
136+
+# ifdef IA64
137+
+ t -> backing_store_end = (ptr_t)sb->reg_base;
138+
+# endif
139+
+ return;
140+
+ }
141+
+ /* Otherwise alter the stack bottom of the primordial thread. */
142+
+ }
143+
+
144+
+ GC_stackbottom = (char*)sb->mem_base;
145+
+# ifdef IA64
146+
+ GC_register_stackbottom = (ptr_t)sb->reg_base;
147+
+# endif
148+
+}
149+
+
150+
+GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb)
151+
+{
152+
+ pthread_t self = pthread_self();
153+
+ GC_thread me;
154+
+ DCL_LOCK_STATE;
155+
+
156+
+ LOCK();
157+
+ me = GC_lookup_thread(self);
158+
+ /* The thread is assumed to be registered. */
159+
+ if ((me -> flags & MAIN_THREAD) == 0) {
160+
+ sb -> mem_base = me -> stack_end;
161+
+# ifdef IA64
162+
+ sb -> reg_base = me -> backing_store_end;
163+
+# endif
164+
+ } else {
165+
+ sb -> mem_base = GC_stackbottom;
166+
+# ifdef IA64
167+
+ sb -> reg_base = GC_register_stackbottom;
168+
+# endif
169+
+ }
170+
+ UNLOCK();
171+
+ return (void *)me; /* gc_thread_handle */
172+
+}
173+
+
174+
/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */
175+
/* functionality. It might be called from a user function invoked by */
176+
/* GC_do_blocking() to temporarily back allow calling any GC function */
177+
diff --git a/tests/test.c b/tests/test.c
178+
index f7db8495..c5338005 100644
179+
--- a/tests/test.c
180+
+++ b/tests/test.c
181+
@@ -1312,6 +1312,18 @@ void * GC_CALLBACK inc_int_counter(void *pcounter)
182+
return NULL;
183+
}
184+
185+
+struct thr_hndl_sb_s {
186+
+ void *gc_thread_handle;
187+
+ struct GC_stack_base sb;
188+
+};
189+
+
190+
+void * GC_CALLBACK set_stackbottom(void *cd)
191+
+{
192+
+ GC_set_stackbottom(((struct thr_hndl_sb_s *)cd)->gc_thread_handle,
193+
+ &((struct thr_hndl_sb_s *)cd)->sb);
194+
+ return NULL;
195+
+}
196+
+
197+
#ifndef MIN_WORDS
198+
# define MIN_WORDS 2
199+
#endif
200+
@@ -1332,6 +1344,7 @@ void run_one_test(void)
201+
pid_t pid;
202+
int wstatus;
203+
# endif
204+
+ struct thr_hndl_sb_s thr_hndl_sb;
205+
206+
GC_FREE(0);
207+
# ifdef THREADS
208+
@@ -1476,6 +1489,7 @@ void run_one_test(void)
209+
GC_FREE(GC_MALLOC_IGNORE_OFF_PAGE(2));
210+
}
211+
}
212+
+ thr_hndl_sb.gc_thread_handle = GC_get_my_stackbottom(&thr_hndl_sb.sb);
213+
# ifdef GC_GCJ_SUPPORT
214+
GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *));
215+
GC_init_gcj_malloc(0, (void *)(GC_word)fake_gcj_mark_proc);
216+
@@ -1545,6 +1559,8 @@ void run_one_test(void)
217+
exit(0);
218+
}
219+
# endif
220+
+ (void)GC_call_with_alloc_lock(set_stackbottom, &thr_hndl_sb);
221+
+
222+
/* Repeated list reversal test. */
223+
# ifndef NO_CLOCK
224+
GET_TIME(start_time);
225+
diff --git a/win32_threads.c b/win32_threads.c
226+
index 2e9f37fb..f45e8c04 100644
227+
--- a/win32_threads.c
228+
+++ b/win32_threads.c
229+
@@ -214,6 +214,11 @@ struct GC_Thread_Rep {
230+
# ifdef IA64
231+
ptr_t backing_store_end;
232+
ptr_t backing_store_ptr;
233+
+# elif defined(I386)
234+
+ ptr_t initial_stack_base;
235+
+ /* The cold end of the stack saved by */
236+
+ /* GC_record_stack_base (never modified */
237+
+ /* by GC_set_stackbottom). */
238+
# endif
239+
240+
ptr_t thread_blocked_sp; /* Protected by GC lock. */
241+
@@ -374,6 +379,8 @@ GC_INLINE void GC_record_stack_base(GC_vthread me,
242+
me -> stack_base = (ptr_t)sb->mem_base;
243+
# ifdef IA64
244+
me -> backing_store_end = (ptr_t)sb->reg_base;
245+
+# elif defined(I386)
246+
+ me -> initial_stack_base = (ptr_t)sb->mem_base;
247+
# endif
248+
if (me -> stack_base == NULL)
249+
ABORT("Bad stack base in GC_register_my_thread");
250+
@@ -913,8 +920,12 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
251+
/* Adjust our stack bottom pointer (this could happen unless */
252+
/* GC_get_stack_base() was used which returned GC_SUCCESS). */
253+
GC_ASSERT(me -> stack_base != NULL);
254+
- if ((word)me->stack_base < (word)(&stacksect))
255+
+ if ((word)me->stack_base < (word)(&stacksect)) {
256+
me -> stack_base = (ptr_t)(&stacksect);
257+
+# if defined(I386)
258+
+ me -> initial_stack_base = me -> stack_base;
259+
+# endif
260+
+ }
261+
262+
if (me -> thread_blocked_sp == NULL) {
263+
/* We are not inside GC_do_blocking() - do nothing more. */
264+
@@ -958,6 +969,53 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
265+
return client_data; /* result */
266+
}
267+
268+
+GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle,
269+
+ const struct GC_stack_base *sb)
270+
+{
271+
+ GC_thread t = (GC_thread)gc_thread_handle;
272+
+
273+
+ GC_ASSERT(sb -> mem_base != NULL);
274+
+ if (!EXPECT(GC_is_initialized, TRUE)) {
275+
+ GC_ASSERT(NULL == t);
276+
+ GC_stackbottom = (char *)sb->mem_base;
277+
+# ifdef IA64
278+
+ GC_register_stackbottom = (ptr_t)sb->reg_base;
279+
+# endif
280+
+ return;
281+
+ }
282+
+
283+
+ GC_ASSERT(I_HOLD_LOCK());
284+
+ if (NULL == t) { /* current thread? */
285+
+ t = GC_lookup_thread_inner(GetCurrentThreadId());
286+
+ CHECK_LOOKUP_MY_THREAD(t);
287+
+ }
288+
+ GC_ASSERT(!KNOWN_FINISHED(t));
289+
+ GC_ASSERT(NULL == t -> thread_blocked_sp
290+
+ && NULL == t -> traced_stack_sect); /* for now */
291+
+ t -> stack_base = (ptr_t)sb->mem_base;
292+
+ t -> last_stack_min = ADDR_LIMIT; /* reset the known minimum */
293+
+# ifdef IA64
294+
+ t -> backing_store_end = (ptr_t)sb->reg_base;
295+
+# endif
296+
+}
297+
+
298+
+GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb)
299+
+{
300+
+ DWORD thread_id = GetCurrentThreadId();
301+
+ GC_thread me;
302+
+ DCL_LOCK_STATE;
303+
+
304+
+ LOCK();
305+
+ me = GC_lookup_thread_inner(thread_id);
306+
+ CHECK_LOOKUP_MY_THREAD(me); /* the thread is assumed to be registered */
307+
+ sb -> mem_base = me -> stack_base;
308+
+# ifdef IA64
309+
+ sb -> reg_base = me -> backing_store_end;
310+
+# endif
311+
+ UNLOCK();
312+
+ return (void *)me; /* gc_thread_handle */
313+
+}
314+
+
315+
#ifdef GC_PTHREADS
316+
317+
/* A quick-and-dirty cache of the mapping between pthread_t */
318+
@@ -1454,6 +1512,20 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me)
319+
# endif
320+
GC_ASSERT(!((word)thread->stack_base
321+
COOLER_THAN (word)tib->StackBase));
322+
+ if (thread->stack_base != thread->initial_stack_base) {
323+
+ /* We are in a coroutine. */
324+
+ if ((word)thread->stack_base <= (word)sp /* StackLimit */
325+
+ || (word)tib->StackBase < (word)thread->stack_base) {
326+
+ /* The coroutine stack is not within TIB stack. */
327+
+ sp = (ptr_t)context.Esp;
328+
+ WARN("GetThreadContext might return stale register values"
329+
+ " including ESP=%p\n", sp);
330+
+ /* TODO: Because of WoW64 bug, there is no guarantee that */
331+
+ /* sp really points to the stack top but, for now, we do */
332+
+ /* our best as the TIB stack limit/base cannot be used */
333+
+ /* while we are inside a coroutine. */
334+
+ }
335+
+ }
336+
} else {
337+
# ifdef DEBUG_THREADS
338+
{

0 commit comments

Comments
 (0)