~ubuntu-branches/debian/lenny/mono/lenny

« back to all changes in this revision

Viewing changes to libgc/darwin_stop_world.c

  • Committer: Bazaar Package Importer
  • Author(s): Debian Mono Group
  • Date: 2004-06-19 14:38:57 UTC
  • Revision ID: james.westby@ubuntu.com-20040619143857-pycck6oxgwd172zc
Tags: upstream-0.96
ImportĀ upstreamĀ versionĀ 0.96

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "private/pthread_support.h"
 
2
 
 
3
# if defined(GC_DARWIN_THREADS)
 
4
 
 
5
/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
 
6
   Page 49:
 
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
 
10
   added to the stack."
 
11
   
 
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."
 
14
*/
 
15
#define PPC_RED_ZONE_SIZE 224
 
16
 
 
17
/* Not 64-bit clean. Wait until Apple defines their 64-bit ABI */
 
18
typedef struct StackFrame {
 
19
  unsigned int  savedSP;
 
20
  unsigned int  savedCR;
 
21
  unsigned int  savedLR;
 
22
  unsigned int  reserved[2];
 
23
  unsigned int  savedRTOC;
 
24
} StackFrame;
 
25
 
 
26
 
 
27
unsigned int FindTopOfStack(unsigned int stack_start) {
 
28
  StackFrame    *frame;
 
29
  
 
30
  if (stack_start == 0) {
 
31
    __asm__ volatile("lwz       %0,0(r1)" : "=r" (frame));
 
32
  } else {
 
33
    frame = (StackFrame *)stack_start;
 
34
  }
 
35
 
 
36
# ifdef DEBUG_THREADS
 
37
    /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
 
38
# endif
 
39
  do {
 
40
    if (frame->savedSP == NULL) break;
 
41
                /* if there are no more stack frames, stop */
 
42
 
 
43
    frame = (StackFrame*)frame->savedSP;
 
44
 
 
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 */
 
50
  } while (1); 
 
51
 
 
52
# ifdef DEBUG_THREADS
 
53
    /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
 
54
# endif
 
55
 
 
56
  return (unsigned int)frame;
 
57
}       
 
58
 
 
59
void GC_push_all_stacks() {
 
60
    int i;
 
61
    kern_return_t r;
 
62
    mach_port_t me;
 
63
    ptr_t lo, hi;
 
64
    thread_act_array_t act_list = 0;
 
65
    mach_msg_type_number_t listcount = 0;
 
66
 
 
67
    me = mach_thread_self();
 
68
    if (!GC_thr_initialized) GC_thr_init();
 
69
    
 
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];
 
74
      if (thread == me) {
 
75
        lo = GC_approx_sp();
 
76
        hi = (ptr_t)FindTopOfStack(0);
 
77
      } else {
 
78
#      ifdef POWERPC
 
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;
 
84
 
 
85
        lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
 
86
        hi = (ptr_t)FindTopOfStack(info.r1);
 
87
 
 
88
        GC_push_one(info.r0); 
 
89
        GC_push_one(info.r2); 
 
90
        GC_push_one(info.r3); 
 
91
        GC_push_one(info.r4); 
 
92
        GC_push_one(info.r5); 
 
93
        GC_push_one(info.r6); 
 
94
        GC_push_one(info.r7); 
 
95
        GC_push_one(info.r8); 
 
96
        GC_push_one(info.r9); 
 
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);
 
119
#      else
 
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;
 
127
 
 
128
        lo = (void*)info.esp;
 
129
        hi = (ptr_t)FindTopOfStack(info.esp);
 
130
 
 
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 */
 
147
      }
 
148
#     if DEBUG_THREADS
 
149
       GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
 
150
                  (unsigned long) thread,
 
151
                  (unsigned long) lo,
 
152
                  (unsigned long) hi
 
153
                 );
 
154
#     endif
 
155
      GC_push_all_stack(lo, hi); 
 
156
    } /* for(p=GC_threads[i]...) */
 
157
}
 
158
 
 
159
static mach_port_t GC_mach_handler_thread;
 
160
static int GC_use_mach_handler_thread = 0;
 
161
 
 
162
static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
 
163
static int GC_mach_threads_count;
 
164
 
 
165
void GC_stop_init() {
 
166
  int i;
 
167
 
 
168
  for (i = 0; i < THREAD_TABLE_SZ; i++) {
 
169
    GC_mach_threads[i].thread = 0;
 
170
    GC_mach_threads[i].already_suspended = 0;
 
171
  }
 
172
  GC_mach_threads_count = 0;
 
173
}
 
174
 
 
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();
 
179
  int i, j;
 
180
 
 
181
  int changed = 0;
 
