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 #include <stdio.h> 55 #include <stdlib.h> 56 57 static void btstack_run_loop_posix_dump_timer(void); 58 static int btstack_run_loop_posix_timeval_compare(struct timeval *a, struct timeval *b); 59 static int btstack_run_loop_posix_timer_compare(btstack_timer_source_t *a, btstack_timer_source_t *b); 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 static struct timeval init_tv; 66 67 /** 68 * Add data_source to run_loop 69 */ 70 static void btstack_run_loop_posix_add_data_source(btstack_data_source_t *ds){ 71 data_sources_modified = 1; 72 // log_info("btstack_run_loop_posix_add_data_source %x with fd %u\n", (int) ds, ds->fd); 73 btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds); 74 } 75 76 /** 77 * Remove data_source from run loop 78 */ 79 static int btstack_run_loop_posix_remove_data_source(btstack_data_source_t *ds){ 80 data_sources_modified = 1; 81 // log_info("btstack_run_loop_posix_remove_data_source %x\n", (int) ds); 82 return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds); 83 } 84 85 /** 86 * Add timer to run_loop (keep list sorted) 87 */ 88 static void btstack_run_loop_posix_add_timer(btstack_timer_source_t *ts){ 89 btstack_linked_item_t *it; 90 for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){ 91 if ((btstack_timer_source_t *) it->next == ts){ 92 log_error( "btstack_run_loop_timer_add error: timer to add already in list!"); 93 return; 94 } 95 if (btstack_run_loop_posix_timer_compare( (btstack_timer_source_t *) it->next, ts) > 0) { 96 break; 97 } 98 } 99 ts->item.next = it->next; 100 it->next = (btstack_linked_item_t *) ts; 101 // log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 102 // btstack_run_loop_posix_dump_timer(); 103 } 104 105 /** 106 * Remove timer from run loop 107 */ 108 static int btstack_run_loop_posix_remove_timer(btstack_timer_source_t *ts){ 109 // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 110 // btstack_run_loop_posix_dump_timer(); 111 return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts); 112 } 113 114 static void btstack_run_loop_posix_dump_timer(void){ 115 btstack_linked_item_t *it; 116 int i = 0; 117 for (it = (btstack_linked_item_t *) timers; it ; it = it->next){ 118 btstack_timer_source_t *ts = (btstack_timer_source_t*) it; 119 log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout.tv_sec); 120 } 121 } 122 123 /** 124 * Execute run_loop 125 */ 126 static void btstack_run_loop_posix_execute(void) { 127 fd_set descriptors; 128 129 btstack_timer_source_t *ts; 130 struct timeval current_tv; 131 struct timeval next_tv; 132 struct timeval *timeout; 133 btstack_linked_list_iterator_t it; 134 135 while (1) { 136 // collect FDs 137 FD_ZERO(&descriptors); 138 int highest_fd = 0; 139 btstack_linked_list_iterator_init(&it, &data_sources); 140 while (btstack_linked_list_iterator_has_next(&it)){ 141 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 142 if (ds->fd >= 0) { 143 FD_SET(ds->fd, &descriptors); 144 if (ds->fd > highest_fd) { 145 highest_fd = ds->fd; 146 } 147 } 148 } 149 150 // get next timeout 151 // pre: 0 <= tv_usec < 1000000 152 timeout = NULL; 153 if (timers) { 154 gettimeofday(¤t_tv, NULL); 155 ts = (btstack_timer_source_t *) timers; 156 next_tv.tv_usec = ts->timeout.tv_usec - current_tv.tv_usec; 157 next_tv.tv_sec = ts->timeout.tv_sec - current_tv.tv_sec; 158 while (next_tv.tv_usec < 0){ 159 next_tv.tv_usec += 1000000; 160 next_tv.tv_sec--; 161 } 162 if (next_tv.tv_sec < 0){ 163 next_tv.tv_sec = 0; 164 next_tv.tv_usec = 0; 165 } 166 timeout = &next_tv; 167 } 168 169 // wait for ready FDs 170 select( highest_fd+1 , &descriptors, NULL, NULL, timeout); 171 172 // process data sources very carefully 173 // bt_control.close() triggered from a client can remove a different data source 174 175 // log_info("btstack_run_loop_posix_execute: before ds check\n"); 176 data_sources_modified = 0; 177 btstack_linked_list_iterator_init(&it, &data_sources); 178 while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){ 179 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 180 // log_info("btstack_run_loop_posix_execute: check %x with fd %u\n", (int) ds, ds->fd); 181 if (FD_ISSET(ds->fd, &descriptors)) { 182 // log_info("btstack_run_loop_posix_execute: process %x with fd %u\n", (int) ds, ds->fd); 183 ds->process(ds); 184 } 185 } 186 // log_info("btstack_run_loop_posix_execute: after ds check\n"); 187 188 // process timers 189 // pre: 0 <= tv_usec < 1000000 190 while (timers) { 191 gettimeofday(¤t_tv, NULL); 192 ts = (btstack_timer_source_t *) timers; 193 if (ts->timeout.tv_sec > current_tv.tv_sec) break; 194 if (ts->timeout.tv_sec == current_tv.tv_sec && ts->timeout.tv_usec > current_tv.tv_usec) break; 195 // log_info("btstack_run_loop_posix_execute: process times %x\n", (int) ts); 196 197 // remove timer before processing it to allow handler to re-register with run loop 198 btstack_run_loop_remove_timer(ts); 199 ts->process(ts); 200 } 201 } 202 } 203 204 // set timer 205 static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ 206 gettimeofday(&a->timeout, NULL); 207 a->timeout.tv_sec += timeout_in_ms / 1000; 208 a->timeout.tv_usec += (timeout_in_ms % 1000) * 1000; 209 if (a->timeout.tv_usec > 1000000) { 210 a->timeout.tv_usec -= 1000000; 211 a->timeout.tv_sec++; 212 } 213 } 214 215 // compare timers - NULL is assumed to be before the Big Bang 216 // pre: 0 <= tv_usec < 1000000 217 static int btstack_run_loop_posix_timeval_compare(struct timeval *a, struct timeval *b){ 218 if (!a && !b) return 0; 219 if (!a) return -1; 220 if (!b) return 1; 221 222 if (a->tv_sec < b->tv_sec) { 223 return -1; 224 } 225 if (a->tv_sec > b->tv_sec) { 226 return 1; 227 } 228 229 if (a->tv_usec < b->tv_usec) { 230 return -1; 231 } 232 if (a->tv_usec > b->tv_usec) { 233 return 1; 234 } 235 236 return 0; 237 238 } 239 240 // compare timers - NULL is assumed to be before the Big Bang 241 // pre: 0 <= tv_usec < 1000000 242 static int btstack_run_loop_posix_timer_compare(btstack_timer_source_t *a, btstack_timer_source_t *b){ 243 if (!a && !b) return 0; 244 if (!a) return -1; 245 if (!b) return 1; 246 return btstack_run_loop_posix_timeval_compare(&a->timeout, &b->timeout); 247 } 248 249 static void btstack_run_loop_posix_init(void){ 250 data_sources = NULL; 251 timers = NULL; 252 gettimeofday(&init_tv, NULL); 253 } 254 255 /** 256 * @brief Queries the current time in ms since start 257 */ 258 static uint32_t btstack_run_loop_posix_get_time_ms(void){ 259 struct timeval current_tv; 260 gettimeofday(¤t_tv, NULL); 261 return (current_tv.tv_sec - init_tv.tv_sec) * 1000 262 + (current_tv.tv_usec - init_tv.tv_usec) / 1000; 263 } 264 265 266 static const btstack_run_loop_t btstack_run_loop_posix = { 267 &btstack_run_loop_posix_init, 268 &btstack_run_loop_posix_add_data_source, 269 &btstack_run_loop_posix_remove_data_source, 270 &btstack_run_loop_posix_set_timer, 271 &btstack_run_loop_posix_add_timer, 272 &btstack_run_loop_posix_remove_timer, 273 &btstack_run_loop_posix_execute, 274 &btstack_run_loop_posix_dump_timer, 275 &btstack_run_loop_posix_get_time_ms, 276 }; 277 278 /** 279 * Provide btstack_run_loop_posix instance 280 */ 281 const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){ 282 return &btstack_run_loop_posix; 283 } 284 285