xref: /btstack/platform/posix/btstack_run_loop_posix.c (revision a06bcae0f7f63d9d69b2f846d04300ea7058ea66)
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 #define __BTSTACK_FILE__ "btstack_run_loop_posix.c"
39 
40 /*
41  *  btstack_run_loop.c
42  *
43  *  Created by Matthias Ringwald on 6/6/09.
44  */
45 
46 #include "btstack_run_loop.h"
47 #include "btstack_run_loop_posix.h"
48 #include "btstack_linked_list.h"
49 #include "btstack_debug.h"
50 
51 #ifdef _WIN32
52 #include "Winsock2.h"
53 #else
54 #include <sys/select.h>
55 #endif
56 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <sys/time.h>
60 
61 static void btstack_run_loop_posix_dump_timer(void);
62 
63 // the run loop
64 static btstack_linked_list_t data_sources;
65 static int data_sources_modified;
66 static btstack_linked_list_t timers;
67 // start time. tv_usec = 0
68 static struct timeval init_tv;
69 
70 /**
71  * Add data_source to run_loop
72  */
73 static void btstack_run_loop_posix_add_data_source(btstack_data_source_t *ds){
74     data_sources_modified = 1;
75     // log_info("btstack_run_loop_posix_add_data_source %x with fd %u\n", (int) ds, ds->fd);
76     btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds);
77 }
78 
79 /**
80  * Remove data_source from run loop
81  */
82 static int btstack_run_loop_posix_remove_data_source(btstack_data_source_t *ds){
83     data_sources_modified = 1;
84     // log_info("btstack_run_loop_posix_remove_data_source %x\n", (int) ds);
85     return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds);
86 }
87 
88 /**
89  * Add timer to run_loop (keep list sorted)
90  */
91 static void btstack_run_loop_posix_add_timer(btstack_timer_source_t *ts){
92     btstack_linked_item_t *it;
93     for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){
94         btstack_timer_source_t * next = (btstack_timer_source_t *) it->next;
95         if (next == ts){
96             log_error( "btstack_run_loop_timer_add error: timer to add already in list!");
97             return;
98         }
99         if (next->timeout > ts->timeout) {
100             break;
101         }
102     }
103     ts->item.next = it->next;
104     it->next = (btstack_linked_item_t *) ts;
105     log_debug("Added timer %p at %u\n", ts, ts->timeout);
106     // btstack_run_loop_posix_dump_timer();
107 }
108 
109 /**
110  * Remove timer from run loop
111  */
112 static int btstack_run_loop_posix_remove_timer(btstack_timer_source_t *ts){
113     // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
114     // btstack_run_loop_posix_dump_timer();
115     return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts);
116 }
117 
118 static void btstack_run_loop_posix_dump_timer(void){
119     btstack_linked_item_t *it;
120     int i = 0;
121     for (it = (btstack_linked_item_t *) timers; it ; it = it->next){
122         btstack_timer_source_t *ts = (btstack_timer_source_t*) it;
123         log_info("timer %u, timeout %u\n", i, ts->timeout);
124     }
125 }
126 
127 static void btstack_run_loop_posix_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){
128     ds->flags |= callback_types;
129 }
130 
131 static void btstack_run_loop_posix_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){
132     ds->flags &= ~callback_types;
133 }
134 
135 /**
136  * @brief Queries the current time in ms since start
137  */
138 static uint32_t btstack_run_loop_posix_get_time_ms(void){
139     struct timeval tv;
140     gettimeofday(&tv, NULL);
141     uint32_t time_ms = (uint32_t)((tv.tv_sec  - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000);
142     log_debug("btstack_run_loop_posix_get_time_ms: %u <- %u / %u", time_ms, (int) tv.tv_sec, (int) tv.tv_usec);
143     return time_ms;
144 }
145 
146 /**
147  * Execute run_loop
148  */
149 static void btstack_run_loop_posix_execute(void) {
150     fd_set descriptors_read;
151     fd_set descriptors_write;
152 
153     btstack_timer_source_t       *ts;
154     btstack_linked_list_iterator_t it;
155     struct timeval * timeout;
156     struct timeval tv;
157     uint32_t now_ms;
158 
159     while (1) {
160         // collect FDs
161         FD_ZERO(&descriptors_read);
162         FD_ZERO(&descriptors_write);
163         int highest_fd = -1;
164         btstack_linked_list_iterator_init(&it, &data_sources);
165         while (btstack_linked_list_iterator_has_next(&it)){
166             btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it);
167             if (ds->fd < 0) continue;
168             if (ds->flags & DATA_SOURCE_CALLBACK_READ){
169                 FD_SET(ds->fd, &descriptors_read);
170                 if (ds->fd > highest_fd) {
171                     highest_fd = ds->fd;
172                 }
173                 log_debug("btstack_run_loop_execute adding fd %u for read", ds->fd);
174             }
175             if (ds->flags & DATA_SOURCE_CALLBACK_WRITE){
176                 FD_SET(ds->fd, &descriptors_write);
177                 if (ds->fd > highest_fd) {
178                     highest_fd = ds->fd;
179                 }
180                 log_debug("btstack_run_loop_execute adding fd %u for write", ds->fd);
181             }
182         }
183 
184         // get next timeout
185         timeout = NULL;
186         if (timers) {
187             ts = (btstack_timer_source_t *) timers;
188             timeout = &tv;
189             now_ms = btstack_run_loop_posix_get_time_ms();
190             int delta = ts->timeout - now_ms;
191             if (delta < 0){
192                 delta = 0;
193             }
194             tv.tv_sec  = delta / 1000;
195             tv.tv_usec = (int) (delta - (tv.tv_sec * 1000)) * 1000;
196             log_debug("btstack_run_loop_execute next timeout in %u ms", delta);
197         }
198 
199         // wait for ready FDs
200         select( highest_fd+1 , &descriptors_read, &descriptors_write, NULL, timeout);
201 
202 
203         data_sources_modified = 0;
204         btstack_linked_list_iterator_init(&it, &data_sources);
205         while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){
206             btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it);
207             log_debug("btstack_run_loop_posix_execute: check ds %p with fd %u\n", ds, ds->fd);
208             if (FD_ISSET(ds->fd, &descriptors_read)) {
209                 log_debug("btstack_run_loop_posix_execute: process read ds %p with fd %u\n", ds, ds->fd);
210                 ds->process(ds, DATA_SOURCE_CALLBACK_READ);
211             }
212             if (FD_ISSET(ds->fd, &descriptors_write)) {
213                 log_debug("btstack_run_loop_posix_execute: process write ds %p with fd %u\n", ds, ds->fd);
214                 ds->process(ds, DATA_SOURCE_CALLBACK_WRITE);
215             }
216         }
217         log_debug("btstack_run_loop_posix_execute: after ds check\n");
218 
219         // process timers
220         now_ms = btstack_run_loop_posix_get_time_ms();
221         while (timers) {
222             ts = (btstack_timer_source_t *) timers;
223             if (ts->timeout > now_ms) break;
224             log_debug("btstack_run_loop_posix_execute: process timer %p\n", ts);
225 
226             // remove timer before processing it to allow handler to re-register with run loop
227             btstack_run_loop_posix_remove_timer(ts);
228             ts->process(ts);
229         }
230     }
231 }
232 
233 // set timer
234 static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){
235     uint32_t time_ms = btstack_run_loop_posix_get_time_ms();
236     a->timeout = time_ms + timeout_in_ms;
237     log_debug("btstack_run_loop_posix_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms);
238 }
239 
240 static void btstack_run_loop_posix_init(void){
241     data_sources = NULL;
242     timers = NULL;
243     // just assume that we started at tv_usec == 0
244     gettimeofday(&init_tv, NULL);
245     init_tv.tv_usec = 0;
246     log_debug("btstack_run_loop_posix_init at %u/%u", (int) init_tv.tv_sec, 0);
247 }
248 
249 
250 static const btstack_run_loop_t btstack_run_loop_posix = {
251     &btstack_run_loop_posix_init,
252     &btstack_run_loop_posix_add_data_source,
253     &btstack_run_loop_posix_remove_data_source,
254     &btstack_run_loop_posix_enable_data_source_callbacks,
255     &btstack_run_loop_posix_disable_data_source_callbacks,
256     &btstack_run_loop_posix_set_timer,
257     &btstack_run_loop_posix_add_timer,
258     &btstack_run_loop_posix_remove_timer,
259     &btstack_run_loop_posix_execute,
260     &btstack_run_loop_posix_dump_timer,
261     &btstack_run_loop_posix_get_time_ms,
262 };
263 
264 /**
265  * Provide btstack_run_loop_posix instance
266  */
267 const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){
268     return &btstack_run_loop_posix;
269 }
270 
271