2011-05-27 Pedro Alves <pedro@codesourcery.com>
[external/binutils.git] / gdb / continuations.c
1 /* Continuations for GDB, the GNU debugger.
2
3    Copyright (C) 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
4    1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
5    2009, 2010, 2011 Free Software Foundation, Inc.
6
7    This file is part of GDB.
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21
22 #include "defs.h"
23 #include "gdbthread.h"
24 #include "inferior.h"
25
26 struct continuation
27 {
28   struct continuation *next;
29   void (*function) (void *);
30   void (*free_arg) (void *);
31   void *arg;
32 };
33
34 typedef void (make_continuation_ftype) (void *);
35
36 /* Add a new continuation to the continuation chain, and return the
37    previous chain pointer to be passed later to do_continuations or
38    discard_continuations.  Args are FUNCTION to run the continuation
39    up with, and ARG to pass to it.  */
40
41 static struct continuation *
42 make_continuation (struct continuation **pmy_chain,
43                    make_continuation_ftype *function,
44                    void *arg,  void (*free_arg) (void *))
45 {
46   struct continuation *new = XNEW (struct continuation);
47   struct continuation *old_chain = *pmy_chain;
48
49   new->next = *pmy_chain;
50   new->function = function;
51   new->free_arg = free_arg;
52   new->arg = arg;
53   *pmy_chain = new;
54
55   return old_chain;
56 }
57
58 static void
59 do_my_continuations (struct continuation **pmy_chain,
60                      struct continuation *old_chain)
61 {
62   struct continuation *ptr;
63
64   while ((ptr = *pmy_chain) != old_chain)
65     {
66       *pmy_chain = ptr->next;   /* Do this first in case of recursion.  */
67       (*ptr->function) (ptr->arg);
68       if (ptr->free_arg)
69         (*ptr->free_arg) (ptr->arg);
70       xfree (ptr);
71     }
72 }
73
74 void
75 discard_my_continuations (struct continuation **pmy_chain,
76                           struct continuation *old_chain)
77 {
78   struct continuation *ptr;
79
80   while ((ptr = *pmy_chain) != old_chain)
81     {
82       *pmy_chain = ptr->next;
83       if (ptr->free_arg)
84         (*ptr->free_arg) (ptr->arg);
85       xfree (ptr);
86     }
87 }
88
89 /* Add a continuation to the continuation list of THREAD.  The new
90    continuation will be added at the front.  */
91
92 void
93 add_continuation (struct thread_info *thread,
94                   void (*continuation_hook) (void *), void *args,
95                   void (*continuation_free_args) (void *))
96 {
97   struct continuation *continuations = thread->continuations;
98   make_cleanup_ftype *continuation_hook_fn = continuation_hook;
99
100   make_continuation (&continuations,
101                      continuation_hook_fn,
102                      args,
103                      continuation_free_args);
104
105   thread->continuations = continuations;
106 }
107
108 /* Add a continuation to the continuation list of INFERIOR.  The new
109    continuation will be added at the front.  */
110
111 void
112 add_inferior_continuation (void (*continuation_hook) (void *), void *args,
113                            void (*continuation_free_args) (void *))
114 {
115   struct inferior *inf = current_inferior ();
116   struct continuation *continuations = inf->continuations;
117   make_cleanup_ftype *continuation_hook_fn = continuation_hook;
118
119   make_continuation (&continuations,
120                      continuation_hook_fn,
121                      args,
122                      continuation_free_args);
123
124   inf->continuations = continuations;
125 }
126
127 /* Do all continuations of the current inferior.  */
128
129 void
130 do_all_inferior_continuations (void)
131 {
132   struct continuation *continuations;
133   struct inferior *inf = current_inferior ();
134
135   if (inf->continuations == NULL)
136     return;
137
138   /* Copy the list header into another pointer, and set the global
139      list header to null, so that the global list can change as a side
140      effect of invoking the continuations and the processing of the
141      preexisting continuations will not be affected.  */
142
143   continuations = inf->continuations;
144   inf->continuations = NULL;
145
146   /* Work now on the list we have set aside.  */
147   do_my_continuations (&continuations, NULL);
148 }
149
150 /* Get rid of all the inferior-wide continuations of INF.  */
151
152 void
153 discard_all_inferior_continuations (struct inferior *inf)
154 {
155   struct continuation *continuation_ptr = inf->continuations;
156
157   discard_my_continuations (&continuation_ptr, NULL);
158   inf->continuations = NULL;
159 }
160
161 static void
162 restore_thread_cleanup (void *arg)
163 {
164   ptid_t *ptid_p = arg;
165
166   switch_to_thread (*ptid_p);
167 }
168
169 /* Walk down the continuation list of PTID, and execute all the
170    continuations.  There is a problem though.  In some cases new
171    continuations may be added while we are in the middle of this loop.
172    If this happens they will be added in the front, and done before we
173    have a chance of exhausting those that were already there.  We need
174    to then save the beginning of the list in a pointer and do the
175    continuations from there on, instead of using the global beginning
176    of list as our iteration pointer.  */
177
178 static void
179 do_all_continuations_ptid (ptid_t ptid,
180                            struct continuation **continuations_p)
181 {
182   struct cleanup *old_chain;
183   struct continuation *continuations;
184   ptid_t current_thread;
185
186   if (*continuations_p == NULL)
187     return;
188
189   current_thread = inferior_ptid;
190
191   /* Restore selected thread on exit.  Don't try to restore the frame
192      as well, because:
193
194      - When running continuations, the selected frame is always #0.
195
196      - The continuations may trigger symbol file loads, which may
197      change the frame layout (frame ids change), which would trigger
198      a warning if we used make_cleanup_restore_current_thread.  */
199
200   old_chain = make_cleanup (restore_thread_cleanup, &current_thread);
201
202   /* Let the continuation see this thread as selected.  */
203   switch_to_thread (ptid);
204
205   /* Copy the list header into another pointer, and set the global
206      list header to null, so that the global list can change as a side
207      effect of invoking the continuations and the processing of the
208      preexisting continuations will not be affected.  */
209
210   continuations = *continuations_p;
211   *continuations_p = NULL;
212
213   /* Work now on the list we have set aside.  */
214   do_my_continuations (&continuations, NULL);
215
216   do_cleanups (old_chain);
217 }
218
219 /* Callback for iterate over threads.  */
220
221 static int
222 do_all_continuations_thread_callback (struct thread_info *thread, void *data)
223 {
224   do_all_continuations_ptid (thread->ptid, &thread->continuations);
225   return 0;
226 }
227
228 /* Do all continuations of thread THREAD.  */
229
230 void
231 do_all_continuations_thread (struct thread_info *thread)
232 {
233   do_all_continuations_thread_callback (thread, NULL);
234 }
235
236 /* Do all continuations of all threads.  */
237
238 void
239 do_all_continuations (void)
240 {
241   iterate_over_threads (do_all_continuations_thread_callback, NULL);
242 }
243
244 /* Callback for iterate over threads.  */
245
246 static int
247 discard_all_continuations_thread_callback (struct thread_info *thread,
248                                            void *data)
249 {
250   struct continuation *continuation_ptr = thread->continuations;
251
252   discard_my_continuations (&continuation_ptr, NULL);
253   thread->continuations = NULL;
254   return 0;
255 }
256
257 /* Get rid of all the continuations of THREAD.  */
258
259 void
260 discard_all_continuations_thread (struct thread_info *thread)
261 {
262   discard_all_continuations_thread_callback (thread, NULL);
263 }
264
265 /* Get rid of all the continuations of all threads.  */
266
267 void
268 discard_all_continuations (void)
269 {
270   iterate_over_threads (discard_all_continuations_thread_callback, NULL);
271 }
272
273
274 /* Add a continuation to the intermediate continuation list of THREAD.
275    The new continuation will be added at the front.  */
276
277 void
278 add_intermediate_continuation (struct thread_info *thread,
279                                void (*continuation_hook)
280                                (void *), void *args,
281                                void (*continuation_free_args) (void *))
282 {
283   struct continuation *continuations = thread->intermediate_continuations;
284   make_cleanup_ftype *continuation_hook_fn = continuation_hook;
285
286   make_continuation (&continuations,
287                      continuation_hook_fn,
288                      args,
289                      continuation_free_args);
290
291   thread->intermediate_continuations = continuations;
292 }
293
294 /* Walk down the cmd_continuation list, and execute all the
295    continuations.  There is a problem though.  In some cases new
296    continuations may be added while we are in the middle of this
297    loop.  If this happens they will be added in the front, and done
298    before we have a chance of exhausting those that were already
299    there.  We need to then save the beginning of the list in a pointer
300    and do the continuations from there on, instead of using the
301    global beginning of list as our iteration pointer.  */
302
303 static int
304 do_all_intermediate_continuations_thread_callback (struct thread_info *thread,
305                                                    void *data)
306 {
307   do_all_continuations_ptid (thread->ptid,
308                              &thread->intermediate_continuations);
309   return 0;
310 }
311
312 /* Do all intermediate continuations of thread THREAD.  */
313
314 void
315 do_all_intermediate_continuations_thread (struct thread_info *thread)
316 {
317   do_all_intermediate_continuations_thread_callback (thread, NULL);
318 }
319
320 /* Do all intermediate continuations of all threads.  */
321
322 void
323 do_all_intermediate_continuations (void)
324 {
325   iterate_over_threads (do_all_intermediate_continuations_thread_callback,
326                         NULL);
327 }
328
329 /* Callback for iterate over threads.  */
330
331 static int
332 discard_all_intermediate_continuations_thread_callback (struct thread_info *thread,
333                                                         void *data)
334 {
335   struct continuation *continuation_ptr = thread->intermediate_continuations;
336
337   discard_my_continuations (&continuation_ptr, NULL);
338   thread->intermediate_continuations = NULL;
339   return 0;
340 }
341
342 /* Get rid of all the intermediate continuations of THREAD.  */
343
344 void
345 discard_all_intermediate_continuations_thread (struct thread_info *thread)
346 {
347   discard_all_intermediate_continuations_thread_callback (thread, NULL);
348 }
349
350 /* Get rid of all the intermediate continuations of all threads.  */
351
352 void
353 discard_all_intermediate_continuations (void)
354 {
355   iterate_over_threads (discard_all_intermediate_continuations_thread_callback,
356                         NULL);
357 }