Initial creation of sourceware repository
[external/binutils.git] / sim / ppc / events.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1998, 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 _EVENTS_C_
23 #define _EVENTS_C_
24
25 #include "basics.h"
26 #include "events.h"
27
28 #include <signal.h>
29
30 #if !defined (SIM_EVENTS_POLL_RATE)
31 #define SIM_EVENTS_POLL_RATE 0x1000
32 #endif
33
34
35
36 /* The event queue maintains a single absolute time using two
37    variables.
38    
39    TIME_OF_EVENT: this holds the time at which the next event is ment
40    to occure.  If no next event it will hold the time of the last
41    event.
42
43    TIME_FROM_EVENT: The current distance from TIME_OF_EVENT.  If an
44    event is pending, this will be positive.  If no future event is
45    pending this will be negative.  This variable is decremented once
46    for each iteration of a clock cycle.
47
48    Initially, the clock is started at time one (1) with TIME_OF_EVENT
49    == 0 and TIME_FROM_EVENT == -1.
50
51    Clearly there is a bug in that this code assumes that the absolute
52    time counter will never become greater than 2^62. */
53
54 typedef struct _event_entry event_entry;
55 struct _event_entry {
56   void *data;
57   event_handler *handler;
58   signed64 time_of_event;  
59   event_entry *next;
60 };
61
62 struct _event_queue {
63   int processing;
64   event_entry *queue;
65   event_entry *volatile held;
66   event_entry *volatile *volatile held_end;
67   signed64 time_of_event;
68   signed64 time_from_event;
69 };
70
71
72 STATIC_INLINE_EVENTS\
73 (void)
74 sim_events_poll (void *data)
75 {
76   event_queue *queue = data;
77   /* just re-schedule in 1000 million ticks time */
78   event_queue_schedule (queue, SIM_EVENTS_POLL_RATE, sim_events_poll, queue);
79   sim_io_poll_quit ();
80 }
81
82
83 INLINE_EVENTS\
84 (event_queue *)
85 event_queue_create(void)
86 {
87   event_queue *new_event_queue = ZALLOC(event_queue);
88
89   new_event_queue->processing = 0;
90   new_event_queue->queue = NULL;
91   new_event_queue->held = NULL;
92   new_event_queue->held_end = &new_event_queue->held;
93
94   /* both times are already zero */
95   return new_event_queue;
96 }
97
98
99 INLINE_EVENTS\
100 (void)
101 event_queue_init(event_queue *queue)
102 {
103   event_entry *event;
104
105   /* drain the interrupt queue */
106   {
107 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
108     sigset_t old_mask;
109     sigset_t new_mask;
110     sigfillset(&new_mask);
111     /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
112 #endif
113     event = queue->held;
114     while (event != NULL) {
115       event_entry *dead = event;
116       event = event->next;
117       zfree(dead);
118     }
119     queue->held = NULL;
120     queue->held_end = &queue->held;
121 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
122     /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
123 #endif
124   }
125
126   /* drain the normal queue */
127   event = queue->queue;
128   while (event != NULL) {
129     event_entry *dead = event;
130     event = event->next;
131     zfree(dead);
132   }
133   queue->queue = NULL;
134     
135   /* wind time back to one */
136   queue->processing = 0;
137   queue->time_of_event = 0;
138   queue->time_from_event = -1;
139
140   /* schedule our initial counter event */
141   event_queue_schedule (queue, 0, sim_events_poll, queue);
142 }
143
144 INLINE_EVENTS\
145 (signed64)
146 event_queue_time(event_queue *queue)
147 {
148   return queue->time_of_event - queue->time_from_event;
149 }
150
151 STATIC_INLINE_EVENTS\
152 (void)
153 update_time_from_event(event_queue *events)
154 {
155   signed64 current_time = event_queue_time(events);
156   if (events->queue != NULL) {
157     events->time_from_event = (events->queue->time_of_event - current_time);
158     events->time_of_event = events->queue->time_of_event;
159   }
160   else {
161     events->time_of_event = current_time - 1;
162     events->time_from_event = -1;
163   }
164   ASSERT(current_time == event_queue_time(events));
165   ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
166 }
167
168 STATIC_INLINE_EVENTS\
169 (void)
170 insert_event_entry(event_queue *events,
171                    event_entry *new_event,
172                    signed64 delta)
173 {
174   event_entry *curr;
175   event_entry **prev;
176   signed64 time_of_event;
177
178   if (delta < 0)
179     error("what is past is past!\n");
180
181   /* compute when the event should occure */
182   time_of_event = event_queue_time(events) + 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     ASSERT(curr->next == NULL
189            || curr->time_of_event <= curr->next->time_of_event);
190     prev = &curr->next;
191     curr = curr->next;
192   }
193   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(events);
202 }
203
204 INLINE_EVENTS\
205 (event_entry_tag)
206 event_queue_schedule(event_queue *events,
207                      signed64 delta_time,
208                      event_handler *handler,
209                      void *data)
210 {
211   event_entry *new_event = ZALLOC(event_entry);
212   new_event->data = data;
213   new_event->handler = handler;
214   insert_event_entry(events, new_event, delta_time);
215   TRACE(trace_events, ("event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
216                        (long)event_queue_time(events),
217                        (long)new_event,
218                        (long)new_event->time_of_event,
219                        (long)new_event->handler,
220                        (long)new_event->data));
221   return (event_entry_tag)new_event;
222 }
223
224
225 INLINE_EVENTS\
226 (event_entry_tag)
227 event_queue_schedule_after_signal(event_queue *events,
228                                   signed64 delta_time,
229                                   event_handler *handler,
230                                   void *data)
231 {
232   event_entry *new_event = ZALLOC(event_entry);
233
234   new_event->data = data;
235   new_event->handler = handler;
236   new_event->time_of_event = delta_time; /* work it out later */
237   new_event->next = NULL;
238
239   {
240 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
241     sigset_t old_mask;
242     sigset_t new_mask;
243     sigfillset(&new_mask);
244     /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
245 #endif
246     if (events->held == NULL) {
247       events->held = new_event;
248     }
249     else {
250       *events->held_end = new_event;
251     }
252     events->held_end = &new_event->next;
253 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
254     /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
255 #endif
256   }
257
258   TRACE(trace_events, ("event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
259                        (long)event_queue_time(events),
260                        (long)new_event,
261                        (long)new_event->time_of_event,
262                        (long)new_event->handler,
263                        (long)new_event->data));
264
265   return (event_entry_tag)new_event;
266 }
267
268
269 INLINE_EVENTS\
270 (void)
271 event_queue_deschedule(event_queue *events,
272                        event_entry_tag event_to_remove)
273 {
274   event_entry *to_remove = (event_entry*)event_to_remove;
275   ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
276   if (event_to_remove != NULL) {
277     event_entry *current;
278     event_entry **ptr_to_current;
279     for (ptr_to_current = &events->queue, current = *ptr_to_current;
280          current != NULL && current != to_remove;
281          ptr_to_current = &current->next, current = *ptr_to_current);
282     if (current == to_remove) {
283       *ptr_to_current = current->next;
284       TRACE(trace_events, ("event descheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
285                            (long)event_queue_time(events),
286                            (long)event_to_remove,
287                            (long)current->time_of_event,
288                            (long)current->handler,
289                            (long)current->data));
290       zfree(current);
291       update_time_from_event(events);
292     }
293     else {
294       TRACE(trace_events, ("event descheduled at %ld - tag 0x%lx - not found\n",
295                            (long)event_queue_time(events),
296                            (long)event_to_remove));
297     }
298   }
299   ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
300 }
301
302
303
304
305 INLINE_EVENTS\
306 (int)
307 event_queue_tick(event_queue *events)
308 {
309   signed64 time_from_event;
310
311   /* we should only be here when the previous tick has been fully processed */
312   ASSERT(!events->processing);
313
314   /* move any events that were queued by any signal handlers onto the
315      real event queue.  BTW: When inlining, having this code here,
316      instead of in event_queue_process() causes GCC to put greater
317      weight on keeping the pointer EVENTS in a register.  This, in
318      turn results in better code being output. */
319   if (events->held != NULL) {
320     event_entry *held_events;
321     event_entry *curr_event;
322
323     {
324 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
325       sigset_t old_mask;
326       sigset_t new_mask;
327       sigfillset(&new_mask);
328       /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
329 #endif
330       held_events = events->held;
331       events->held = NULL;
332       events->held_end = &events->held;
333 #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
334       /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
335 #endif
336     }
337
338     do {
339       curr_event = held_events;
340       held_events = curr_event->next;
341       insert_event_entry(events, curr_event, curr_event->time_of_event);
342     } while (held_events != NULL);
343   }
344
345   /* advance time, checking to see if we've reached time zero which
346      would indicate the time for the next event has arrived */
347   time_from_event = events->time_from_event;
348   events->time_from_event = time_from_event - 1;
349   return time_from_event == 0;
350 }
351
352
353
354 INLINE_EVENTS\
355 (void)
356 event_queue_process(event_queue *events)
357 {
358   signed64 event_time = event_queue_time(events);
359
360   ASSERT((events->time_from_event == -1 && events->queue != NULL)
361          || events->processing); /* something to do */
362
363   /* consume all events for this or earlier times.  Be careful to
364      allow a new event to appear under our feet */
365   events->processing = 1;
366   while (events->queue != NULL
367          && events->queue->time_of_event <= event_time) {
368     event_entry *to_do = events->queue;
369     event_handler *handler = to_do->handler;
370     void *data = to_do->data;
371     events->queue = to_do->next;
372     TRACE(trace_events, ("event issued at %ld - tag 0x%lx - handler 0x%lx, data 0x%lx\n",
373                          (long)event_time,
374                          (long)to_do,
375                          (long)handler,
376                          (long)data));
377     zfree(to_do);
378     handler(data);
379   }
380   events->processing = 0;
381
382   /* re-caculate time for new events */
383   update_time_from_event(events);
384 }
385
386
387 #endif /* _EVENTS_C_ */