1 /*
2 * Copyright (c) 2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include "minip-internal.h"
10
11 #include <lk/trace.h>
12 #include <lk/debug.h>
13 #include <lk/compiler.h>
14 #include <stdlib.h>
15 #include <lk/list.h>
16 #include <lk/err.h>
17 #include <sys/types.h>
18 #include <kernel/thread.h>
19 #include <kernel/event.h>
20 #include <kernel/mutex.h>
21 #include <platform.h>
22
23 #define LOCAL_TRACE 0
24
25 static struct list_node net_timer_list = LIST_INITIAL_VALUE(net_timer_list);
26 static event_t net_timer_event = EVENT_INITIAL_VALUE(net_timer_event, false, 0);
27 static mutex_t net_timer_lock = MUTEX_INITIAL_VALUE(net_timer_lock);
28
add_to_queue(net_timer_t * t)29 static void add_to_queue(net_timer_t *t) {
30 net_timer_t *e;
31 list_for_every_entry(&net_timer_list, e, net_timer_t, node) {
32 if (TIME_GT(e->sched_time, t->sched_time)) {
33 list_add_before(&e->node, &t->node);
34 return;
35 }
36 }
37
38 list_add_tail(&net_timer_list, &t->node);
39 }
40
net_timer_set(net_timer_t * t,net_timer_callback_t cb,void * callback_args,lk_time_t delay)41 bool net_timer_set(net_timer_t *t, net_timer_callback_t cb, void *callback_args, lk_time_t delay) {
42 bool newly_queued = true;
43
44 lk_time_t now = current_time();
45
46 mutex_acquire(&net_timer_lock);
47
48 if (list_in_list(&t->node)) {
49 list_delete(&t->node);
50 newly_queued = false;
51 }
52
53 t->cb = cb;
54 t->arg = callback_args;
55 t->sched_time = now + delay;
56
57 add_to_queue(t);
58
59 mutex_release(&net_timer_lock);
60
61 event_signal(&net_timer_event, true);
62
63 return newly_queued;
64 }
65
net_timer_cancel(net_timer_t * t)66 bool net_timer_cancel(net_timer_t *t) {
67 bool was_queued = false;
68
69 mutex_acquire(&net_timer_lock);
70
71 if (list_in_list(&t->node)) {
72 list_delete(&t->node);
73 was_queued = true;
74 }
75
76 mutex_release(&net_timer_lock);
77
78 return was_queued;
79 }
80
81 /* returns the delay to the next event */
net_timer_work_routine(void)82 static lk_time_t net_timer_work_routine(void) {
83 lk_time_t now = current_time();
84 lk_time_t delay = INFINITE_TIME;
85
86 mutex_acquire(&net_timer_lock);
87
88 for (;;) {
89 net_timer_t *e;
90 e = list_peek_head_type(&net_timer_list, net_timer_t, node);
91 if (!e) {
92 delay = INFINITE_TIME;
93 goto done;
94 }
95
96 if (TIME_GT(e->sched_time, now)) {
97 delay = e->sched_time - now;
98 goto done;
99 }
100
101 list_delete(&e->node);
102
103 mutex_release(&net_timer_lock);
104
105 LTRACEF("firing timer %p, cb %p, arg %p\n", e, e->cb, e->arg);
106 e->cb(e->arg);
107
108 mutex_acquire(&net_timer_lock);
109 }
110
111 done:
112 if (delay == INFINITE_TIME)
113 event_unsignal(&net_timer_event);
114
115 mutex_release(&net_timer_lock);
116
117 return delay;
118 }
119
net_timer_work_thread(void * args)120 static int net_timer_work_thread(void *args) {
121 for (;;) {
122 event_wait(&net_timer_event);
123
124 lk_time_t delay = net_timer_work_routine();
125 if (delay != INFINITE_TIME) {
126 thread_sleep(MIN(delay, 100));
127 }
128 }
129
130 return 0;
131 }
132
net_timer_init(void)133 void net_timer_init(void) {
134 thread_detach_and_resume(thread_create("net timer", &net_timer_work_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
135 }
136
137
138