blob: 1f567b9240f6fadcbb977613560aa436f8e9c5db [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Task management functions.
3 *
4 * Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau2dd0d472006-06-29 17:53:05 +020013#include <common/config.h>
14#include <common/mini-clist.h>
15#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016
17#include <proto/task.h>
18
19
20/* FIXME : this should be removed very quickly ! */
21extern int maintain_proxies(void);
22
23void **pool_task= NULL;
24struct task *rq = NULL; /* global run queue */
25struct task wait_queue[2] = { /* global wait queue */
26 {
27 prev:LIST_HEAD(wait_queue[0]), /* expirable tasks */
28 next:LIST_HEAD(wait_queue[0]),
29 },
30 {
31 prev:LIST_HEAD(wait_queue[1]), /* non-expirable tasks */
32 next:LIST_HEAD(wait_queue[1]),
33 },
34};
35
36
37/* inserts <task> into its assigned wait queue, where it may already be. In this case, it
38 * may be only moved or left where it was, depending on its timing requirements.
39 * <task> is returned.
40 */
41struct task *task_queue(struct task *task)
42{
43 struct task *list = task->wq;
44 struct task *start_from;
45
46 /* This is a very dirty hack to queue non-expirable tasks in another queue
47 * in order to avoid pulluting the tail of the standard queue. This will go
48 * away with the new O(log(n)) scheduler anyway.
49 */
50 if (tv_iseternity(&task->expire)) {
51 /* if the task was queued in the standard wait queue, we must dequeue it */
52 if (task->prev) {
53 if (task->wq == LIST_HEAD(wait_queue[1]))
54 return task;
55 else {
56 task_delete(task);
57 task->prev = NULL;
58 }
59 }
60 list = task->wq = LIST_HEAD(wait_queue[1]);
61 } else {
62 /* if the task was queued in the eternity queue, we must dequeue it */
63 if (task->prev && (task->wq == LIST_HEAD(wait_queue[1]))) {
64 task_delete(task);
65 task->prev = NULL;
66 list = task->wq = LIST_HEAD(wait_queue[0]);
67 }
68 }
69
70 /* next, test if the task was already in a list */
71 if (task->prev == NULL) {
72 // start_from = list;
73 start_from = list->prev;
74 /* insert the unlinked <task> into the list, searching back from the last entry */
75 while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
76 start_from = start_from->prev;
77 }
78
79 // while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
80 // start_from = start_from->next;
81 // stats_tsk_nsrch++;
82 // }
83 }
84 else if (task->prev == list ||
85 tv_cmp2(&task->expire, &task->prev->expire) >= 0) { /* walk right */
86 start_from = task->next;
87 if (start_from == list || tv_cmp2(&task->expire, &start_from->expire) <= 0) {
88 return task; /* it's already in the right place */
89 }
90
91 /* if the task is not at the right place, there's little chance that
92 * it has only shifted a bit, and it will nearly always be queued
93 * at the end of the list because of constant timeouts
94 * (observed in real case).
95 */
96#ifndef WE_REALLY_THINK_THAT_THIS_TASK_MAY_HAVE_SHIFTED
97 start_from = list->prev; /* assume we'll queue to the end of the list */
98 while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
99 start_from = start_from->prev;
100 }
101#else /* WE_REALLY_... */
102 /* insert the unlinked <task> into the list, searching after position <start_from> */
103 while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
104 start_from = start_from->next;
105 }
106#endif /* WE_REALLY_... */
107
108 /* we need to unlink it now */
109 task_delete(task);
110 }
111 else { /* walk left. */
112#ifdef LEFT_TO_TOP /* not very good */
113 start_from = list;
114 while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
115 start_from = start_from->next;
116 }
117#else
118 start_from = task->prev->prev; /* valid because of the previous test above */
119 while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
120 start_from = start_from->prev;
121 }
122#endif
123 /* we need to unlink it now */
124 task_delete(task);
125 }
126 task->prev = start_from;
127 task->next = start_from->next;
128 task->next->prev = task;
129 start_from->next = task;
130 return task;
131}
132
133/*
134 * This does 4 things :
135 * - wake up all expired tasks
136 * - call all runnable tasks
137 * - call maintain_proxies() to enable/disable the listeners
138 * - return the delay till next event in ms, -1 = wait indefinitely
139 * Note: this part should be rewritten with the O(ln(n)) scheduler.
140 *
141 */
142
143int process_runnable_tasks()
144{
145 int next_time;
146 int time2;
147 struct task *t, *tnext;
148
149 next_time = TIME_ETERNITY; /* set the timer to wait eternally first */
150
151 /* look for expired tasks and add them to the run queue.
152 */
153 tnext = ((struct task *)LIST_HEAD(wait_queue[0]))->next;
154 while ((t = tnext) != LIST_HEAD(wait_queue[0])) { /* we haven't looped ? */
155 tnext = t->next;
156 if (t->state & TASK_RUNNING)
157 continue;
158
159 if (tv_iseternity(&t->expire))
160 continue;
161
162 /* wakeup expired entries. It doesn't matter if they are
163 * already running because of a previous event
164 */
165 if (tv_cmp_ms(&t->expire, &now) <= 0) {
166 task_wakeup(&rq, t);
167 }
168 else {
169 /* first non-runnable task. Use its expiration date as an upper bound */
170 int temp_time = tv_remain(&now, &t->expire);
171 if (temp_time)
172 next_time = temp_time;
173 break;
174 }
175 }
176
177 /* process each task in the run queue now. Each task may be deleted
178 * since we only use the run queue's head. Note that any task can be
179 * woken up by any other task and it will be processed immediately
180 * after as it will be queued on the run queue's head.
181 */
182 while ((t = rq) != NULL) {
183 int temp_time;
184
185 task_sleep(&rq, t);
186 temp_time = t->process(t);
187 next_time = MINTIME(temp_time, next_time);
188 }
189
190 /* maintain all proxies in a consistent state. This should quickly become a task */
191 time2 = maintain_proxies();
192 return MINTIME(time2, next_time);
193}
194
195
196/*
197 * Local variables:
198 * c-indent-level: 8
199 * c-basic-offset: 8
200 * End:
201 */