Ref gdb/11763 - can't stop a running simulator:
[external/binutils.git] / sim / common / sim-events.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program 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
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  
19     */
20
21
22 #ifndef _SIM_EVENTS_C_
23 #define _SIM_EVENTS_C_
24
25 #include "sim-main.h"
26 #include "sim-assert.h"
27
28 #include <signal.h>
29
30
31 /* The event queue maintains a single absolute time using two
32    variables.
33    
34    TIME_OF_EVENT: this holds the time at which the next event is ment
35    to occure.  If no next event it will hold the time of the last
36    event.
37
38    TIME_FROM_EVENT: The current distance from TIME_OF_EVENT.  If an
39    event is pending, this will be positive.  If no future event is
40    pending this will be negative.  This variable is decremented once
41    for each iteration of a clock cycle.
42
43    Initially, the clock is started at time one (0) with TIME_OF_EVENT
44    == 0 and TIME_FROM_EVENT == 0.
45
46    Clearly there is a bug in that this code assumes that the absolute
47    time counter will never become greater than 2^62.
48
49    To avoid the need to use 64bit arithmetic, the event queue always
50    contains at least one event scheduled every 16 000 ticks.  This
51    limits the time from event counter to values less than
52    16 000. */
53
54
55 #if !defined (SIM_EVENTS_POLL_RATE)
56 #define SIM_EVENTS_POLL_RATE 0x4000
57 #endif
58
59
60 #define _ETRACE sd
61
62 #undef ETRACE
63 #define ETRACE(ARGS) \
64 do \
65   { \
66     if (WITH_TRACE) \
67       { \
68         if (sd->events.trace) \
69           { \
70             const char *file; \
71             SIM_FILTER_PATH(file, __FILE__); \
72             sim_io_printf (sd, "%s:%d: ", file, __LINE__); \
73             sim_io_printf  ARGS; \
74           } \
75       } \
76   } \
77 while (0)
78
79
80 STATIC_INLINE_SIM_EVENTS\
81 (void)
82 sim_events_poll (void *data)
83 {
84   /* just re-schedule in 1000 million ticks time */
85   SIM_DESC sd = data;
86   sim_events_schedule(sd, SIM_EVENTS_POLL_RATE, sim_events_poll, sd);
87   sim_io_poll_quit (sd);
88 }
89
90
91 INLINE_SIM_EVENTS\
92 (void)
93 sim_events_init(SIM_DESC sd)
94 {
95   sim_events *events = &sd->events;
96   sim_event *event;
97
98   /* drain the interrupt queue */
99   {
100 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
101     sigset_t old_mask;
102     sigset_t new_mask;
103     sigfillset(&new_mask);
104     /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
105 #endif
106     event = events->held;
107     while (event != NULL) {
108       sim_event *dead = event;
109       event = event->next;
110       zfree(dead);
111     }
112     events->held = NULL;
113     events->held_end = &events->held;
114 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
115     /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
116 #endif
117   }
118
119   /* drain the normal queue */
120   event = events->queue;
121   while (event != NULL) {
122     sim_event *dead = event;
123     event = event->next;
124     zfree(dead);
125   }
126   events->queue = NULL;
127     
128   /* wind time back to zero */
129   events->processing = 0;
130   events->time_of_event = 0;
131   events->time_from_event = 0;
132
133   /* schedule our initial counter event */
134   sim_events_schedule(sd, 0, sim_events_poll, sd);
135
136   /* from now on, except when the large-int event is being processed
137      the event queue is non empty */
138   SIM_ASSERT(events->queue != NULL);
139 }
140
141 INLINE_SIM_EVENTS\
142 (signed64)
143 sim_events_time(SIM_DESC sd)
144 {
145   sim_events *events = &sd->events;
146   return events->time_of_event - events->time_from_event;
147 }
148
149 STATIC_INLINE_SIM_EVENTS\
150 (void)
151 update_time_from_event(SIM_DESC sd)
152 {
153   sim_events *events = &sd->events;
154   signed64 current_time = sim_events_time(sd);
155   if (events->queue != NULL) {
156     events->time_from_event = (events->queue->time_of_event - current_time);
157     events->time_of_event = events->queue->time_of_event;
158   }
159   else {
160     events->time_of_event = current_time - 1;
161     events->time_from_event = -1;
162   }
163   SIM_ASSERT(current_time == sim_events_time (sd));
164   SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
165 }
166
167 STATIC_INLINE_SIM_EVENTS\
168 (void)
169 insert_sim_event(SIM_DESC sd,
170                  sim_event *new_event,
171                  signed64 delta)
172 {
173   sim_events *events = &sd->events;
174   sim_event *curr;
175   sim_event **prev;
176   signed64 time_of_event;
177
178   if (delta < 0)
179     engine_error (sd, "what is past is past!\n");
180
181   /* compute when the event should occure */
182   time_of_event = sim_events_time(sd) + delta;
183
184   /* find the queue insertion point - things are time ordered */
185   prev = &events->queue;
186   curr = events->queue;
187   while (curr != NULL && time_of_event >= curr->time_of_event) {
188     SIM_ASSERT(curr->next == NULL
189            || curr->time_of_event <= curr->next->time_of_event);
190     prev = &curr->next;
191     curr = curr->next;
192   }
193   SIM_ASSERT(curr == NULL || time_of_event < curr->time_of_event);
194
195   /* insert it */
196   new_event->next = curr;
197   *prev = new_event;
198   new_event->time_of_event = time_of_event;
199
200   /* adjust the time until the first event */
201   update_time_from_event(sd);
202 }
203
204 INLINE_SIM_EVENTS\
205 (sim_event *)
206 sim_events_schedule(SIM_DESC sd,
207                     signed64 delta_time,
208                     sim_event_handler *handler,
209                     void *data)
210 {
211   sim_event *new_event = ZALLOC(sim_event);
212   new_event->data = data;
213   new_event->handler = handler;
214   insert_sim_event(sd, new_event, delta_time);
215   ETRACE((_ETRACE,
216           "event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
217           (long)sim_events_time(sd),
218           (long)new_event,
219           (long)new_event->time_of_event,
220           (long)new_event->handler,
221           (long)new_event->data));
222   return new_event;
223 }
224
225
226 INLINE_SIM_EVENTS\
227 (sim_event *)
228 sim_events_schedule_after_signal(SIM_DESC sd,
229                                  signed64 delta_time,
230                                  sim_event_handler *handler,
231                                  void *data)
232 {
233   sim_events *events = &sd->events;
234   sim_event *new_event = ZALLOC(sim_event);
235
236   new_event->data = data;
237   new_event->handler = handler;
238   new_event->time_of_event = delta_time; /* work it out later */
239   new_event->next = NULL;
240
241   {
242 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
243     sigset_t old_mask;
244     sigset_t new_mask;
245     sigfillset(&new_mask);
246     /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
247 #endif
248     if (events->held == NULL) {
249       events->held = new_event;
250     }
251     else {
252       *events->held_end = new_event;
253     }
254     events->held_end = &new_event->next;
255 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
256     /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
257 #endif
258   }
259
260   ETRACE((_ETRACE,
261           "event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
262           (long)sim_events_time(sd),
263           (long)new_event,
264           (long)new_event->time_of_event,
265           (long)new_event->handler,
266           (long)new_event->data));
267
268   return new_event;
269 }
270
271
272 INLINE_SIM_EVENTS\
273 (void)
274 sim_events_deschedule(SIM_DESC sd,
275                       sim_event *event_to_remove)
276 {
277   sim_events *events = &sd->events;
278   sim_event *to_remove = (sim_event*)event_to_remove;
279   SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
280   if (event_to_remove != NULL) {
281     sim_event *current;
282     sim_event **ptr_to_current;
283     for (ptr_to_current = &events->queue, current = *ptr_to_current;
284          current != NULL && current != to_remove;
285          ptr_to_current = &current->next, current = *ptr_to_current);
286     if (current == to_remove) {
287       *ptr_to_current = current->next;
288       ETRACE((_ETRACE,
289               "event descheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
290               (long)sim_events_time(sd),
291               (long)event_to_remove,
292               (long)current->time_of_event,
293               (long)current->handler,
294               (long)current->data));
295       zfree(current);
296       update_time_from_event(sd);
297     }
298     else {
299       ETRACE((_ETRACE,
300               "event descheduled at %ld - tag 0x%lx - not found\n",
301               (long)sim_events_time(sd),
302               (long)event_to_remove));
303     }
304   }
305   SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
306 }
307
308
309
310
311 INLINE_SIM_EVENTS\
312 (int)
313 sim_events_tick(SIM_DESC sd)
314 {
315   sim_events *events = &sd->events;
316
317   /* we should only be here when the previous tick has been fully
318      processed */
319   SIM_ASSERT(!events->processing && events->queue != NULL);
320
321   /* Advance the time but *only* if there is nothing to process */
322   if (events->time_from_event == 0)
323     return 1;
324   else if (events->held != NULL)
325     return 1;
326   else {
327     events->time_from_event -= 1;
328     return 0;
329   }
330 }
331
332
333
334 INLINE_SIM_EVENTS\
335 (void)
336 sim_events_process(SIM_DESC sd)
337 {
338   sim_events *events = &sd->events;
339   signed64 event_time = sim_events_time(sd);
340
341   /* something to do */
342   SIM_ASSERT(events->time_from_event == 0 || events->held != NULL);
343   SIM_ASSERT(events->queue != NULL);
344
345   /* move any events that were queued by any signal handlers onto the
346      real event queue.  */
347   if (events->held != NULL) {
348     sim_event *held_events;
349     sim_event *curr_event;
350
351 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
352     /*-LOCK-*/
353     sigset_t old_mask;
354     sigset_t new_mask;
355     sigfillset(&new_mask);
356     sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
357 #endif
358
359     held_events = events->held;
360     events->held = NULL;
361     events->held_end = &events->held;
362
363 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
364     /*-UNLOCK-*/
365     sigprocmask(SIG_SETMASK, &old_mask, NULL);
366 #endif
367
368     do {
369       curr_event = held_events;
370       held_events = curr_event->next;
371       insert_sim_event(sd, curr_event, curr_event->time_of_event);
372     } while (held_events != NULL);
373   }
374
375   /* consume all events for this or earlier times.  Be careful to
376      allow a new event to appear under our feet */
377   events->processing = 1;
378   while (events->queue->time_of_event <= event_time) {
379     sim_event *to_do = events->queue;
380     sim_event_handler *handler = to_do->handler;
381     void *data = to_do->data;
382     events->queue = to_do->next;
383     ETRACE((_ETRACE,
384             "event issued at %ld - tag 0x%lx - handler 0x%lx, data 0x%lx\n",
385             (long)event_time,
386             (long)to_do,
387             (long)handler,
388             (long)data));
389     zfree (to_do);
390     handler (data);
391   }
392   events->processing = 0;
393
394   /* re-caculate time for new events - advance the time */
395   update_time_from_event(sd);
396   SIM_ASSERT(events->time_from_event > 0 && events->queue != NULL);
397   events->time_from_event -= 1;
398 }
399
400 #endif