xref: /btstack/platform/posix/btstack_run_loop_posix.c (revision b3fcedb9c9ccbcc9680da165cd86f394be5d84a8)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 /*
39  *  btstack_run_loop.c
40  *
41  *  Created by Matthias Ringwald on 6/6/09.
42  */
43 
44 #include "btstack_run_loop.h"
45 #include "btstack_run_loop_posix.h"
46 #include "btstack_linked_list.h"
47 #include "btstack_debug.h"
48 
49 #ifdef _WIN32
50 #include "Winsock2.h"
51 #else
52 #include <sys/select.h>
53 #endif
54 
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <sys/time.h>
58 
59 static void btstack_run_loop_posix_dump_timer(void);
60 
61 // the run loop
62 static btstack_linked_list_t data_sources;
63 static int data_sources_modified;
64 static btstack_linked_list_t timers;
65 // start time. tv_usec = 0
66 static struct timeval init_tv;
67 
68 /**
69  * Add data_source to run_loop
70  */
71 static void btstack_run_loop_posix_add_data_source(btstack_data_source_t *ds){
72     data_sources_modified = 1;
73     // log_info("btstack_run_loop_posix_add_data_source %x with fd %u\n", (int) ds, ds->fd);
74     btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds);
75 }
76 
77 /**
78  * Remove data_source from run loop
79  */
80 static int btstack_run_loop_posix_remove_data_source(btstack_data_source_t *ds){
81     data_sources_modified = 1;
82     // log_info("btstack_run_loop_posix_remove_data_source %x\n", (int) ds);
83     return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds);
84 }
85 
86 /**
87  * Add timer to run_loop (keep list sorted)
88  */
89 static void btstack_run_loop_posix_add_timer(btstack_timer_source_t *ts){
90     btstack_linked_item_t *it;
91     for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){
92         btstack_timer_source_t * next = (btstack_timer_source_t *) it->next;
93         if (next == ts){
94             log_error( "btstack_run_loop_timer_add error: timer to add already in list!");
95             return;
96         }
97         if (next->timeout > ts->timeout) {
98             break;
99         }
100     }
101     ts->item.next = it->next;
102     it->next = (btstack_linked_item_t *) ts;
103     log_debug("Added timer %p at %u\n", ts, ts->timeout);
104     // btstack_run_loop_posix_dump_timer();
105 }
106 
107 /**
108  * Remove timer from run loop
109  */
110 static int btstack_run_loop_posix_remove_timer(btstack_timer_source_t *ts){
111     // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
112     // btstack_run_loop_posix_dump_timer();
113     return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts);
114 }
115 
116 static void btstack_run_loop_posix_dump_timer(void){
117     btstack_linked_item_t *it;
118     int i = 0;
119     for (it = (btstack_linked_item_t *) timers; it ; it = it->next){
120         btstack_timer_source_t *ts = (btstack_timer_source_t*) it;
121         log_info("timer %u, timeout %u\n", i, ts->timeout);
122     }
123 }
124 
125 static void btstack_run_loop_posix_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){
126     ds->flags |= callback_types;
127 }
128 
129 static void btstack_run_loop_posix_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){
130     ds->flags &= ~callback_types;
131 }
132 
133 /**
134  * @brief Queries the current time in ms since start
135  */
136 static uint32_t btstack_run_loop_posix_get_time_ms(void){
137     struct timeval tv;
138     gettimeofday(&tv, NULL);
139     uint32_t time_ms = ((tv.tv_sec  - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000);
140     log_debug("btstack_run_loop_posix_get_time_ms: %u <- %u / %u", time_ms, (int) tv.tv_sec, (int) tv.tv_usec);
141     return time_ms;
142 }
143 
144 /**
145  * Execute run_loop
146  */
147 static void btstack_run_loop_posix_execute(void) {
148     fd_set descriptors_read;
149     fd_set descriptors_write;
150 
151     btstack_timer_source_t       *ts;
152     btstack_linked_list_iterator_t it;
153     struct timeval * timeout;
154     struct timeval tv;
155     uint32_t now_ms;
156 
157     while (1) {
158         // collect FDs
159         FD_ZERO(&descriptors_read);
160         FD_ZERO(&descriptors_write);
161         int highest_fd = -1;
162         btstack_linked_list_iterator_init(&it, &data_sources);
163         while (btstack_linked_list_iterator_has_next(&it)){
164             btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it);
165             if (ds->fd < 0) continue;
166             if (ds->flags & DATA_SOURCE_CALLBACK_READ){
167                 FD_SET(ds->fd, &descriptors_read);
168                 if (ds->fd > highest_fd) {
169                     highest_fd = ds->fd;
170                 }
171                 log_debug("btstack_run_loop_execute adding fd %u for read", ds->fd);
172             }
173             if (ds->flags & DATA_SOURCE_CALLBACK_WRITE){
174                 FD_SET(ds->fd, &descriptors_write);
175                 if (ds->fd > highest_fd) {
176                     highest_fd = ds->fd;
177                 }
178                 log_debug("btstack_run_loop_execute adding fd %u for write", ds->fd);
179             }
180         }
181 
182         // get next timeout
183         timeout = NULL;
184         if (timers) {
185             ts = (btstack_timer_source_t *) timers;
186             timeout = &tv;
187             now_ms = btstack_run_loop_posix_get_time_ms();
188             int delta = ts->timeout - now_ms;
189             if (delta < 0){
190                 delta = 0;
191             }
192             tv.tv_sec  = delta / 1000;
193             tv.tv_usec = (delta - (tv.tv_sec * 1000)) * 1000;
194             log_debug("btstack_run_loop_execute next timeout in %u ms", delta);
195         }
196 
197         // wait for ready FDs
198         select( highest_fd+1 , &descriptors_read, &descriptors_write, NULL, timeout);
199 
200 
201         data_sources_modified = 0;
202         btstack_linked_list_iterator_init(&it, &data_sources);
203         while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){
204             btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it);
205             log_debug("btstack_run_loop_posix_execute: check ds %p with fd %u\n", ds, ds->fd);
206             if (FD_ISSET(ds->fd, &descriptors_read)) {
207                 log_debug("btstack_run_loop_posix_execute: process read ds %p with fd %u\n", ds, ds->fd);
208                 ds->process(ds, DATA_SOURCE_CALLBACK_READ);
209             }
210             if (FD_ISSET(ds->fd, &descriptors_write)) {
211                 log_debug("btstack_run_loop_posix_execute: process write ds %p with fd %u\n", ds, ds->fd);
212                 ds->process(ds, DATA_SOURCE_CALLBACK_WRITE);
213             }
214         }
215         log_debug("btstack_run_loop_posix_execute: after ds check\n");
216 
217         // process timers
218         now_ms = btstack_run_loop_posix_get_time_ms();
219         while (timers) {
220             ts = (btstack_timer_source_t *) timers;
221             if (ts->timeout > now_ms) break;
222             log_debug("btstack_run_loop_posix_execute: process timer %p\n", ts);
223 
224             // remove timer before processing it to allow handler to re-register with run loop
225             btstack_run_loop_remove_timer(ts);
226             ts->process(ts);
227         }
228     }
229 }
230 
231 // set timer
232 static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){
233     uint32_t time_ms = btstack_run_loop_posix_get_time_ms();
234     a->timeout = time_ms + timeout_in_ms;
235     log_debug("btstack_run_loop_posix_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms);
236 }
237 
238 static void btstack_run_loop_posix_init(void){
239     data_sources = NULL;
240     timers = NULL;
241     // just assume that we started at tv_usec == 0
242     gettimeofday(&init_tv, NULL);
243     init_tv.tv_usec = 0;
244     log_debug("btstack_run_loop_posix_init at %u/%u", (int) init_tv.tv_sec, 0);
245 }
246 
247 
248 static const btstack_run_loop_t btstack_run_loop_posix = {
249     &btstack_run_loop_posix_init,
250     &btstack_run_loop_posix_add_data_source,
251     &btstack_run_loop_posix_remove_data_source,
252     &btstack_run_loop_posix_enable_data_source_callbacks,
253     &btstack_run_loop_posix_disable_data_source_callbacks,
254     &btstack_run_loop_posix_set_timer,
255     &btstack_run_loop_posix_add_timer,
256     &btstack_run_loop_posix_remove_timer,
257     &btstack_run_loop_posix_execute,
258     &btstack_run_loop_posix_dump_timer,
259     &btstack_run_loop_posix_get_time_ms,
260 };
261 
262 /**
263  * Provide btstack_run_loop_posix instance
264  */
265 const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){
266     return &btstack_run_loop_posix;
267 }
268 
269