1 //
2 //  Created by Дмитрий Бахвалов on 13.07.15.
3 //  Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
4 //
5 
6 #ifndef __HIREDIS_MACOSX_H__
7 #define __HIREDIS_MACOSX_H__
8 
9 #include <CoreFoundation/CoreFoundation.h>
10 
11 #include "../hiredis.h"
12 #include "../async.h"
13 
14 typedef struct {
15     redisAsyncContext *context;
16     CFSocketRef socketRef;
17     CFRunLoopSourceRef sourceRef;
18 } RedisRunLoop;
19 
freeRedisRunLoop(RedisRunLoop * redisRunLoop)20 static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
21     if( redisRunLoop != NULL ) {
22         if( redisRunLoop->sourceRef != NULL ) {
23             CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
24             CFRelease(redisRunLoop->sourceRef);
25         }
26         if( redisRunLoop->socketRef != NULL ) {
27             CFSocketInvalidate(redisRunLoop->socketRef);
28             CFRelease(redisRunLoop->socketRef);
29         }
30         free(redisRunLoop);
31     }
32     return REDIS_ERR;
33 }
34 
redisMacOSAddRead(void * privdata)35 static void redisMacOSAddRead(void *privdata) {
36     RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
37     CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
38 }
39 
redisMacOSDelRead(void * privdata)40 static void redisMacOSDelRead(void *privdata) {
41     RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
42     CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
43 }
44 
redisMacOSAddWrite(void * privdata)45 static void redisMacOSAddWrite(void *privdata) {
46     RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
47     CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
48 }
49 
redisMacOSDelWrite(void * privdata)50 static void redisMacOSDelWrite(void *privdata) {
51     RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
52     CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
53 }
54 
redisMacOSCleanup(void * privdata)55 static void redisMacOSCleanup(void *privdata) {
56     RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
57     freeRedisRunLoop(redisRunLoop);
58 }
59 
redisMacOSAsyncCallback(CFSocketRef __unused s,CFSocketCallBackType callbackType,CFDataRef __unused address,const void __unused * data,void * info)60 static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
61     redisAsyncContext* context = (redisAsyncContext*) info;
62 
63     switch (callbackType) {
64         case kCFSocketReadCallBack:
65             redisAsyncHandleRead(context);
66             break;
67 
68         case kCFSocketWriteCallBack:
69             redisAsyncHandleWrite(context);
70             break;
71 
72         default:
73             break;
74     }
75 }
76 
redisMacOSAttach(redisAsyncContext * redisAsyncCtx,CFRunLoopRef runLoop)77 static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
78     redisContext *redisCtx = &(redisAsyncCtx->c);
79 
80     /* Nothing should be attached when something is already attached */
81     if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
82 
83     RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
84     if( !redisRunLoop ) return REDIS_ERR;
85 
86     /* Setup redis stuff */
87     redisRunLoop->context = redisAsyncCtx;
88 
89     redisAsyncCtx->ev.addRead  = redisMacOSAddRead;
90     redisAsyncCtx->ev.delRead  = redisMacOSDelRead;
91     redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
92     redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
93     redisAsyncCtx->ev.cleanup  = redisMacOSCleanup;
94     redisAsyncCtx->ev.data     = redisRunLoop;
95 
96     /* Initialize and install read/write events */
97     CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
98 
99     redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
100                                                        kCFSocketReadCallBack | kCFSocketWriteCallBack,
101                                                        redisMacOSAsyncCallback,
102                                                        &socketCtx);
103     if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
104 
105     redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
106     if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
107 
108     CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
109 
110     return REDIS_OK;
111 }
112 
113 #endif
114 
115