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