xref: /btstack/platform/corefoundation/btstack_run_loop_corefoundation.m (revision f8d5edf1c32c4c262255b765a9aae9bc20809ca1)
1/*
2 * Copyright (C) 2009-2012 by Matthias Ringwald
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 MATTHIAS RINGWALD 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 [email protected]
34 *
35 */
36
37#define __BTSTACK_FILE__ "btstack_run_loop_corefoundation.c"
38
39/*
40 *  btstack_run_loop_corefoundation.c
41 *
42 *  Created by Matthias Ringwald on 8/2/09.
43 */
44
45#include "btstack_run_loop.h"
46#include "btstack_debug.h"
47
48#import <Foundation/Foundation.h>
49#import <CoreFoundation/CoreFoundation.h>
50
51#include <stdio.h>
52#include <stdlib.h>
53#include <sys/time.h>
54#include <unistd.h>
55#include <pthread.h>
56
57static struct timeval init_tv;
58static CFAbsoluteTime init_cf;
59static CFRunLoopRef current_runloop;
60
61// to trigger run loop from other thread
62static int run_loop_pipe_fd;
63static btstack_data_source_t run_loop_pipe_ds;
64static pthread_mutex_t run_loop_callback_mutex = PTHREAD_MUTEX_INITIALIZER;
65
66typedef struct  {
67    CFSocketRef socket;
68    CFRunLoopSourceRef socket_run_loop;
69} btstack_corefoundation_data_source_helper_t;
70
71static void theCFRunLoopTimerCallBack (CFRunLoopTimerRef timer,void *info){
72    btstack_timer_source_t * ts = (btstack_timer_source_t*)info;
73    ts->process(ts);
74}
75
76static void socketDataCallback (
77						 CFSocketRef s,
78						 CFSocketCallBackType callbackType,
79						 CFDataRef address,
80						 const void *data,
81						 void *info) {
82	if (!info) return;
83    btstack_data_source_t * ds = (btstack_data_source_t *) info;
84
85    if ((callbackType == kCFSocketReadCallBack) && (ds->flags & DATA_SOURCE_CALLBACK_READ)){
86        // printf("btstack_run_loop_corefoundation_ds %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) ds, ds->source.fd, (int) s, (int) ds->item.next);
87        ds->process(ds, DATA_SOURCE_CALLBACK_READ);
88    }
89    if ((callbackType == kCFSocketWriteCallBack) && (ds->flags & DATA_SOURCE_CALLBACK_WRITE)){
90        // printf("btstack_run_loop_corefoundation_ds %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) ds, ds->source.fd, (int) s, (int) ds->item.next);
91        ds->process(ds, DATA_SOURCE_CALLBACK_WRITE);
92    }
93}
94
95static CFOptionFlags btstack_run_loop_corefoundation_option_flags_for_callback_types(uint16_t callback_types){
96    uint16_t option_flags = 0;
97    if (callback_types & DATA_SOURCE_CALLBACK_READ){
98        option_flags |= kCFSocketReadCallBack;
99    }
100    if (callback_types & DATA_SOURCE_CALLBACK_WRITE){
101        option_flags |= kCFSocketWriteCallBack;
102    }
103    return option_flags;
104}
105
106static void btstack_run_loop_corefoundation_add_data_source(btstack_data_source_t *data_source){
107
108	// add fd as CFSocket
109
110    // allocate memory for Core Foundation references
111    btstack_corefoundation_data_source_helper_t * references = malloc(sizeof(btstack_corefoundation_data_source_helper_t));
112    if (!references){
113        log_error("btstack_run_loop_corefoundation_add_data_source could not allocate btstack_corefoundation_data_source_helper_t");
114        return;
115    }
116
117	// store our data_source in socket context
118	CFSocketContext socketContext;
119	memset(&socketContext, 0, sizeof(CFSocketContext));
120	socketContext.info = data_source;
121
122	// create CFSocket from file descriptor
123    uint16_t callback_types = btstack_run_loop_corefoundation_option_flags_for_callback_types(data_source->flags);
124	CFSocketRef socket = CFSocketCreateWithNative (
125										  kCFAllocatorDefault,
126										  data_source->source.fd,
127										  callback_types,
128										  socketDataCallback,
129										  &socketContext
130    );
131
132    // configure options:
133    // - don't close native fd on CFSocketInvalidate
134    CFOptionFlags socket_options = CFSocketGetSocketFlags(socket);
135    socket_options &= ~kCFSocketCloseOnInvalidate;
136    CFSocketSetSocketFlags(socket, socket_options);
137
138	// create run loop source
139	CFRunLoopSourceRef socket_run_loop = CFSocketCreateRunLoopSource ( kCFAllocatorDefault, socket, 0);
140
141    // store CFSocketRef and CFRunLoopSource in struct on heap
142    memset(references, 0, sizeof(btstack_corefoundation_data_source_helper_t));
143    references->socket = socket;
144    references->socket_run_loop = socket_run_loop;
145
146    // hack: store btstack_corefoundation_data_source_helper_t in "next" of btstack_linked_item_t
147    data_source->item.next      = (void *) references;
148
149    // add to run loop
150	CFRunLoopAddSource( CFRunLoopGetCurrent(), socket_run_loop, kCFRunLoopCommonModes);
151    // printf("btstack_run_loop_corefoundation_add_data_source    %x - fd %u - CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->source.fd, (int) socket, (int) socket_run_loop);
152
153}
154
155static void btstack_run_loop_corefoundation_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){
156    btstack_corefoundation_data_source_helper_t * references = (btstack_corefoundation_data_source_helper_t *) ds->item.next;
157    uint16_t option_flags = btstack_run_loop_corefoundation_option_flags_for_callback_types(callback_types);
158    CFSocketEnableCallBacks(references->socket, option_flags);
159}
160
161static void btstack_run_loop_corefoundation_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){
162    btstack_corefoundation_data_source_helper_t * references = (btstack_corefoundation_data_source_helper_t *) ds->item.next;
163    uint16_t option_flags = btstack_run_loop_corefoundation_option_flags_for_callback_types(callback_types);
164    CFSocketDisableCallBacks(references->socket, option_flags);
165}
166
167static bool  btstack_run_loop_corefoundation_remove_data_source(btstack_data_source_t *ds){
168    btstack_corefoundation_data_source_helper_t * references = (btstack_corefoundation_data_source_helper_t *) ds->item.next;
169    // printf("btstack_run_loop_corefoundation_remove_data_source %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->source.fd, (int) dataSource->item.next, (int) dataSource->item.user_data);
170    CFRunLoopRemoveSource( CFRunLoopGetCurrent(), references->socket_run_loop, kCFRunLoopCommonModes);
171    CFRelease(references->socket_run_loop);
172
173    CFSocketInvalidate(references->socket);
174    CFRelease(references->socket);
175
176    free(references);
177	return true;
178}
179
180static void btstack_run_loop_corefoundation_add_timer(btstack_timer_source_t * ts)
181{
182    CFAbsoluteTime fireDate = init_cf + ts->timeout;
183    CFRunLoopTimerContext timerContext = {0, ts, NULL, NULL, NULL};
184    CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate (kCFAllocatorDefault,fireDate,0,0,0,theCFRunLoopTimerCallBack,&timerContext);
185    CFRetain(timerRef);
186
187    // hack: store CFRunLoopTimerRef in next pointer of linked_item
188    ts->item.next = (void *)timerRef;
189    // printf("btstack_run_loop_corefoundation_add_timer %x -> %x now %f, then %f\n", (int) ts, (int) ts->item.next, CFAbsoluteTimeGetCurrent(),fireDate);
190    CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopCommonModes);
191}
192
193static bool btstack_run_loop_corefoundation_remove_timer(btstack_timer_source_t * ts){
194    // printf("btstack_run_loop_corefoundation_remove_timer %x -> %x\n", (int) ts, (int) ts->item.next);
195	if (ts->item.next != NULL) {
196    	CFRunLoopTimerInvalidate((CFRunLoopTimerRef) ts->item.next);    // also removes timer from run loops + releases it
197    	CFRelease((CFRunLoopTimerRef) ts->item.next);
198        return true;
199	}
200	return false;
201}
202
203/**
204 * @brief Queries the current time in ms since start
205 */
206static uint32_t btstack_run_loop_corefoundation_get_time_ms(void){
207    struct timeval tv;
208    gettimeofday(&tv, NULL);
209    uint32_t time_ms = ((tv.tv_sec  - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000);
210    log_debug("btstack_run_loop_corefoundation_get_time_ms: %u <- %u / %u", time_ms, (int) tv.tv_sec, (int) tv.tv_usec);
211    return time_ms;
212}
213
214// set timer
215static void btstack_run_loop_corefoundation_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){
216    uint32_t time_ms = btstack_run_loop_corefoundation_get_time_ms();
217    a->timeout = time_ms + timeout_in_ms;
218    log_debug("btstack_run_loop_corefoundation_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms);
219}
220
221static void btstack_run_loop_corefoundation_pipe_process(btstack_data_source_t * ds, btstack_data_source_callback_type_t callback_type){
222    UNUSED(callback_type);
223    uint8_t buffer[1];
224    (void) read(ds->source.fd, buffer, 1);
225    // execute callbacks - protect list with mutex
226    while (1){
227        pthread_mutex_lock(&run_loop_callback_mutex);
228        btstack_context_callback_registration_t * callback_registration = (btstack_context_callback_registration_t *) btstack_linked_list_pop(&btstack_run_loop_base_callbacks);
229        pthread_mutex_unlock(&run_loop_callback_mutex);
230        if (callback_registration == NULL){
231            break;
232        }
233        (*callback_registration->callback)(callback_registration->context);
234    }
235}
236
237static void btstack_run_loop_corefoundation_execute_on_main_thread(btstack_context_callback_registration_t * callback_registration){
238    // protect list with mutex
239    pthread_mutex_lock(&run_loop_callback_mutex);
240    btstack_run_loop_base_add_callback(callback_registration);
241    pthread_mutex_unlock(&run_loop_callback_mutex);
242    // trigger run loop
243    if (run_loop_pipe_fd >= 0){
244        const uint8_t x = (uint8_t) 'x';
245        (void) write(run_loop_pipe_fd, &x, 1);
246    }
247}
248
249static void btstack_run_loop_corefoundation_init(void){
250    gettimeofday(&init_tv, NULL);
251    // calc CFAbsoluteTime for init_tv
252    // note: timeval uses unix time: seconds since Jan 1st 1970, CF uses Jan 1st 2001 as reference date
253    init_cf = ((double)init_tv.tv_sec) + (((double)init_tv.tv_usec)/1000000.0) - kCFAbsoluteTimeIntervalSince1970; // unix time - since Jan 1st 1970
254
255    // create pipe and register as data source
256    int fildes[2];
257    int status = pipe(fildes);
258    if (status == 0 ) {
259        run_loop_pipe_fd  = fildes[1];
260        run_loop_pipe_ds.source.fd = fildes[0];
261        run_loop_pipe_ds.flags = DATA_SOURCE_CALLBACK_READ;
262        run_loop_pipe_ds.process = &btstack_run_loop_corefoundation_pipe_process;
263        btstack_run_loop_base_add_data_source(&run_loop_pipe_ds);
264        log_info("Pipe in %u, out %u", run_loop_pipe_fd, fildes[0]);
265    } else {
266        run_loop_pipe_fd  = -1;
267        log_error("pipe() failed");
268    }
269}
270
271static void btstack_run_loop_corefoundation_execute(void) {
272    current_runloop = CFRunLoopGetCurrent();
273    CFRunLoopRun();
274}
275
276static void btstack_run_loop_corefoundation_trigger_exit(void){
277    if (current_runloop != NULL){
278        CFRunLoopStop(current_runloop);
279        current_runloop = NULL;
280    }
281}
282static void btstack_run_loop_corefoundation_dump_timer(void){
283    log_error("WARNING: btstack_run_loop_dump_timer not implemented!");
284	return;
285}
286
287static const btstack_run_loop_t btstack_run_loop_corefoundation = {
288    &btstack_run_loop_corefoundation_init,
289    &btstack_run_loop_corefoundation_add_data_source,
290    &btstack_run_loop_corefoundation_remove_data_source,
291    &btstack_run_loop_corefoundation_enable_data_source_callbacks,
292    &btstack_run_loop_corefoundation_disable_data_source_callbacks,
293    &btstack_run_loop_corefoundation_set_timer,
294    &btstack_run_loop_corefoundation_add_timer,
295    &btstack_run_loop_corefoundation_remove_timer,
296    &btstack_run_loop_corefoundation_execute,
297    &btstack_run_loop_corefoundation_dump_timer,
298    &btstack_run_loop_corefoundation_get_time_ms,
299    NULL, /* poll data sources from irq */
300    &btstack_run_loop_corefoundation_execute_on_main_thread,
301    &btstack_run_loop_corefoundation_trigger_exit,
302};
303
304/**
305 * Provide btstack_run_loop_corefoundation instance
306 */
307const btstack_run_loop_t * btstack_run_loop_corefoundation_get_instance(void){
308    return &btstack_run_loop_corefoundation;
309}
310