182
 
 
183
  for(i = 0; i < count; i++) {
 
184
    thread_act_t thread = act_list[i];
 
185
#   if DEBUG_THREADS 
 
186
      GC_printf1("Attempting to suspend thread %p\n", thread);
 
187
#   endif
 
188
    /* find the current thread in the old list */
 
189
    int found = 0;
 
190
    for(j = 0; j < old_count; j++) {
 
191
      thread_act_t old_thread = old_list[j];
 
192
      if (old_thread == thread) {
 
193
        found = 1;
 
194
        break;
 
195
      }
 
196
    }
 
197
    if (!found) {
 
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;
 
202
      changed = 1;
 
203
    }      
 
204
 
 
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
 
216
         */
 
217
        if (!found) {
 
218
          GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
 
219
          GC_mach_threads_count++;
 
220
        }
 
221
        continue;
 
222
      }
 
223
#     if DEBUG_THREADS
 
224
        GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
 
225
#     endif
 
226
      if (!found) {
 
227
        GC_mach_threads[GC_mach_threads_count].already_suspended =
 
228
          (info.run_state != TH_STATE_RUNNING);
 
229
      }
 
230
      if (info.run_state != TH_STATE_RUNNING) continue;
 
231
      
 
232
#     if DEBUG_THREADS
 
233
        GC_printf1("Suspending 0x%lx\n", thread);
 
234
#     endif
 
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
 
240
         */
 
241
        if (!found) {
 
242
          GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
 
243
          GC_mach_threads_count++;
 
244
        }
 
245
        continue;
 
246
      }
 
247
    } 
 
248
    if (!found) GC_mach_threads_count++;
 
249
  }
 
250
  return changed;
 
251
}
 
252
 
 
253
 
 
254
/* Caller holds allocation lock.        */
 
255
void GC_stop_world()
 
256
{
 
257
  int i, changes;
 
258
    GC_thread p;
 
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;
 
263
    
 
264
#   if DEBUG_THREADS
 
265
      GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
 
266
#   endif
 
267
 
 
268
    /* clear out the mach threads list table */
 
269
    GC_stop_init(); 
 
270
       
 
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 */
 
280
 
 
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.
 
287
      */
 
288
      
 
289
      changes = 1;
 
290
      prev_list = NULL;
 
291
      prevcount = 0;
 
292
      do {
 
293
        int result;
 
294
        kern_result = task_threads(current_task(), &act_list, &listcount);
 
295
        result = GC_suspend_thread_list(act_list, listcount,
 
296
                                        prev_list, prevcount);
 
297
        changes = result;
 
298
        prev_list = act_list;
 
299
        prevcount = listcount;
 
300
      } while (changes);
 
301
      
 
302
 
 
303
#   ifdef MPROTECT_VDB
 
304
      if(GC_incremental) {
 
305
        extern void GC_mprotect_stop();
 
306
        GC_mprotect_stop();
 
307
      }
 
308
#   endif
 
309
    
 
310
#   ifdef PARALLEL_MARK
 
311
      GC_release_mark_lock();
 
312
#   endif
 
313
    #if DEBUG_THREADS
 
314
      GC_printf1("World stopped from 0x%lx\n", my_thread);
 
315
    #endif
 
316
}
 
317
 
 
318
/* Caller holds allocation lock, and has held it continuously since     */
 
319
/* the world stopped.                                                   */
 
320
void GC_start_world()
 
321
{
 
322
  mach_port_t my_thread = mach_thread_self();
 
323
  int i, j;
 
324
  GC_thread p;
 
325
  kern_return_t kern_result;
 
326
  thread_act_array_t act_list;
 
327
  mach_msg_type_number_t listcount;
 
328
  
 
329
#   if DEBUG_THREADS
 
330
      GC_printf0("World starting\n");
 
331
#   endif
 
332
 
 
333
#   ifdef MPROTECT_VDB
 
334
      if(GC_incremental) {
 
335
        extern void GC_mprotect_resume();
 
336
        GC_mprotect_resume();
 
337
      }
 
338
#   endif
 
339
 
 
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) {
 
349
#             if DEBUG_THREADS
 
350
                GC_printf1("Not resuming already suspended thread %p\n", thread);
 
351
#             endif
 
352
              continue;
 
353
            }
 
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;
 
359
#           if DEBUG_THREADS
 
360
              GC_printf2("Thread state for 0x%lx = %d\n", thread,
 
361
                         info.run_state);
 
362
              GC_printf1("Resuming 0x%lx\n", thread);
 
363
#           endif
 
364
            /* Resume the thread */
 
365
            kern_result = thread_resume(thread);
 
366
            if(kern_result != KERN_SUCCESS) continue;
 
367
          } 
 
368
        }
 
369
      }
 
370
    }
 
371
#   if DEBUG_THREADS
 
372
     GC_printf0("World started\n");
 
373
#   endif
 
374
}
 
375
 
 
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;
 
379
}
 
380
 
 
381
#endif