1
#include "private/pthread_support.h"
3
# if defined(GC_DARWIN_THREADS)
5
/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
7
"The space beneath the stack pointer, where a new stack frame would normally
8
be allocated, is called the red zone. This area as shown in Figure 3-2 may
9
be used for any purpose as long as a new stack frame does not need to be
12
Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
13
it must set up a stack frame just like routines that call other routines."
15
#define PPC_RED_ZONE_SIZE 224
17
/* Not 64-bit clean. Wait until Apple defines their 64-bit ABI */
18
typedef struct StackFrame {
22
unsigned int reserved[2];
23
unsigned int savedRTOC;
27
unsigned int FindTopOfStack(unsigned int stack_start) {
30
if (stack_start == 0) {
31
__asm__ volatile("lwz %0,0(r1)" : "=r" (frame));
33
frame = (StackFrame *)stack_start;
37
/* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
40
if (frame->savedSP == NULL) break;
41
/* if there are no more stack frames, stop */
43
frame = (StackFrame*)frame->savedSP;
45
/* we do these next two checks after going to the next frame
46
because the LR for the first stack frame in the loop
47
is not set up on purpose, so we shouldn't check it. */
48
if ((frame->savedLR & ~3) == 0) break; /* if the next LR is bogus, stop */
49
if ((~(frame->savedLR) & ~3) == 0) break; /* ditto */
53
/* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
56
return (unsigned int)frame;
59
void GC_push_all_stacks() {
64
thread_act_array_t act_list = 0;
65
mach_msg_type_number_t listcount = 0;
67
me = mach_thread_self();
68
if (!GC_thr_initialized) GC_thr_init();
70
r = task_threads(current_task(), &act_list, &listcount);
71
if(r != KERN_SUCCESS) ABORT("task_threads failed");
72
for(i = 0; i < listcount; i++) {
73
thread_act_t thread = act_list[i];
76
hi = (ptr_t)FindTopOfStack(0);
79
ppc_thread_state_t info;
80
mach_msg_type_number_t outCount = THREAD_STATE_MAX;
81
r = thread_get_state(thread, MACHINE_THREAD_STATE,
82
(natural_t *)&info, &outCount);
83
if(r != KERN_SUCCESS) continue;
85
lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
86
hi = (ptr_t)FindTopOfStack(info.r1);
97
GC_push_one(info.r10);
98
GC_push_one(info.r11);
99
GC_push_one(info.r12);
100
GC_push_one(info.r13);
101
GC_push_one(info.r14);
102
GC_push_one(info.r15);
103
GC_push_one(info.r16);
104
GC_push_one(info.r17);
105
GC_push_one(info.r18);
106
GC_push_one(info.r19);
107
GC_push_one(info.r20);
108
GC_push_one(info.r21);
109
GC_push_one(info.r22);
110
GC_push_one(info.r23);
111
GC_push_one(info.r24);
112
GC_push_one(info.r25);
113
GC_push_one(info.r26);
114
GC_push_one(info.r27);
115
GC_push_one(info.r28);
116
GC_push_one(info.r29);
117
GC_push_one(info.r30);
118
GC_push_one(info.r31);
120
/* FIXME: Remove after testing: */
121
WARN("This is completely untested and likely will not work\n", 0);
122
i386_thread_state_t info;
123
mach_msg_type_number_t outCount = THREAD_STATE_MAX;
124
r = thread_get_state(thread, MACHINE_THREAD_STATE,
125
(natural_t *)&info, &outCount);
126
if(r != KERN_SUCCESS) continue;
128
lo = (void*)info.esp;
129
hi = (ptr_t)FindTopOfStack(info.esp);
131
GC_push_one(info.eax);
132
GC_push_one(info.ebx);
133
GC_push_one(info.ecx);
134
GC_push_one(info.edx);
135
GC_push_one(info.edi);
136
GC_push_one(info.esi);
137
/* GC_push_one(info.ebp); */
138
/* GC_push_one(info.esp); */
139
GC_push_one(info.ss);
140
GC_push_one(info.eip);
141
GC_push_one(info.cs);
142
GC_push_one(info.ds);
143
GC_push_one(info.es);
144
GC_push_one(info.fs);
145
GC_push_one(info.gs);
146
# endif /* !POWERPC */
149
GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
150
(unsigned long) thread,
155
GC_push_all_stack(lo, hi);
156
} /* for(p=GC_threads[i]...) */
159
static mach_port_t GC_mach_handler_thread;
160
static int GC_use_mach_handler_thread = 0;
162
static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
163
static int GC_mach_threads_count;
165
void GC_stop_init() {
168
for (i = 0; i < THREAD_TABLE_SZ; i++) {
169
GC_mach_threads[i].thread = 0;
170
GC_mach_threads[i].already_suspended = 0;
172
GC_mach_threads_count = 0;
175
/* returns true if there's a thread in act_list that wasn't in old_list */
176
int GC_suspend_thread_list(thread_act_array_t act_list, int count,
177
thread_act_array_t old_list, int old_count) {
178
mach_port_t my_thread = mach_thread_self();
183
for(i = 0; i < count; i++) {
184
thread_act_t thread = act_list[i];
186
GC_printf1("Attempting to suspend thread %p\n", thread);
188
/* find the current thread in the old list */
190
for(j = 0; j < old_count; j++) {
191
thread_act_t old_thread = old_list[j];
192
if (old_thread == thread) {
198
/* add it to the GC_mach_threads list */
199
GC_mach_threads[GC_mach_threads_count].thread = thread;
200
/* default is not suspended */
201
GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
205
if (thread != my_thread &&
206
(!GC_use_mach_handler_thread
207
|| (GC_use_mach_handler_thread
208
&& GC_mach_handler_thread != thread))) {
209
struct thread_basic_info info;
210
mach_msg_type_number_t outCount = THREAD_INFO_MAX;
211
kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
212
(thread_info_t)&info, &outCount);
213
if(kern_result != KERN_SUCCESS) {
214
/* the thread may have quit since the thread_threads () call
215
* we mark already_suspended so it's not dealt with anymore later
218
GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
219
GC_mach_threads_count++;
224
GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
227
GC_mach_threads[GC_mach_threads_count].already_suspended =
228
(info.run_state != TH_STATE_RUNNING);
230
if (info.run_state != TH_STATE_RUNNING) continue;
233
GC_printf1("Suspending 0x%lx\n", thread);
235
/* Suspend the thread */
236
kern_result = thread_suspend(thread);
237
if(kern_result != KERN_SUCCESS) {
238
/* the thread may have quit since the thread_threads () call
239
* we mark already_suspended so it's not dealt with anymore later
242
GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
243
GC_mach_threads_count++;
248
if (!found) GC_mach_threads_count++;
254
/* Caller holds allocation lock. */
259
mach_port_t my_thread = mach_thread_self();
260
kern_return_t kern_result;
261
thread_act_array_t act_list, prev_list;
262
mach_msg_type_number_t listcount, prevcount;
265
GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
268
/* clear out the mach threads list table */
271
/* Make sure all free list construction has stopped before we start. */
272
/* No new construction can start, since free list construction is */
273
/* required to acquire and release the GC lock before it starts, */
274
/* and we have the lock. */
275
# ifdef PARALLEL_MARK
276
GC_acquire_mark_lock();
277
GC_ASSERT(GC_fl_builder_count == 0);
278
/* We should have previously waited for it to become zero. */
279
# endif /* PARALLEL_MARK */
281
/* Loop stopping threads until you have gone over the whole list
282
twice without a new one appearing. thread_create() won't
283
return (and thus the thread stop) until the new thread
284
exists, so there is no window whereby you could stop a
285
thread, recognise it is stopped, but then have a new thread
286
it created before stopping show up later.
294
kern_result = task_threads(current_task(), &act_list, &listcount);
295
result = GC_suspend_thread_list(act_list, listcount,
296
prev_list, prevcount);
298
prev_list = act_list;
299
prevcount = listcount;
305
extern void GC_mprotect_stop();
310
# ifdef PARALLEL_MARK
311
GC_release_mark_lock();
314
GC_printf1("World stopped from 0x%lx\n", my_thread);
318
/* Caller holds allocation lock, and has held it continuously since */
319
/* the world stopped. */
320
void GC_start_world()
322
mach_port_t my_thread = mach_thread_self();
325
kern_return_t kern_result;
326
thread_act_array_t act_list;
327
mach_msg_type_number_t listcount;
330
GC_printf0("World starting\n");
335
extern void GC_mprotect_resume();
336
GC_mprotect_resume();
340
kern_result = task_threads(current_task(), &act_list, &listcount);
341
for(i = 0; i < listcount; i++) {
342
thread_act_t thread = act_list[i];
343
if (thread != my_thread &&
344
(!GC_use_mach_handler_thread ||
345
(GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
346
for(j = 0; j < GC_mach_threads_count; j++) {
347
if (thread == GC_mach_threads[j].thread) {
348
if (GC_mach_threads[j].already_suspended) {
350
GC_printf1("Not resuming already suspended thread %p\n", thread);
354
struct thread_basic_info info;
355
mach_msg_type_number_t outCount = THREAD_INFO_MAX;
356
kern_result = thread_info(thread, THREAD_BASIC_INFO,
357
(thread_info_t)&info, &outCount);
358
if(kern_result != KERN_SUCCESS) continue;
360
GC_printf2("Thread state for 0x%lx = %d\n", thread,
362
GC_printf1("Resuming 0x%lx\n", thread);
364
/* Resume the thread */
365
kern_result = thread_resume(thread);
366
if(kern_result != KERN_SUCCESS) continue;
372
GC_printf0("World started\n");
376
void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
377
GC_mach_handler_thread = thread;
378
GC_use_mach_handler_thread = 1;