remove ~files
[aversive.git] / modules / base / scheduler / scheduler_interrupt.c
1 /*  
2  *  Copyright Droids Corporation, Microb Technology, Eirbot (2005)
3  * 
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: scheduler_interrupt.c,v 1.1.2.9 2009-11-08 17:33:14 zer0 Exp $
19  *
20  */
21
22 #include <stdlib.h>
23
24 #include <aversive.h>
25 #include <scheduler_config.h>
26 #include <scheduler_private.h>
27 #include <scheduler_stats.h>
28
29 /** priority of the running event */
30 static volatile uint8_t priority_running=0;
31
32 /** number of imbricated scheduler interruptions */
33 static volatile uint8_t nb_stacking=0;
34         
35 uint8_t scheduler_disable_save(void)
36 {
37         uint8_t ret;
38         ret = priority_running;
39         priority_running = 255;
40         return ret;
41 }
42
43 void scheduler_enable_restore(uint8_t old_prio)
44 {
45         priority_running = old_prio;
46 }
47
48 /** 
49  *  this function is called from a timer interruption. If an event has
50  *  to be scheduled, it will execute the fonction (IRQ are allowed
51  *  during the execution of the function). This interruption can be
52  *  interrupted by itself too, in this case only events with a higher
53  *  priority can be scheduled.
54  * 
55  *  We assume that this function is called from a SIGNAL(), with
56  *  global interrupt flag disabled --> that's why we can use cli() and
57  *  sei() instead of IRQ_LOCK(flags).
58  */
59 void
60 scheduler_interrupt(void)
61 {
62         uint8_t i;
63         uint8_t priority_tmp;
64         SLIST_HEAD(event_list_t, event_t) event_list;
65         struct event_t *e, *next_e, *prev_e=NULL;
66
67         /* maximize the number of imbrications */
68         if (nb_stacking >= SCHEDULER_NB_STACKING_MAX) {
69                 SCHED_INC_STAT(max_stacking);
70                 return;
71         }
72         
73         nb_stacking ++;
74         sei();
75
76         SLIST_INIT(&event_list);
77
78         /* browse events table to determine which events should be
79          * scheduled */
80         for (i=0 ; i<SCHEDULER_NB_MAX_EVENT ; i++) {
81                 cli();
82
83                 /* the event is already present in a schedule list,
84                  * only update its current time until it reaches 1 */
85                 if (g_tab_event[i].state == SCHEDULER_EVENT_SCHEDULED) {
86                         if (g_tab_event[i].current_time > 1) {
87                                 g_tab_event[i].current_time --;
88                                 sei();
89                                 continue;
90                         }
91                         else {
92                                 SCHED_INC_STAT2(task_delayed, i);
93                                 sei();
94                                 continue;
95                         }
96                 }
97
98                 /* nothing to do with other unactive events */
99                 if (g_tab_event[i].state != SCHEDULER_EVENT_ACTIVE) {
100                         sei();
101                         continue;
102                 }
103
104                 /* decrement current time (we know it is >0 if it is
105                  * in SCHEDULER_EVENT_ACTIVE state */
106                 g_tab_event[i].current_time --;
107                 
108                 /* don't need to schedule now */
109                 if ( g_tab_event[i].current_time != 0 ) {
110                         sei();
111                         continue;
112                 }
113                 
114                 /* time to schedule, but priority is too low,
115                  * delay it */
116                 if (g_tab_event[i].priority <= priority_running) {
117                         g_tab_event[i].current_time = 1;
118                         SCHED_INC_STAT2(task_delayed, i);
119                         sei();
120                         continue;
121                 }
122
123                 /* reload event (it is 0 if it is non-periodical) */
124                 g_tab_event[i].current_time = g_tab_event[i].period;
125
126                 /* schedule it */
127                 g_tab_event[i].state = SCHEDULER_EVENT_SCHEDULED;
128                 sei();
129
130                 /* insert it in the list (list is ordered).
131                    this should be quite fast since the list is
132                    expected to be small. */
133
134                 e = SLIST_FIRST(&event_list);
135                 /* easy case : list is empty */
136                 if (e == NULL) {
137                         SLIST_INSERT_HEAD(&event_list, &g_tab_event[i], next);
138                         continue;
139                 }
140
141                 /* insert at head if it's the event with highest prio */
142                 if (g_tab_event[i].priority >= e->priority) {
143                         SLIST_INSERT_HEAD(&event_list, &g_tab_event[i], next);
144                         continue;
145                 }
146
147                 /* harder : find the good place in list */
148                 SLIST_FOREACH(e, &event_list, next) {
149                         next_e = SLIST_NEXT(e, next);
150                         if (next_e == NULL || 
151                             g_tab_event[i].priority >= next_e->priority) {
152                                 SLIST_INSERT_AFTER(e, &g_tab_event[i], next);
153                                 break;
154                         }
155                 }
156         }
157
158         /* only called if SCHEDULER_DEBUG is defined */
159         DUMP_EVENTS();
160
161         cli();
162         priority_tmp = priority_running;
163
164         SLIST_FOREACH(e, &event_list, next) {
165                 /* remove previous elt from list */
166                 if (prev_e)
167                         SLIST_NEXT(prev_e, next) = NULL;
168
169                 /* set running priority */
170                 priority_running = e->priority;
171                 sei();
172
173                 /* the following fields (f and data) can't be modified
174                  * while an event is in state SCHEDULED */
175                 e->f(e->data);
176
177                 cli();
178                 /* free it if it is single (non-periodical) */
179                 if (!e->period) {
180                         e->state = SCHEDULER_EVENT_FREE;
181                 }
182
183                 /* free event if someone asked for deletion during
184                  * schedule */
185                 if (e->state == SCHEDULER_EVENT_DELETING) {
186                         e->state = SCHEDULER_EVENT_FREE;
187                 }
188                 
189                 /* end of schedule, mark it as active */
190                 if (e->state == SCHEDULER_EVENT_SCHEDULED) {
191                         e->state = SCHEDULER_EVENT_ACTIVE;
192                 }
193
194                 prev_e = e;
195         }
196         /* remove previous elt from list */
197         if (prev_e)
198                 SLIST_NEXT(prev_e, next) = NULL;
199
200         priority_running = priority_tmp;
201         nb_stacking--;
202 }