diff options
author | Christian Grothoff <christian@grothoff.org> | 2017-03-27 11:44:46 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2017-03-28 02:27:25 +0200 |
commit | d486f5da0f495d817a19efd077501c25e4eef6fa (patch) | |
tree | bb785996088c46ed38012b23698d6354f8da7cfe /src | |
parent | 802f89d2979d3a92228543d42cee6bb8e3e786d1 (diff) | |
download | gnunet-d486f5da0f495d817a19efd077501c25e4eef6fa.tar.gz gnunet-d486f5da0f495d817a19efd077501c25e4eef6fa.zip |
initial ideas for improving the scheduler API (far from finished)
Diffstat (limited to 'src')
-rw-r--r-- | src/include/gnunet_scheduler_lib.h | 246 | ||||
-rw-r--r-- | src/util/scheduler.c | 328 |
2 files changed, 563 insertions, 11 deletions
diff --git a/src/include/gnunet_scheduler_lib.h b/src/include/gnunet_scheduler_lib.h index 2be1858ce..a7385e31c 100644 --- a/src/include/gnunet_scheduler_lib.h +++ b/src/include/gnunet_scheduler_lib.h | |||
@@ -97,6 +97,84 @@ enum GNUNET_SCHEDULER_Reason | |||
97 | 97 | ||
98 | 98 | ||
99 | /** | 99 | /** |
100 | * Possible events on FDs, used as a bitmask. | ||
101 | * Modelled after GPollFD. | ||
102 | */ | ||
103 | enum GNUNET_SCHEDULER_EventType | ||
104 | { | ||
105 | |||
106 | /** | ||
107 | * No event (useful for timeout). | ||
108 | */ | ||
109 | GNUNET_SCHEDULER_ET_NONE = 0, | ||
110 | |||
111 | /** | ||
112 | * Data available for reading. | ||
113 | */ | ||
114 | GNUNET_SCHEDULER_ET_IN = 1, | ||
115 | |||
116 | /** | ||
117 | * Buffer available for writing. | ||
118 | */ | ||
119 | GNUNET_SCHEDULER_ET_OUT = 2, | ||
120 | |||
121 | /** | ||
122 | * | ||
123 | */ | ||
124 | GNUNET_SCHEDULER_ET_HUP = 4, | ||
125 | |||
126 | /** | ||
127 | * | ||
128 | */ | ||
129 | GNUNET_SCHEDULER_ET_ERR = 8, | ||
130 | |||
131 | /** | ||
132 | * | ||
133 | */ | ||
134 | GNUNET_SCHEDULER_ET_PRI = 16, | ||
135 | |||
136 | /** | ||
137 | * | ||
138 | */ | ||
139 | GNUNET_SCHEDULER_ET_NVAL = 32 | ||
140 | |||
141 | }; | ||
142 | |||
143 | |||
144 | /** | ||
145 | * Information about an event relating to a file descriptor/socket. | ||
146 | */ | ||
147 | struct GNUNET_SCHEDULER_FdInfo | ||
148 | { | ||
149 | |||
150 | /** | ||
151 | * GNUnet network socket the event is about, matches @a sock, | ||
152 | * NULL if this is about a file handle or if no network | ||
153 | * handle was given to the scheduler originally. | ||
154 | */ | ||
155 | struct GNUNET_NETWORK_Handle *fd; | ||
156 | |||
157 | /** | ||
158 | * GNUnet file handle the event is about, matches @a sock, | ||
159 | * NULL if this is about a network socket or if no network | ||
160 | * handle was given to the scheduler originally. | ||
161 | */ | ||
162 | struct GNUNET_DISK_FileHandle *fh; | ||
163 | |||
164 | /** | ||
165 | * Type of the event that was generated related to @e sock. | ||
166 | */ | ||
167 | enum GNUNET_SCHEDULER_EventType et; | ||
168 | |||
169 | /** | ||
170 | * Underlying OS handle the event was about. | ||
171 | */ | ||
172 | int sock; | ||
173 | |||
174 | }; | ||
175 | |||
176 | |||
177 | /** | ||
100 | * Context information passed to each scheduler task. | 178 | * Context information passed to each scheduler task. |
101 | */ | 179 | */ |
102 | struct GNUNET_SCHEDULER_TaskContext | 180 | struct GNUNET_SCHEDULER_TaskContext |
@@ -107,16 +185,29 @@ struct GNUNET_SCHEDULER_TaskContext | |||
107 | enum GNUNET_SCHEDULER_Reason reason; | 185 | enum GNUNET_SCHEDULER_Reason reason; |
108 | 186 | ||
109 | /** | 187 | /** |
110 | * Set of file descriptors ready for reading; | 188 | * Length of the following array. |
111 | * note that additional bits may be set | 189 | */ |
112 | * that were not in the original request | 190 | unsigned int fds_len; |
191 | |||
192 | /** | ||
193 | * Array of length @e fds_len with information about ready FDs. | ||
194 | * Note that we use the same format regardless of the internal | ||
195 | * event loop that was used. The given array should only contain | ||
196 | * information about file descriptors relevant to the current task. | ||
197 | */ | ||
198 | const struct GNUNET_SCHEDULER_FdInfo *fds; | ||
199 | |||
200 | /** | ||
201 | * Set of file descriptors ready for reading; note that additional | ||
202 | * bits may be set that were not in the original request. | ||
203 | * @deprecated | ||
113 | */ | 204 | */ |
114 | const struct GNUNET_NETWORK_FDSet *read_ready; | 205 | const struct GNUNET_NETWORK_FDSet *read_ready; |
115 | 206 | ||
116 | /** | 207 | /** |
117 | * Set of file descriptors ready for writing; | 208 | * Set of file descriptors ready for writing; note that additional |
118 | * note that additional bits may be set | 209 | * bits may be set that were not in the original request. |
119 | * that were not in the original request. | 210 | * @deprecated |
120 | */ | 211 | */ |
121 | const struct GNUNET_NETWORK_FDSet *write_ready; | 212 | const struct GNUNET_NETWORK_FDSet *write_ready; |
122 | 213 | ||
@@ -124,16 +215,155 @@ struct GNUNET_SCHEDULER_TaskContext | |||
124 | 215 | ||
125 | 216 | ||
126 | /** | 217 | /** |
218 | * Function used by event-loop implementations to signal the scheduler | ||
219 | * that a particular @a task is ready due to an event of type @a et. | ||
220 | * | ||
221 | * This function will then queue the task to notify the application | ||
222 | * that the task is ready (with the respective priority). | ||
223 | * | ||
224 | * @param task the task that is ready | ||
225 | * @param et information about why the task is ready | ||
226 | */ | ||
227 | void | ||
228 | GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task, | ||
229 | enum GNUNET_SCHEDULER_EventType et); | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Handle to the scheduler's state to be used by the driver. | ||
234 | */ | ||
235 | struct GNUNET_SCHEDULER_Handle; | ||
236 | |||
237 | |||
238 | /** | ||
239 | * Function called by the driver to tell the scheduler to run some of | ||
240 | * the tasks that are ready. This function may return even though | ||
241 | * there are tasks left to run just to give other tasks a chance as | ||
242 | * well. If we return #GNUNET_YES, the driver should call this | ||
243 | * function again as soon as possible, while if we return #GNUNET_NO | ||
244 | * it must block until the operating system has more work as the | ||
245 | * scheduler has no more work to do right now. | ||
246 | * | ||
247 | * @param sh scheduler handle that was given to the `loop` | ||
248 | * @return #GNUNET_OK if there are more tasks that are ready, | ||
249 | * and thus we would like to run more (yield to avoid | ||
250 | * blocking other activities for too long) | ||
251 | * #GNUNET_NO if we are done running tasks (yield to block) | ||
252 | * #GNUNET_SYSERR on error | ||
253 | */ | ||
254 | int | ||
255 | GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh); | ||
256 | |||
257 | |||
258 | /** | ||
259 | * API a driver has to implement for | ||
260 | * #GNUNET_SCHEDULER_run_with_driver(). | ||
261 | */ | ||
262 | struct GNUNET_SCHEDULER_Driver | ||
263 | { | ||
264 | |||
265 | /** | ||
266 | * Closure to pass to the functions in this struct. | ||
267 | */ | ||
268 | void *cls; | ||
269 | |||
270 | /** | ||
271 | * Add a @a task to be run if the conditions given | ||
272 | * in @a fdi are satisfied. | ||
273 | * | ||
274 | * @param cls closure | ||
275 | * @param task task to add | ||
276 | * @param fdi conditions to watch for | ||
277 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
278 | * (i.e. @a fdi too high or invalid) | ||
279 | */ | ||
280 | int | ||
281 | (*add)(void *cls, | ||
282 | struct GNUNET_SCHEDULER_Task *task, | ||
283 | struct GNUNET_SCHEDULER_FdInfo *fdi); | ||
284 | |||
285 | /** | ||
286 | * Delete a @a task from the set of tasks to be run. | ||
287 | * | ||
288 | * @param cls closure | ||
289 | * @param task task to delete | ||
290 | * @param fdi conditions to watch for (must match @e add call) | ||
291 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
292 | * (i.e. @a task or @a fdi do not match prior @e add call) | ||
293 | */ | ||
294 | int | ||
295 | (*del)(void *cls, | ||
296 | struct GNUNET_SCHEDULER_Task *task, | ||
297 | const struct GNUNET_SCHEDULER_FdInfo *fdi); | ||
298 | |||
299 | /** | ||
300 | * Set time at which we definitively want to get a wakeup call. | ||
301 | * | ||
302 | * @param cls closure | ||
303 | * @param dt time when we want to wake up next | ||
304 | */ | ||
305 | void | ||
306 | (*set_wakeup)(void *cls, | ||
307 | struct GNUNET_TIME_Absolute dt); | ||
308 | |||
309 | /** | ||
310 | * Event loop's "main" function, to be called from | ||
311 | * #GNUNET_SCHEDULER_run_with_driver() to actually | ||
312 | * launch the loop. | ||
313 | * | ||
314 | * @param cls closure | ||
315 | * @param sh scheduler handle to pass to | ||
316 | * #GNUNET_SCHEDULER_run_from_driver() | ||
317 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
318 | */ | ||
319 | int | ||
320 | (*loop)(void *cls, | ||
321 | struct GNUNET_SCHEDULER_Handle *sh); | ||
322 | |||
323 | }; | ||
324 | |||
325 | |||
326 | /** | ||
127 | * Signature of the main function of a task. | 327 | * Signature of the main function of a task. |
128 | * | 328 | * |
129 | * @param cls closure | 329 | * @param cls closure |
130 | * @param tc context information (why was this task triggered now) | ||
131 | */ | 330 | */ |
132 | typedef void | 331 | typedef void |
133 | (*GNUNET_SCHEDULER_TaskCallback) (void *cls); | 332 | (*GNUNET_SCHEDULER_TaskCallback) (void *cls); |
134 | 333 | ||
135 | 334 | ||
136 | /** | 335 | /** |
336 | * Initialize and run scheduler. This function will return when all | ||
337 | * tasks have completed. On systems with signals, receiving a SIGTERM | ||
338 | * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown | ||
339 | * to be run after the active task is complete. As a result, SIGTERM | ||
340 | * causes all shutdown tasks to be scheduled with reason | ||
341 | * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added | ||
342 | * afterwards will execute normally!). Note that any particular | ||
343 | * signal will only shut down one scheduler; applications should | ||
344 | * always only create a single scheduler. | ||
345 | * | ||
346 | * @param driver drive to use for the event loop | ||
347 | * @param task task to run first (and immediately) | ||
348 | * @param task_cls closure of @a task | ||
349 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
350 | */ | ||
351 | int | ||
352 | GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver, | ||
353 | GNUNET_SCHEDULER_TaskCallback task, | ||
354 | void *task_cls); | ||
355 | |||
356 | |||
357 | /** | ||
358 | * Obtain the driver for using select() as the event loop. | ||
359 | * | ||
360 | * @return NULL on error | ||
361 | */ | ||
362 | const struct GNUNET_SCHEDULER_Driver * | ||
363 | GNUNET_SCHEDULER_driver_select (void); | ||
364 | |||
365 | |||
366 | /** | ||
137 | * Signature of the select function used by the scheduler. | 367 | * Signature of the select function used by the scheduler. |
138 | * #GNUNET_NETWORK_socket_select matches it. | 368 | * #GNUNET_NETWORK_socket_select matches it. |
139 | * | 369 | * |
@@ -571,7 +801,7 @@ GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio, | |||
571 | * Sets the select function to use in the scheduler (scheduler_select). | 801 | * Sets the select function to use in the scheduler (scheduler_select). |
572 | * | 802 | * |
573 | * @param new_select new select function to use (NULL to reset to default) | 803 | * @param new_select new select function to use (NULL to reset to default) |
574 | * @param new_select_cls closure for 'new_select' | 804 | * @param new_select_cls closure for @a new_select |
575 | */ | 805 | */ |
576 | void | 806 | void |
577 | GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select, | 807 | GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select, |
diff --git a/src/util/scheduler.c b/src/util/scheduler.c index 409a0942f..a7b1d8e2a 100644 --- a/src/util/scheduler.c +++ b/src/util/scheduler.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of GNUnet | 2 | This file is part of GNUnet |
3 | Copyright (C) 2009-2016 GNUnet e.V. | 3 | Copyright (C) 2009-2017 GNUnet e.V. |
4 | 4 | ||
5 | GNUnet is free software; you can redistribute it and/or modify | 5 | GNUnet is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published | 6 | it under the terms of the GNU General Public License as published |
@@ -17,7 +17,6 @@ | |||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | Boston, MA 02110-1301, USA. | 18 | Boston, MA 02110-1301, USA. |
19 | */ | 19 | */ |
20 | |||
21 | /** | 20 | /** |
22 | * @file util/scheduler.c | 21 | * @file util/scheduler.c |
23 | * @brief schedule computations using continuation passing style | 22 | * @brief schedule computations using continuation passing style |
@@ -71,6 +70,35 @@ | |||
71 | 70 | ||
72 | 71 | ||
73 | /** | 72 | /** |
73 | * Argument to be passed from the driver to | ||
74 | * #GNUNET_SCHEDULER_run_from_driver(). Contains the | ||
75 | * scheduler's internal state. | ||
76 | */ | ||
77 | struct GNUNET_SCHEDULER_Handle | ||
78 | { | ||
79 | /** | ||
80 | * Passed here to avoid constantly allocating/deallocating | ||
81 | * this element, but generally we want to get rid of this. | ||
82 | * @deprecated | ||
83 | */ | ||
84 | struct GNUNET_NETWORK_FDSet *rs; | ||
85 | |||
86 | /** | ||
87 | * Passed here to avoid constantly allocating/deallocating | ||
88 | * this element, but generally we want to get rid of this. | ||
89 | * @deprecated | ||
90 | */ | ||
91 | struct GNUNET_NETWORK_FDSet *ws; | ||
92 | |||
93 | /** | ||
94 | * Driver we used for the event loop. | ||
95 | */ | ||
96 | const struct GNUNET_SCHEDULER_Driver *driver; | ||
97 | |||
98 | }; | ||
99 | |||
100 | |||
101 | /** | ||
74 | * Entry in list of pending tasks. | 102 | * Entry in list of pending tasks. |
75 | */ | 103 | */ |
76 | struct GNUNET_SCHEDULER_Task | 104 | struct GNUNET_SCHEDULER_Task |
@@ -96,6 +124,11 @@ struct GNUNET_SCHEDULER_Task | |||
96 | void *callback_cls; | 124 | void *callback_cls; |
97 | 125 | ||
98 | /** | 126 | /** |
127 | * Handle to the scheduler's state. | ||
128 | */ | ||
129 | const struct GNUNET_SCHEDULER_Handle *sh; | ||
130 | |||
131 | /** | ||
99 | * Set of file descriptors this task is waiting | 132 | * Set of file descriptors this task is waiting |
100 | * for for reading. Once ready, this is updated | 133 | * for for reading. Once ready, this is updated |
101 | * to reflect the set of file descriptors ready | 134 | * to reflect the set of file descriptors ready |
@@ -111,6 +144,18 @@ struct GNUNET_SCHEDULER_Task | |||
111 | struct GNUNET_NETWORK_FDSet *write_set; | 144 | struct GNUNET_NETWORK_FDSet *write_set; |
112 | 145 | ||
113 | /** | 146 | /** |
147 | * Information about which FDs are ready for this task (and why). | ||
148 | */ | ||
149 | const struct GNUNET_SCHEDULER_FdInfo *fds; | ||
150 | |||
151 | /** | ||
152 | * Storage location used for @e fds if we want to avoid | ||
153 | * a separate malloc() call in the common case that this | ||
154 | * task is only about a single FD. | ||
155 | */ | ||
156 | struct GNUNET_SCHEDULER_FdInfo fdx; | ||
157 | |||
158 | /** | ||
114 | * Absolute timeout value for the task, or | 159 | * Absolute timeout value for the task, or |
115 | * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout". | 160 | * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout". |
116 | */ | 161 | */ |
@@ -124,6 +169,11 @@ struct GNUNET_SCHEDULER_Task | |||
124 | #endif | 169 | #endif |
125 | 170 | ||
126 | /** | 171 | /** |
172 | * Size of the @e fds array. | ||
173 | */ | ||
174 | unsigned int fds_len; | ||
175 | |||
176 | /** | ||
127 | * Why is the task ready? Set after task is added to ready queue. | 177 | * Why is the task ready? Set after task is added to ready queue. |
128 | * Initially set to zero. All reasons that have already been | 178 | * Initially set to zero. All reasons that have already been |
129 | * satisfied (i.e. read or write ready) will be set over time. | 179 | * satisfied (i.e. read or write ready) will be set over time. |
@@ -1742,7 +1792,8 @@ GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio, | |||
1742 | GNUNET_CONTAINER_DLL_insert (pending_head, | 1792 | GNUNET_CONTAINER_DLL_insert (pending_head, |
1743 | pending_tail, | 1793 | pending_tail, |
1744 | t); | 1794 | t); |
1745 | max_priority_added = GNUNET_MAX (max_priority_added, t->priority); | 1795 | max_priority_added = GNUNET_MAX (max_priority_added, |
1796 | t->priority); | ||
1746 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 1797 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1747 | "Adding task %p\n", | 1798 | "Adding task %p\n", |
1748 | t); | 1799 | t); |
@@ -1750,4 +1801,275 @@ GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio, | |||
1750 | return t; | 1801 | return t; |
1751 | } | 1802 | } |
1752 | 1803 | ||
1804 | |||
1805 | /** | ||
1806 | * Function used by event-loop implementations to signal the scheduler | ||
1807 | * that a particular @a task is ready due to an event of type @a et. | ||
1808 | * | ||
1809 | * This function will then queue the task to notify the application | ||
1810 | * that the task is ready (with the respective priority). | ||
1811 | * | ||
1812 | * @param task the task that is ready, NULL for wake up calls | ||
1813 | * @param et information about why the task is ready | ||
1814 | */ | ||
1815 | void | ||
1816 | GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task, | ||
1817 | enum GNUNET_SCHEDULER_EventType et) | ||
1818 | { | ||
1819 | enum GNUNET_SCHEDULER_Reason reason; | ||
1820 | struct GNUNET_TIME_Absolute now; | ||
1821 | |||
1822 | now = GNUNET_TIME_absolute_get (); | ||
1823 | reason = task->reason; | ||
1824 | if (now.abs_value_us >= task->timeout.abs_value_us) | ||
1825 | reason |= GNUNET_SCHEDULER_REASON_TIMEOUT; | ||
1826 | if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) && | ||
1827 | (0 != (GNUNET_SCHEDULER_ET_IN & et)) ) | ||
1828 | reason |= GNUNET_SCHEDULER_REASON_READ_READY; | ||
1829 | if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) && | ||
1830 | (0 != (GNUNET_SCHEDULER_ET_OUT & et)) ) | ||
1831 | reason |= GNUNET_SCHEDULER_REASON_WRITE_READY; | ||
1832 | reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE; | ||
1833 | task->reason = reason; | ||
1834 | task->fds = &task->fdx; | ||
1835 | task->fdx.et = et; | ||
1836 | task->fds_len = 1; | ||
1837 | queue_ready_task (task); | ||
1838 | } | ||
1839 | |||
1840 | |||
1841 | /** | ||
1842 | * Function called by the driver to tell the scheduler to run some of | ||
1843 | * the tasks that are ready. This function may return even though | ||
1844 | * there are tasks left to run just to give other tasks a chance as | ||
1845 | * well. If we return #GNUNET_YES, the driver should call this | ||
1846 | * function again as soon as possible, while if we return #GNUNET_NO | ||
1847 | * it must block until the operating system has more work as the | ||
1848 | * scheduler has no more work to do right now. | ||
1849 | * | ||
1850 | * @param sh scheduler handle that was given to the `loop` | ||
1851 | * @return #GNUNET_OK if there are more tasks that are ready, | ||
1852 | * and thus we would like to run more (yield to avoid | ||
1853 | * blocking other activities for too long) | ||
1854 | * #GNUNET_NO if we are done running tasks (yield to block) | ||
1855 | * #GNUNET_SYSERR on error | ||
1856 | */ | ||
1857 | int | ||
1858 | GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh) | ||
1859 | { | ||
1860 | enum GNUNET_SCHEDULER_Priority p; | ||
1861 | struct GNUNET_SCHEDULER_Task *pos; | ||
1862 | struct GNUNET_TIME_Absolute now; | ||
1863 | |||
1864 | /* check for tasks that reached the timeout! */ | ||
1865 | now = GNUNET_TIME_absolute_get (); | ||
1866 | while (NULL != (pos = pending_timeout_head)) | ||
1867 | { | ||
1868 | if (now.abs_value_us >= pos->timeout.abs_value_us) | ||
1869 | pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT; | ||
1870 | if (0 == pos->reason) | ||
1871 | break; | ||
1872 | GNUNET_CONTAINER_DLL_remove (pending_timeout_head, | ||
1873 | pending_timeout_tail, | ||
1874 | pos); | ||
1875 | if (pending_timeout_last == pos) | ||
1876 | pending_timeout_last = NULL; | ||
1877 | queue_ready_task (pos); | ||
1878 | } | ||
1879 | |||
1880 | if (0 == ready_count) | ||
1881 | return GNUNET_NO; | ||
1882 | |||
1883 | /* find out which task priority level we are going to | ||
1884 | process this time */ | ||
1885 | max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP; | ||
1886 | GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]); | ||
1887 | /* yes, p>0 is correct, 0 is "KEEP" which should | ||
1888 | * always be an empty queue (see assertion)! */ | ||
1889 | for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--) | ||
1890 | { | ||
1891 | pos = ready_head[p]; | ||
1892 | if (NULL != pos) | ||
1893 | break; | ||
1894 | } | ||
1895 | GNUNET_assert (NULL != pos); /* ready_count wrong? */ | ||
1896 | |||
1897 | /* process all tasks at this priority level, then yield */ | ||
1898 | while (NULL != (pos = ready_head[p])) | ||
1899 | { | ||
1900 | GNUNET_CONTAINER_DLL_remove (ready_head[p], | ||
1901 | ready_tail[p], | ||
1902 | pos); | ||
1903 | ready_count--; | ||
1904 | current_priority = pos->priority; | ||
1905 | current_lifeness = pos->lifeness; | ||
1906 | active_task = pos; | ||
1907 | #if PROFILE_DELAYS | ||
1908 | if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us > | ||
1909 | DELAY_THRESHOLD.rel_value_us) | ||
1910 | { | ||
1911 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1912 | "Task %p took %s to be scheduled\n", | ||
1913 | pos, | ||
1914 | GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time), | ||
1915 | GNUNET_YES)); | ||
1916 | } | ||
1917 | #endif | ||
1918 | tc.reason = pos->reason; | ||
1919 | GNUNET_NETWORK_fdset_zero (sh->rs); | ||
1920 | GNUNET_NETWORK_fdset_zero (sh->ws); | ||
1921 | tc.fds_len = pos->fds_len; | ||
1922 | tc.fds = pos->fds; | ||
1923 | tc.read_ready = (NULL == pos->read_set) ? sh->rs : pos->read_set; | ||
1924 | if ( (-1 != pos->read_fd) && | ||
1925 | (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) ) | ||
1926 | GNUNET_NETWORK_fdset_set_native (sh->rs, | ||
1927 | pos->read_fd); | ||
1928 | tc.write_ready = (NULL == pos->write_set) ? sh->ws : pos->write_set; | ||
1929 | if ((-1 != pos->write_fd) && | ||
1930 | (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))) | ||
1931 | GNUNET_NETWORK_fdset_set_native (sh->ws, | ||
1932 | pos->write_fd); | ||
1933 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1934 | "Running task: %p\n", | ||
1935 | pos); | ||
1936 | pos->callback (pos->callback_cls); | ||
1937 | active_task = NULL; | ||
1938 | dump_backtrace (pos); | ||
1939 | destroy_task (pos); | ||
1940 | tasks_run++; | ||
1941 | } | ||
1942 | if (0 == ready_count) | ||
1943 | return GNUNET_NO; | ||
1944 | return GNUNET_OK; | ||
1945 | } | ||
1946 | |||
1947 | |||
1948 | /** | ||
1949 | * Initialize and run scheduler. This function will return when all | ||
1950 | * tasks have completed. On systems with signals, receiving a SIGTERM | ||
1951 | * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown | ||
1952 | * to be run after the active task is complete. As a result, SIGTERM | ||
1953 | * causes all shutdown tasks to be scheduled with reason | ||
1954 | * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added | ||
1955 | * afterwards will execute normally!). Note that any particular | ||
1956 | * signal will only shut down one scheduler; applications should | ||
1957 | * always only create a single scheduler. | ||
1958 | * | ||
1959 | * @param driver drive to use for the event loop | ||
1960 | * @param task task to run first (and immediately) | ||
1961 | * @param task_cls closure of @a task | ||
1962 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure | ||
1963 | */ | ||
1964 | int | ||
1965 | GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver, | ||
1966 | GNUNET_SCHEDULER_TaskCallback task, | ||
1967 | void *task_cls) | ||
1968 | { | ||
1969 | int ret; | ||
1970 | struct GNUNET_SIGNAL_Context *shc_int; | ||
1971 | struct GNUNET_SIGNAL_Context *shc_term; | ||
1972 | #if (SIGTERM != GNUNET_TERM_SIG) | ||
1973 | struct GNUNET_SIGNAL_Context *shc_gterm; | ||
1974 | #endif | ||
1975 | #ifndef MINGW | ||
1976 | struct GNUNET_SIGNAL_Context *shc_quit; | ||
1977 | struct GNUNET_SIGNAL_Context *shc_hup; | ||
1978 | struct GNUNET_SIGNAL_Context *shc_pipe; | ||
1979 | #endif | ||
1980 | struct GNUNET_SCHEDULER_Task tsk; | ||
1981 | const struct GNUNET_DISK_FileHandle *pr; | ||
1982 | struct GNUNET_SCHEDULER_Handle sh; | ||
1983 | |||
1984 | /* general set-up */ | ||
1985 | GNUNET_assert (NULL == active_task); | ||
1986 | GNUNET_assert (NULL == shutdown_pipe_handle); | ||
1987 | shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO, | ||
1988 | GNUNET_NO, | ||
1989 | GNUNET_NO, | ||
1990 | GNUNET_NO); | ||
1991 | GNUNET_assert (NULL != shutdown_pipe_handle); | ||
1992 | pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle, | ||
1993 | GNUNET_DISK_PIPE_END_READ); | ||
1994 | GNUNET_assert (NULL != pr); | ||
1995 | my_pid = getpid (); | ||
1996 | |||
1997 | /* install signal handlers */ | ||
1998 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1999 | "Registering signal handlers\n"); | ||
2000 | shc_int = GNUNET_SIGNAL_handler_install (SIGINT, | ||
2001 | &sighandler_shutdown); | ||
2002 | shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, | ||
2003 | &sighandler_shutdown); | ||
2004 | #if (SIGTERM != GNUNET_TERM_SIG) | ||
2005 | shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG, | ||
2006 | &sighandler_shutdown); | ||
2007 | #endif | ||
2008 | #ifndef MINGW | ||
2009 | shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, | ||
2010 | &sighandler_pipe); | ||
2011 | shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, | ||
2012 | &sighandler_shutdown); | ||
2013 | shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, | ||
2014 | &sighandler_shutdown); | ||
2015 | #endif | ||
2016 | |||
2017 | /* Setup initial tasks */ | ||
2018 | current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT; | ||
2019 | current_lifeness = GNUNET_YES; | ||
2020 | memset (&tsk, | ||
2021 | 0, | ||
2022 | sizeof (tsk)); | ||
2023 | active_task = &tsk; | ||
2024 | tsk.sh = &sh; | ||
2025 | GNUNET_SCHEDULER_add_with_reason_and_priority (task, | ||
2026 | task_cls, | ||
2027 | GNUNET_SCHEDULER_REASON_STARTUP, | ||
2028 | GNUNET_SCHEDULER_PRIORITY_DEFAULT); | ||
2029 | GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO, | ||
2030 | &GNUNET_OS_install_parent_control_handler, | ||
2031 | NULL); | ||
2032 | active_task = NULL; | ||
2033 | driver->set_wakeup (driver->cls, | ||
2034 | GNUNET_TIME_absolute_get ()); | ||
2035 | |||
2036 | /* begin main event loop */ | ||
2037 | sh.rs = GNUNET_NETWORK_fdset_create (); | ||
2038 | sh.ws = GNUNET_NETWORK_fdset_create (); | ||
2039 | sh.driver = driver; | ||
2040 | ret = driver->loop (driver->cls, | ||
2041 | &sh); | ||
2042 | GNUNET_NETWORK_fdset_destroy (sh.rs); | ||
2043 | GNUNET_NETWORK_fdset_destroy (sh.ws); | ||
2044 | |||
2045 | /* uninstall signal handlers */ | ||
2046 | GNUNET_SIGNAL_handler_uninstall (shc_int); | ||
2047 | GNUNET_SIGNAL_handler_uninstall (shc_term); | ||
2048 | #if (SIGTERM != GNUNET_TERM_SIG) | ||
2049 | GNUNET_SIGNAL_handler_uninstall (shc_gterm); | ||
2050 | #endif | ||
2051 | #ifndef MINGW | ||
2052 | GNUNET_SIGNAL_handler_uninstall (shc_pipe); | ||
2053 | GNUNET_SIGNAL_handler_uninstall (shc_quit); | ||
2054 | GNUNET_SIGNAL_handler_uninstall (shc_hup); | ||
2055 | #endif | ||
2056 | GNUNET_DISK_pipe_close (shutdown_pipe_handle); | ||
2057 | shutdown_pipe_handle = NULL; | ||
2058 | return ret; | ||
2059 | } | ||
2060 | |||
2061 | |||
2062 | /** | ||
2063 | * Obtain the driver for using select() as the event loop. | ||
2064 | * | ||
2065 | * @return NULL on error | ||
2066 | */ | ||
2067 | const struct GNUNET_SCHEDULER_Driver * | ||
2068 | GNUNET_SCHEDULER_driver_select () | ||
2069 | { | ||
2070 | GNUNET_break (0); // not implemented | ||
2071 | return NULL; | ||
2072 | } | ||
2073 | |||
2074 | |||
1753 | /* end of scheduler.c */ | 2075 | /* end of scheduler.c */ |