1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <stdlib.h>
6 #include <stdio.h>
7 #ifdef HAVE_SYS_SOCKET_H
8 # include <sys/socket.h>
9 #endif
10 #ifdef _WIN32
11 # include <ws2tcpip.h>
12 #endif
13 #ifdef HAVE_NETDB_H
14 # include <netdb.h>
15 #endif
16 #ifdef HAVE_NETINET_IN_H
17 # include <netinet/in.h>
18 #endif
19 
20 #include "Ecore.h"
21 #include "ecore_private.h"
22 
23 #ifdef HAVE_GLIB
24 # include <glib.h>
25 
26 static Eina_Bool _ecore_glib_active = EINA_FALSE;
27 static Ecore_Select_Function _ecore_glib_select_original;
28 static GPollFD *_ecore_glib_fds = NULL;
29 static size_t _ecore_glib_fds_size = 0;
30 static const size_t ECORE_GLIB_FDS_INITIAL = 128;
31 static const size_t ECORE_GLIB_FDS_STEP = 8;
32 static const size_t ECORE_GLIB_FDS_MAX_FREE = 256;
33 #if GLIB_CHECK_VERSION(2,32,0)
34 static GRecMutex *_ecore_glib_select_lock;
35 #else
36 static GStaticRecMutex *_ecore_glib_select_lock;
37 #endif
38 
39 static Eina_Bool
_ecore_glib_fds_resize(size_t size)40 _ecore_glib_fds_resize(size_t size)
41 {
42    void *tmp = realloc(_ecore_glib_fds, sizeof(GPollFD) * size);
43 
44    if (!tmp)
45      {
46         ERR("Could not realloc from %zu to %zu buckets.",
47             _ecore_glib_fds_size, size);
48         return EINA_FALSE;
49      }
50 
51    _ecore_glib_fds = tmp;
52    _ecore_glib_fds_size = size;
53    return EINA_TRUE;
54 }
55 
56 static int
_ecore_glib_context_query(GMainContext * ctx,int priority,int * p_timer)57 _ecore_glib_context_query(GMainContext *ctx,
58                           int           priority,
59                           int          *p_timer)
60 {
61    int reqfds;
62 
63    if (_ecore_glib_fds_size == 0)
64      {
65         if (!_ecore_glib_fds_resize(ECORE_GLIB_FDS_INITIAL)) return -1;
66      }
67 
68    while (1)
69      {
70         size_t size;
71 
72         reqfds = g_main_context_query
73             (ctx, priority, p_timer, _ecore_glib_fds, _ecore_glib_fds_size);
74         if (reqfds <= (int)_ecore_glib_fds_size) break;
75 
76         size = (1 + reqfds / ECORE_GLIB_FDS_STEP) * ECORE_GLIB_FDS_STEP;
77         if (!_ecore_glib_fds_resize(size)) return -1;
78      }
79 
80    if (reqfds + ECORE_GLIB_FDS_MAX_FREE < _ecore_glib_fds_size)
81      {
82         size_t size;
83 
84         size = (1 + reqfds / ECORE_GLIB_FDS_MAX_FREE) * ECORE_GLIB_FDS_MAX_FREE;
85         _ecore_glib_fds_resize(size);
86      }
87 
88    return reqfds;
89 }
90 
91 static int
_ecore_glib_context_poll_from(const GPollFD * pfds,int count,fd_set * rfds,fd_set * wfds,fd_set * efds)92 _ecore_glib_context_poll_from(const GPollFD *pfds,
93                               int            count,
94                               fd_set        *rfds,
95                               fd_set        *wfds,
96                               fd_set        *efds)
97 {
98    const GPollFD *itr = pfds, *itr_end = pfds + count;
99    int glib_fds = -1;
100 
101    for (; itr < itr_end; itr++)
102      {
103         if (glib_fds < itr->fd)
104           glib_fds = itr->fd;
105 
106         if (itr->events & G_IO_IN)
107           FD_SET(itr->fd, rfds);
108         if (itr->events & G_IO_OUT)
109           FD_SET(itr->fd, wfds);
110         if (itr->events & (G_IO_HUP | G_IO_ERR))
111           FD_SET(itr->fd, efds);
112      }
113 
114    return glib_fds + 1;
115 }
116 
117 static int
_ecore_glib_context_poll_to(GPollFD * pfds,int count,const fd_set * rfds,const fd_set * wfds,const fd_set * efds,int ready)118 _ecore_glib_context_poll_to(GPollFD      *pfds,
119                             int           count,
120                             const fd_set *rfds,
121                             const fd_set *wfds,
122                             const fd_set *efds,
123                             int           ready)
124 {
125    GPollFD *itr = pfds, *itr_end = pfds + count;
126    struct stat st;
127 
128    for (; (itr < itr_end) && (ready > 0); itr++)
129      {
130         itr->revents = 0;
131         if (FD_ISSET(itr->fd, rfds) && (itr->events & G_IO_IN))
132           {
133              itr->revents |= G_IO_IN;
134              ready--;
135           }
136         if (FD_ISSET(itr->fd, wfds) && (itr->events & G_IO_OUT))
137           {
138              itr->revents |= G_IO_OUT;
139              ready--;
140              if (!fstat(itr->fd, &st))
141                {
142                   if (S_ISSOCK(st.st_mode))
143                     {
144                        struct sockaddr_in peer;
145                        socklen_t length = sizeof(peer);
146 
147                        memset(&peer, 0, sizeof(peer));
148                        if (getpeername(itr->fd, (struct sockaddr *)&peer,
149                                        &length))
150                          itr->revents |= G_IO_ERR;
151                     }
152                }
153           }
154         if (FD_ISSET(itr->fd, efds) && (itr->events & (G_IO_HUP | G_IO_ERR)))
155           {
156              itr->revents |= G_IO_ERR;
157              ready--;
158           }
159      }
160    return ready;
161 }
162 
163 static int
_ecore_glib_select__locked(GMainContext * ctx,int ecore_fds,fd_set * rfds,fd_set * wfds,fd_set * efds,struct timeval * ecore_timeout)164 _ecore_glib_select__locked(GMainContext   *ctx,
165                            int             ecore_fds,
166                            fd_set         *rfds,
167                            fd_set         *wfds,
168                            fd_set         *efds,
169                            struct timeval *ecore_timeout)
170 {
171    int priority, maxfds, glib_fds, reqfds, reqtimeout, ret;
172    struct timeval *timeout, glib_timeout;
173 
174    g_main_context_prepare(ctx, &priority);
175    reqfds = _ecore_glib_context_query(ctx, priority, &reqtimeout);
176    if (reqfds < 0) goto error;
177 
178    glib_fds = _ecore_glib_context_poll_from
179        (_ecore_glib_fds, reqfds, rfds, wfds, efds);
180 
181    if (reqtimeout == -1)
182      timeout = ecore_timeout;
183    else
184      {
185         glib_timeout.tv_sec = reqtimeout / 1000;
186         glib_timeout.tv_usec = (reqtimeout % 1000) * 1000;
187 
188         if (!ecore_timeout || timercmp(ecore_timeout, &glib_timeout, >))
189           timeout = &glib_timeout;
190         else
191           timeout = ecore_timeout;
192      }
193 
194    maxfds = (ecore_fds >= glib_fds) ? ecore_fds : glib_fds;
195    ret = _ecore_glib_select_original(maxfds, rfds, wfds, efds, timeout);
196 
197    ret = _ecore_glib_context_poll_to
198        (_ecore_glib_fds, reqfds, rfds, wfds, efds, ret);
199 
200    if (g_main_context_check(ctx, priority, _ecore_glib_fds, reqfds))
201      g_main_context_dispatch(ctx);
202 
203    return ret;
204 
205 error:
206    return _ecore_glib_select_original
207             (ecore_fds, rfds, wfds, efds, ecore_timeout);
208 }
209 
210 static int
_ecore_glib_select(int ecore_fds,fd_set * rfds,fd_set * wfds,fd_set * efds,struct timeval * ecore_timeout)211 _ecore_glib_select(int             ecore_fds,
212                    fd_set         *rfds,
213                    fd_set         *wfds,
214                    fd_set         *efds,
215                    struct timeval *ecore_timeout)
216 {
217    GMainContext *ctx;
218    int ret;
219 
220    ctx = g_main_context_default();
221 
222    while (!g_main_context_acquire(ctx))
223      g_thread_yield();
224 
225 #if GLIB_CHECK_VERSION(2,32,0)
226    g_rec_mutex_lock(_ecore_glib_select_lock);
227 #else
228    g_static_rec_mutex_lock(_ecore_glib_select_lock);
229 #endif
230 
231    ret = _ecore_glib_select__locked
232        (ctx, ecore_fds, rfds, wfds, efds, ecore_timeout);
233 
234 #if GLIB_CHECK_VERSION(2,32,0)
235    g_rec_mutex_unlock(_ecore_glib_select_lock);
236 #else
237    g_static_rec_mutex_unlock(_ecore_glib_select_lock);
238 #endif
239    g_main_context_release(ctx);
240 
241    return ret;
242 }
243 
244 #endif
245 
246 void
_ecore_glib_init(void)247 _ecore_glib_init(void)
248 {
249 #ifdef HAVE_GLIB
250 #if GLIB_CHECK_VERSION(2,32,0)
251    _ecore_glib_select_lock = malloc(sizeof(GRecMutex));
252    g_rec_mutex_init(_ecore_glib_select_lock);
253 #else
254    if (!g_thread_get_initialized()) g_thread_init(NULL);
255    _ecore_glib_select_lock = malloc(sizeof(GStaticRecMutex));
256    g_static_rec_mutex_init(_ecore_glib_select_lock);
257 #endif
258 #endif
259 }
260 
261 void
_ecore_glib_shutdown(void)262 _ecore_glib_shutdown(void)
263 {
264 #ifdef HAVE_GLIB
265    if (!_ecore_glib_active) return;
266    _ecore_glib_active = EINA_FALSE;
267 
268    if (ecore_main_loop_select_func_get() == _ecore_glib_select)
269      ecore_main_loop_select_func_set(_ecore_glib_select_original);
270 
271    if (_ecore_glib_fds)
272      {
273         free(_ecore_glib_fds);
274         _ecore_glib_fds = NULL;
275      }
276    _ecore_glib_fds_size = 0;
277 
278 #if GLIB_CHECK_VERSION(2,32,0)
279    g_rec_mutex_clear(_ecore_glib_select_lock);
280    free(_ecore_glib_select_lock);
281    _ecore_glib_select_lock = NULL;
282 #else
283    g_static_rec_mutex_free(_ecore_glib_select_lock);
284    _ecore_glib_select_lock = NULL;
285 #endif
286 #endif
287 }
288 
289 EAPI Eina_Bool
ecore_main_loop_glib_integrate(void)290 ecore_main_loop_glib_integrate(void)
291 {
292 #ifdef HAVE_GLIB
293    void *func;
294 
295    if (_ecore_glib_active) return EINA_TRUE;
296    func = ecore_main_loop_select_func_get();
297    if (func == _ecore_glib_select) return EINA_TRUE;
298    _ecore_glib_select_original = func;
299    ecore_main_loop_select_func_set(_ecore_glib_select);
300    _ecore_glib_active = EINA_TRUE;
301 
302    /* Init only when requested */
303    _ecore_glib_init();
304    return EINA_TRUE;
305 #else
306    ERR("No glib support");
307    return EINA_FALSE;
308 #endif
309 }
310 
311 Eina_Bool _ecore_glib_always_integrate = 1;
312 
313 EAPI void
ecore_main_loop_glib_always_integrate_disable(void)314 ecore_main_loop_glib_always_integrate_disable(void)
315 {
316    _ecore_glib_always_integrate = 0;
317 }
318