scheduler stats
[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                 SCHED_INC_STAT2(task_scheduled, i);
129                 sei();
130
131                 /* insert it in the list (list is ordered).
132                    this should be quite fast since the list is
133                    expected to be small. */
134
135                 e = SLIST_FIRST(&event_list);
136                 /* easy case : list is empty */
137                 if (e == NULL) {
138                         SLIST_INSERT_HEAD(&event_list, &g_tab_event[i], next);
139                         continue;
140                 }
141
142                 /* insert at head if it's the event with highest prio */
143                 if (g_tab_event[i].priority >= e->priority) {
144                         SLIST_INSERT_HEAD(&event_list, &g_tab_event[i], next);
145                         continue;
146                 }
147
148                 /* harder : find the good place in list */
149                 SLIST_FOREACH(e, &event_list, next) {
150                         next_e = SLIST_NEXT(e, next);
151                         if (next_e == NULL ||
152                             g_tab_event[i].priority >= next_e->priority) {
153                                 SLIST_INSERT_AFTER(e, &g_tab_event[i], next);
154                                 break;
155                         }
156                 }
157         }
158
159         /* only called if SCHEDULER_DEBUG is defined */
160         DUMP_EVENTS();
161
162         cli();
163         priority_tmp = priority_running;
164
165         SLIST_FOREACH(e, &event_list, next) {
166                 /* remove previous elt from list */
167                 if (prev_e)
168                         SLIST_NEXT(prev_e, next) = NULL;
169
170                 /* set running priority */
171                 priority_running = e->priority;
172                 sei();
173
174                 /* the following fields (f and data) can't be modified
175                  * while an event is in state SCHEDULED */
176                 e->f(e->data);
177
178                 cli();
179                 /* free it if it is single (non-periodical) */
180                 if (!e->period) {
181                         e->state = SCHEDULER_EVENT_FREE;
182                 }
183
184                 /* free event if someone asked for deletion during
185                  * schedule */
186                 if (e->state == SCHEDULER_EVENT_DELETING) {
187                         e->state = SCHEDULER_EVENT_FREE;
188                 }
189
190                 /* end of schedule, mark it as active */
191                 if (e->state == SCHEDULER_EVENT_SCHEDULED) {
192                         e->state = SCHEDULER_EVENT_ACTIVE;
193                 }
194
195                 prev_e = e;
196         }
197         /* remove previous elt from list */
198         if (prev_e)
199                 SLIST_NEXT(prev_e, next) = NULL;
200
201         priority_running = priority_tmp;
202         nb_stacking--;
203 }