1 #ifndef __HIREDIS_GLIB_H__
2 #define __HIREDIS_GLIB_H__
3 
4 #include <glib.h>
5 
6 #include "../hiredis.h"
7 #include "../async.h"
8 
9 typedef struct
10 {
11     GSource source;
12     redisAsyncContext *ac;
13     GPollFD poll_fd;
14 } RedisSource;
15 
16 static void
redis_source_add_read(gpointer data)17 redis_source_add_read (gpointer data)
18 {
19     RedisSource *source = (RedisSource *)data;
20     g_return_if_fail(source);
21     source->poll_fd.events |= G_IO_IN;
22     g_main_context_wakeup(g_source_get_context((GSource *)data));
23 }
24 
25 static void
redis_source_del_read(gpointer data)26 redis_source_del_read (gpointer data)
27 {
28     RedisSource *source = (RedisSource *)data;
29     g_return_if_fail(source);
30     source->poll_fd.events &= ~G_IO_IN;
31     g_main_context_wakeup(g_source_get_context((GSource *)data));
32 }
33 
34 static void
redis_source_add_write(gpointer data)35 redis_source_add_write (gpointer data)
36 {
37     RedisSource *source = (RedisSource *)data;
38     g_return_if_fail(source);
39     source->poll_fd.events |= G_IO_OUT;
40     g_main_context_wakeup(g_source_get_context((GSource *)data));
41 }
42 
43 static void
redis_source_del_write(gpointer data)44 redis_source_del_write (gpointer data)
45 {
46     RedisSource *source = (RedisSource *)data;
47     g_return_if_fail(source);
48     source->poll_fd.events &= ~G_IO_OUT;
49     g_main_context_wakeup(g_source_get_context((GSource *)data));
50 }
51 
52 static void
redis_source_cleanup(gpointer data)53 redis_source_cleanup (gpointer data)
54 {
55     RedisSource *source = (RedisSource *)data;
56 
57     g_return_if_fail(source);
58 
59     redis_source_del_read(source);
60     redis_source_del_write(source);
61     /*
62      * It is not our responsibility to remove ourself from the
63      * current main loop. However, we will remove the GPollFD.
64      */
65     if (source->poll_fd.fd >= 0) {
66         g_source_remove_poll((GSource *)data, &source->poll_fd);
67         source->poll_fd.fd = -1;
68     }
69 }
70 
71 static gboolean
redis_source_prepare(GSource * source,gint * timeout_)72 redis_source_prepare (GSource *source,
73                       gint    *timeout_)
74 {
75     RedisSource *redis = (RedisSource *)source;
76     *timeout_ = -1;
77     return !!(redis->poll_fd.events & redis->poll_fd.revents);
78 }
79 
80 static gboolean
redis_source_check(GSource * source)81 redis_source_check (GSource *source)
82 {
83     RedisSource *redis = (RedisSource *)source;
84     return !!(redis->poll_fd.events & redis->poll_fd.revents);
85 }
86 
87 static gboolean
redis_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)88 redis_source_dispatch (GSource      *source,
89                        GSourceFunc   callback,
90                        gpointer      user_data)
91 {
92     RedisSource *redis = (RedisSource *)source;
93 
94     if ((redis->poll_fd.revents & G_IO_OUT)) {
95         redisAsyncHandleWrite(redis->ac);
96         redis->poll_fd.revents &= ~G_IO_OUT;
97     }
98 
99     if ((redis->poll_fd.revents & G_IO_IN)) {
100         redisAsyncHandleRead(redis->ac);
101         redis->poll_fd.revents &= ~G_IO_IN;
102     }
103 
104     if (callback) {
105         return callback(user_data);
106     }
107 
108     return TRUE;
109 }
110 
111 static void
redis_source_finalize(GSource * source)112 redis_source_finalize (GSource *source)
113 {
114     RedisSource *redis = (RedisSource *)source;
115 
116     if (redis->poll_fd.fd >= 0) {
117         g_source_remove_poll(source, &redis->poll_fd);
118         redis->poll_fd.fd = -1;
119     }
120 }
121 
122 static GSource *
redis_source_new(redisAsyncContext * ac)123 redis_source_new (redisAsyncContext *ac)
124 {
125     static GSourceFuncs source_funcs = {
126         .prepare  = redis_source_prepare,
127         .check     = redis_source_check,
128         .dispatch = redis_source_dispatch,
129         .finalize = redis_source_finalize,
130     };
131     redisContext *c = &ac->c;
132     RedisSource *source;
133 
134     g_return_val_if_fail(ac != NULL, NULL);
135 
136     source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
137     source->ac = ac;
138     source->poll_fd.fd = c->fd;
139     source->poll_fd.events = 0;
140     source->poll_fd.revents = 0;
141     g_source_add_poll((GSource *)source, &source->poll_fd);
142 
143     ac->ev.addRead = redis_source_add_read;
144     ac->ev.delRead = redis_source_del_read;
145     ac->ev.addWrite = redis_source_add_write;
146     ac->ev.delWrite = redis_source_del_write;
147     ac->ev.cleanup = redis_source_cleanup;
148     ac->ev.data = source;
149 
150     return (GSource *)source;
151 }
152 
153 #endif /* __HIREDIS_GLIB_H__ */
154