Fri Jun 28 02:41:08 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[glibc.git] / hurd / sigunwind.c
blob28c0d941b5e79e47f161638310b511c827d9fcce
1 /* longjmp cleanup function for unwinding past signal handlers.
2 Copyright (C) 1995, 1996 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA. */
20 #include <hurd.h>
21 #include "thread_state.h"
22 #include <setjmp.h>
23 #include <assert.h>
26 /* _hurd_setup_sighandler puts a link on the `active resources' chain so that
27 _longjmp_unwind will call this function with the `struct sigcontext *'
28 describing the context interrupted by the signal, when `longjmp' is jumping
29 to an environment that unwinds past the interrupted frame. */
31 void
32 _hurdsig_longjmp_from_handler (void *data, jmp_buf env, int val)
34 struct sigcontext *scp = data;
35 struct hurd_sigstate *ss = _hurd_self_sigstate ();
36 int onstack;
37 inline void cleanup (void)
39 /* Destroy the MiG reply port used by the signal handler, and restore
40 the reply port in use by the thread when interrupted. */
41 mach_port_t *reply_port =
42 (mach_port_t *) __hurd_threadvar_location (_HURD_THREADVAR_MIG_REPLY);
43 if (*reply_port)
45 mach_port_t port = *reply_port;
46 /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port
47 not to get another reply port, but avoids mig_dealloc_reply_port
48 trying to deallocate it after the receive fails (which it will,
49 because the reply port will be bogus, regardless). */
50 *reply_port = MACH_PORT_DEAD;
51 __mach_port_destroy (__mach_task_self (), port);
53 *reply_port = scp->sc_reply_port;
56 __spin_lock (&ss->lock);
57 /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c),
58 which calls us inside a critical section. */
59 assert (__spin_lock_locked (&ss->critical_section_lock));
60 /* Are we on the alternate signal stack now? */
61 onstack = (ss->sigaltstack.ss_flags & SA_ONSTACK);
62 __spin_unlock (&ss->lock);
64 if (onstack && ! scp->sc_onstack)
66 /* We are unwinding off the signal stack. We must use sigreturn to
67 do it robustly. Mutate the sigcontext so that when sigreturn
68 resumes from that context, it will be as if `__longjmp (ENV, VAL)'
69 were done. */
71 struct hurd_userlink *link;
73 /* Continue _longjmp_unwind's job of running the unwind
74 forms for frames being unwound, since we will not
75 return to its loop like this one, which called us. */
76 for (link = ss->active_resources;
77 link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link);
78 link = link->thread.next)
79 if (_hurd_userlink_unlink (link))
81 if (link->cleanup == &_hurdsig_longjmp_from_handler)
83 /* We are unwinding past another signal handler invocation.
84 Just finish the cleanup for this (inner) one, and then
85 swap SCP to restore to the outer context. */
86 cleanup ();
87 scp = link->cleanup_data;
89 else
90 (*link->cleanup) (link->cleanup_data, env, val);
93 #define sc_machine_thread_state paste(sc_,machine_thread_state)
94 #define paste(a,b) paste1(a,b)
95 #define paste1(a,b) a##b
97 /* There are no more unwind forms to be run!
98 Now we can just have the sigreturn do the longjmp for us. */
99 _hurd_longjmp_thread_state
100 ((struct machine_thread_state *) &scp->sc_machine_thread_state,
101 env, val);
103 /* Restore to the same current signal mask. If sigsetjmp saved the
104 mask, longjmp has already restored it as desired; if not, we
105 should leave it as it is. */
106 scp->sc_mask = ss->blocked;
108 /* sigreturn expects the link added by _hurd_setup_sighandler
109 to still be there, but _longjmp_unwind removed it just before
110 calling us. Put it back now so sigreturn can find it. */
111 link = (void *) &scp[1];
112 assert (! link->resource.next && ! link->resource.prevp);
113 assert (link->thread.next == ss->active_resources);
114 assert (link->thread.prevp = &ss->active_resources);
115 if (link->thread.next)
116 link->thread.next->thread.prevp = &link->thread.next;
117 ss->active_resources = link;
119 /* We must momentarily exit the critical section so that sigreturn
120 does not get upset with us. But we don't want signal handlers
121 running right now, because we are presently in the bogus state of
122 having run all the unwind forms back to ENV's frame, but our SP is
123 still inside those unwound frames. */
124 __spin_lock (&ss->lock);
125 __spin_unlock (&ss->critical_section_lock);
126 ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK;
127 __spin_unlock (&ss->lock);
129 /* Restore to the modified signal context that now
130 performs `longjmp (ENV, VAL)'. */
131 __sigreturn (scp);
132 assert (! "sigreturn returned!");
135 /* We are not unwinding off the alternate signal stack. So nothing
136 really funny is going on here. We can just clean up this handler
137 frame and let _longjmp_unwind continue unwinding. */
138 cleanup ();
139 ss->intr_port = scp->sc_intr_port;