[project @ 2000-02-24 17:20:46 by simonmar]
[ghc-hetmet.git] / ghc / rts / Exception.hc
1 /* -----------------------------------------------------------------------------
2  * $Id: Exception.hc,v 1.8 2000/02/24 17:20:46 simonmar Exp $
3  *
4  * (c) The GHC Team, 1998-1999
5  *
6  * Exception support
7  *
8  * ---------------------------------------------------------------------------*/
9
10 #include "Rts.h"
11 #include "Exception.h"
12 #include "Schedule.h"
13 #include "StgRun.h"
14 #include "Storage.h"
15 #include "RtsUtils.h"
16 #include "RtsFlags.h"
17 #if defined(PAR)
18 # include "FetchMe.h"
19 #endif
20
21 /* -----------------------------------------------------------------------------
22    Exception Primitives
23
24    A thread can request that asynchronous exceptions not be delivered
25    ("blocked") for the duration of an I/O computation.  The primitive
26    
27         blockAsyncExceptions# :: IO a -> IO a
28
29    is used for this purpose.  During a blocked section, asynchronous
30    exceptions may be unblocked again temporarily:
31
32         unblockAsyncExceptions# :: IO a -> IO a
33
34    Furthermore, asynchronous exceptions are blocked automatically during
35    the execution of an exception handler.  Both of these primitives
36    leave a continuation on the stack which reverts to the previous
37    state (blocked or unblocked) on exit.
38
39    A thread which wants to raise an exception in another thread (using
40    killThread#) must block until the target thread is ready to receive
41    it.  The action of unblocking exceptions in a thread will release all
42    the threads waiting to deliver exceptions to that thread.
43
44    -------------------------------------------------------------------------- */
45
46 FN_(blockAsyncExceptionszh_fast)
47 {
48   FB_
49     /* Args: R1 :: IO a */
50     STK_CHK_GEN( 2/* worst case */, R1_PTR, blockAsyncExceptionszh_fast, );
51
52     if (CurrentTSO->blocked_exceptions == NULL) {
53       CurrentTSO->blocked_exceptions = END_TSO_QUEUE;
54       /* avoid growing the stack unnecessarily */
55       if (Sp[0] == (W_)&blockAsyncExceptionszh_ret_info) {
56         Sp++;
57       } else {
58         Sp--;
59         Sp[0] = (W_)&unblockAsyncExceptionszh_ret_info;
60       }
61     }
62     Sp--;
63     Sp[0] = ARG_TAG(0);
64     JMP_(GET_ENTRY(R1.cl));
65   FE_
66 }
67
68 INFO_TABLE_SRT_BITMAP(unblockAsyncExceptionszh_ret_info, unblockAsyncExceptionszh_ret_entry, 0, 0, 0, 0, RET_SMALL, , EF_, 0, 0);
69 FN_(unblockAsyncExceptionszh_ret_entry)
70 {
71   FB_
72     ASSERT(CurrentTSO->blocked_exceptions != NULL);
73 #if defined(GRAN)
74       awakenBlockedQueue(CurrentTSO->blocked_exceptions, 
75                          CurrentTSO->block_info.closure);
76 #elif defined(PAR)
77       // is CurrentTSO->block_info.closure always set to the node
78       // holding the blocking queue !? -- HWL
79       awakenBlockedQueue(CurrentTSO->blocked_exceptions, 
80                          CurrentTSO->block_info.closure);
81 #else
82     awakenBlockedQueue(CurrentTSO->blocked_exceptions);
83 #endif
84     CurrentTSO->blocked_exceptions = NULL;
85 #ifdef REG_R1
86     Sp++;
87     JMP_(ENTRY_CODE(Sp[0]));
88 #else
89     Sp[1] = Sp[0];
90     Sp++;
91     JMP_(ENTRY_CODE(Sp[1]));
92 #endif
93   FE_
94 }
95
96 FN_(unblockAsyncExceptionszh_fast)
97 {
98   FB_
99     /* Args: R1 :: IO a */
100     STK_CHK_GEN(2, R1_PTR, unblockAsyncExceptionszh_fast, );
101
102     if (CurrentTSO->blocked_exceptions != NULL) {
103 #if defined(GRAN)
104       awakenBlockedQueue(CurrentTSO->blocked_exceptions, 
105                          CurrentTSO->block_info.closure);
106 #elif defined(PAR)
107       // is CurrentTSO->block_info.closure always set to the node
108       // holding the blocking queue !? -- HWL
109       awakenBlockedQueue(CurrentTSO->blocked_exceptions, 
110                          CurrentTSO->block_info.closure);
111 #else
112       awakenBlockedQueue(CurrentTSO->blocked_exceptions);
113 #endif
114       CurrentTSO->blocked_exceptions = NULL;
115
116       /* avoid growing the stack unnecessarily */
117       if (Sp[0] == (W_)&unblockAsyncExceptionszh_ret_info) {
118         Sp++;
119       } else {
120         Sp--;   
121         Sp[0] = (W_)&blockAsyncExceptionszh_ret_info;
122       }
123     }
124     Sp--;
125     Sp[0] = ARG_TAG(0);
126     JMP_(GET_ENTRY(R1.cl));
127   FE_
128 }
129
130 INFO_TABLE_SRT_BITMAP(blockAsyncExceptionszh_ret_info, blockAsyncExceptionszh_ret_entry, 0, 0, 0, 0, RET_SMALL, , EF_, 0, 0);
131 FN_(blockAsyncExceptionszh_ret_entry)
132 {
133   FB_
134     ASSERT(CurrentTSO->blocked_exceptions == NULL);
135     CurrentTSO->blocked_exceptions = END_TSO_QUEUE;
136 #ifdef REG_R1
137     Sp++;
138     JMP_(ENTRY_CODE(Sp[0]));
139 #else
140     Sp[1] = Sp[0];
141     Sp++;
142     JMP_(ENTRY_CODE(Sp[1]));
143 #endif
144   FE_
145 }
146
147
148 FN_(killThreadzh_fast)
149 {
150   FB_
151   /* args: R1.p = TSO to kill, R2.p = Exception */
152
153   /* This thread may have been relocated.
154    * (see Schedule.c:threadStackOverflow)
155    */
156   while (R1.t->whatNext == ThreadRelocated) {
157     R1.t = R1.t->link;
158   }
159
160   /* If the target thread is currently blocking async exceptions,
161    * we'll have to block until it's ready to accept them.
162    */
163   if (R1.t->blocked_exceptions != NULL) {
164
165         /* ToDo (SMP): locking if destination thread is currently
166          * running...
167          */
168         CurrentTSO->link = R1.t->blocked_exceptions;
169         R1.t->blocked_exceptions = CurrentTSO;
170
171         CurrentTSO->why_blocked = BlockedOnException;
172         CurrentTSO->block_info.tso = R1.t;
173
174         BLOCK( R1_PTR | R2_PTR, killThreadzh_fast );
175   }
176
177   /* Killed threads turn into zombies, which might be garbage
178    * collected at a later date.  That's why we don't have to
179    * explicitly remove them from any queues they might be on.
180    */
181
182   /* We might have killed ourselves.  In which case, better be *very*
183    * careful.  If the exception killed us, then return to the scheduler.
184    * If the exception went to a catch frame, we'll just continue from
185    * the handler.
186    */
187   if (R1.t == CurrentTSO) {
188         SaveThreadState();      /* inline! */
189         STGCALL2(raiseAsync, R1.t, R2.cl);
190         if (CurrentTSO->whatNext == ThreadKilled) {
191                 R1.w = ThreadYielding;
192                 JMP_(StgReturn);
193         }
194         LoadThreadState();
195         if (CurrentTSO->whatNext == ThreadEnterGHC) {
196                 R1.w = Sp[0];
197                 Sp++;
198                 JMP_(GET_ENTRY(R1.cl));
199         } else {
200                 barf("killThreadzh_fast");
201         }
202   } else {
203         STGCALL2(raiseAsync, R1.t, R2.cl);
204   }
205
206   JMP_(ENTRY_CODE(Sp[0]));
207   FE_
208 }
209
210 /* -----------------------------------------------------------------------------
211    Catch frames
212    -------------------------------------------------------------------------- */
213
214 #ifdef REG_R1
215 #define CATCH_FRAME_ENTRY_TEMPLATE(label,ret)   \
216    FN_(label);                                  \
217    FN_(label)                                   \
218    {                                            \
219       FB_                                       \
220       Su = ((StgCatchFrame *)Sp)->link;         \
221       Sp += sizeofW(StgCatchFrame);             \
222       JMP_(ret);                                \
223       FE_                                       \
224    }
225 #else
226 #define CATCH_FRAME_ENTRY_TEMPLATE(label,ret)   \
227    FN_(label);                                  \
228    FN_(label)                                   \
229    {                                            \
230       StgWord rval;                             \
231       FB_                                       \
232       rval = Sp[0];                             \
233       Sp++;                                     \
234       Su = ((StgCatchFrame *)Sp)->link;         \
235       Sp += sizeofW(StgCatchFrame) - 1;         \
236       Sp[0] = rval;                             \
237       JMP_(ret);                                \
238       FE_                                       \
239    }
240 #endif
241
242 #ifdef REG_R1
243 #define SP_OFF 0
244 #else
245 #define SP_OFF 1
246 #endif
247
248 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_entry,ENTRY_CODE(Sp[SP_OFF]));
249 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_0_entry,RET_VEC(Sp[SP_OFF],0));
250 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_1_entry,RET_VEC(Sp[SP_OFF],1));
251 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_2_entry,RET_VEC(Sp[SP_OFF],2));
252 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_3_entry,RET_VEC(Sp[SP_OFF],3));
253 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_4_entry,RET_VEC(Sp[SP_OFF],4));
254 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_5_entry,RET_VEC(Sp[SP_OFF],5));
255 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_6_entry,RET_VEC(Sp[SP_OFF],6));
256 CATCH_FRAME_ENTRY_TEMPLATE(catch_frame_7_entry,RET_VEC(Sp[SP_OFF],7));
257
258 #ifdef PROFILING
259 #define CATCH_FRAME_BITMAP 7
260 #else
261 #define CATCH_FRAME_BITMAP 3
262 #endif
263
264 /* Catch frames are very similar to update frames, but when entering
265  * one we just pop the frame off the stack and perform the correct
266  * kind of return to the activation record underneath us on the stack.
267  */
268
269 VEC_POLY_INFO_TABLE(catch_frame, CATCH_FRAME_BITMAP, NULL/*srt*/, 0/*srt_off*/, 0/*srt_len*/, CATCH_FRAME,, EF_);
270
271 /* -----------------------------------------------------------------------------
272  * The catch infotable
273  *
274  * This should be exactly the same as would be generated by this STG code
275  *
276  * catch = {x,h} \n {} -> catch#{x,h}
277  *
278  * It is used in deleteThread when reverting blackholes.
279  * -------------------------------------------------------------------------- */
280
281 INFO_TABLE(catch_info,catch_entry,2,0,FUN,,EF_,0,0);
282 STGFUN(catch_entry)
283 {
284   FB_
285   R2.cl = payloadCPtr(R1.cl,1); /* h */
286   R1.cl = payloadCPtr(R1.cl,0); /* x */
287   JMP_(catchzh_fast);
288   FE_
289 }
290
291 FN_(catchzh_fast)
292 {
293   StgCatchFrame *fp;
294   FB_
295
296     /* args: R1 = m :: IO a, R2 = handler :: Exception -> IO a */
297     STK_CHK_GEN(sizeofW(StgCatchFrame) + 1, R1_PTR | R2_PTR, catchzh_fast, );
298   
299     /* Set up the catch frame */
300     Sp -= sizeofW(StgCatchFrame);
301     fp = (StgCatchFrame *)Sp;
302     SET_HDR(fp,(StgInfoTable *)&catch_frame_info,CCCS);
303     fp -> handler = R2.cl;
304     fp -> exceptions_blocked = (CurrentTSO->blocked_exceptions != NULL);
305     fp -> link = Su;
306     Su = (StgUpdateFrame *)fp;
307     TICK_CATCHF_PUSHED();
308
309     /* Push realworld token and enter R1. */
310     Sp--;
311     Sp[0] = ARG_TAG(0);
312     TICK_ENT_VIA_NODE();
313     JMP_(GET_ENTRY(R1.cl));
314     
315   FE_
316 }      
317
318 /* -----------------------------------------------------------------------------
319  * The raise infotable
320  * 
321  * This should be exactly the same as would be generated by this STG code
322  *
323  *   raise = {err} \n {} -> raise#{err}
324  *
325  * It is used in raisezh_fast to update thunks on the update list
326  * -------------------------------------------------------------------------- */
327
328 INFO_TABLE(raise_info,raise_entry,1,0,FUN,,EF_,0,0);
329 STGFUN(raise_entry)
330 {
331   FB_
332   R1.cl = R1.cl->payload[0];
333   JMP_(raisezh_fast);
334   FE_
335 }
336
337 FN_(raisezh_fast)
338 {
339   StgClosure *handler;
340   StgUpdateFrame *p;
341   StgClosure *raise_closure;
342   FB_
343     /* args : R1 = error */
344
345
346 #if defined(PROFILING)
347
348     /* Debugging tool: on raising an  exception, show where we are. */
349
350     /* ToDo: currently this is a hack.  Would be much better if
351      * the info was only displayed for an *uncaught* exception.
352      */
353     if (RtsFlags.ProfFlags.showCCSOnException) {
354       STGCALL2(print_ccs,stderr,CCCS);
355     }
356
357 #endif
358
359     p = Su;
360
361     /* This closure represents the expression 'raise# E' where E
362      * is the exception raise.  It is used to overwrite all the
363      * thunks which are currently under evaluataion.
364      */
365     raise_closure = (StgClosure *)RET_STGCALL1(P_,allocate,
366                                                sizeofW(StgClosure)+1);
367     raise_closure->header.info = &raise_info;
368     raise_closure->payload[0] = R1.cl;
369
370     while (1) {
371
372       switch (get_itbl(p)->type) {
373
374       case UPDATE_FRAME:
375         UPD_IND(p->updatee,raise_closure);
376         p = p->link;
377         continue;
378
379       case SEQ_FRAME:
380         p = ((StgSeqFrame *)p)->link;
381         continue;
382
383       case CATCH_FRAME:
384         /* found it! */
385         break;
386
387       case STOP_FRAME:
388         barf("raisezh_fast: STOP_FRAME");
389
390       default:
391         barf("raisezh_fast: weird activation record");
392       }
393       
394       break;
395
396     }
397     
398     /* Ok, p points to the enclosing CATCH_FRAME.  Pop everything down to
399      * and including this frame, update Su, push R1, and enter the handler.
400      */
401     Su = ((StgCatchFrame *)p)->link; 
402     handler = ((StgCatchFrame *)p)->handler;
403     
404     Sp = (P_)p + sizeofW(StgCatchFrame);
405
406     /* Restore the blocked/unblocked state for asynchronous exceptions
407      * at the CATCH_FRAME.  
408      *
409      * If exceptions were unblocked, arrange that they are unblocked
410      * again after executing the handler by pushing an
411      * unblockAsyncExceptions_ret stack frame.
412      */
413     if (! ((StgCatchFrame *)p)->exceptions_blocked) {
414       *(--Sp) = (W_)&unblockAsyncExceptionszh_ret_info;
415     }
416
417     /* Ensure that async excpetions are blocked when running the handler.
418     */
419     if (CurrentTSO->blocked_exceptions == NULL) {
420       CurrentTSO->blocked_exceptions = END_TSO_QUEUE;
421     }
422
423     /* Enter the handler, passing the exception value and a realworld
424      * token as arguments.
425      */
426     Sp -= 2;
427     Sp[0] = R1.w;
428     Sp[1] = ARG_TAG(0);
429     TICK_ENT_VIA_NODE();
430     R1.cl = handler;
431     JMP_(GET_ENTRY(R1.cl));
432
433   FE_
434 }