source: roaraudio/libroarpulse/mainloop.c @ 3490:170391ca4c93

Last change on this file since 3490:170391ca4c93 was 3490:170391ca4c93, checked in by phi, 14 years ago

some workarounds for poll() on non-poll()-enabled systems

File size: 9.5 KB
Line 
1//mainloop.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010
5 *  The code (may) include prototypes and comments (and maybe
6 *  other code fragements) from libpulse*. They are mostly copyrighted by:
7 *  Lennart Poettering <poettering@users.sourceforge.net> and
8 *  Pierre Ossman <drzeus@drzeus.cx>
9 *
10 *  This file is part of libroarpulse a part of RoarAudio,
11 *  a cross-platform sound system for both, home and professional use.
12 *  See README for details.
13 *
14 *  This file is free software; you can redistribute it and/or modify
15 *  it under the terms of the GNU General Public License version 3
16 *  as published by the Free Software Foundation.
17 *
18 *  RoarAudio is distributed in the hope that it will be useful,
19 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 *  GNU General Public License for more details.
22 *
23 *  You should have received a copy of the GNU General Public License
24 *  along with this software; see the file COPYING.  If not, write to
25 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26 *
27 *  NOTE for everyone want's to change something and send patches:
28 *  read README and HACKING! There a addition information on
29 *  the license of this document you need to read before you send
30 *  any patches.
31 *
32 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
33 *  or libpulse*:
34 *  The libs libroaresd, libroararts and libroarpulse link this libroar
35 *  and are therefore GPL. Because of this it may be illigal to use
36 *  them with any software that uses libesd, libartsc or libpulse*.
37 */
38
39#include <libroarpulse/libroarpulse.h>
40#ifdef ROAR_HAVE_H_POLL
41#include <poll.h>
42#endif
43
44#define MAX_IO_EVENTS    8
45
46#ifndef ROAR_HAVE_H_POLL
47struct pollfd {
48 int fd;
49 short events, revents;
50};
51#endif
52
53struct pa_io_event {
54 int used;
55 pa_mainloop_api *api;
56 int fd;
57 pa_io_event_flags_t events;
58 pa_io_event_cb_t cb;
59 void *userdata;
60 pa_io_event_destroy_cb_t destroy;
61};
62
63struct pa_mainloop {
64 pa_mainloop_api api;
65 pa_poll_func    poll_func;
66 void          * poll_userdata;
67 int             poll_timeout;
68 int             quit;
69 int             quitval;
70 pa_io_event     io_event[MAX_IO_EVENTS];
71 struct pollfd   pollfd[MAX_IO_EVENTS];
72 nfds_t          pollfds;
73};
74
75// IO EVENTS:
76static void _roar_pa_io_enable(pa_io_event* e, pa_io_event_flags_t events);
77
78/** Create a new IO event source object */
79static pa_io_event* _roar_pa_io_new(pa_mainloop_api*a, int fd,
80                                    pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) {
81 pa_mainloop * mainloop = a->userdata;
82 pa_io_event * ret = NULL;
83 int i;
84
85 for (i = 0; i < MAX_IO_EVENTS; i++) {
86  if ( mainloop->io_event[i].used == 0 ) {
87   ret = &(mainloop->io_event[i]);
88  }
89 }
90
91 if ( ret == NULL )
92  return NULL;
93
94 ret->used = 1;
95
96 ret->api      = a;
97 ret->fd       = fd;
98 ret->cb       = cb;
99 ret->userdata = userdata;
100
101 // Callbacks:
102 ret->destroy  = NULL;
103
104 _roar_pa_io_enable(ret, events);
105
106 return ret;
107}
108/** Enable or disable IO events on this object */
109static void _roar_pa_io_enable(pa_io_event* e, pa_io_event_flags_t events) {
110 if ( e == NULL )
111  return;
112
113 e->events = events;
114}
115
116/** Free a IO event source object */
117static void _roar_pa_io_free(pa_io_event* e) {
118 if ( e == NULL )
119  return;
120
121 if ( e->destroy != NULL )
122  e->destroy(e->api, e, e->userdata);
123
124 e->used = 0;
125}
126/** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
127static void _roar_pa_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) {
128 if ( e == NULL )
129  return;
130
131 e->destroy = cb;
132}
133
134
135
136// API:
137
138void _roar_pa_mainloop_quit(pa_mainloop_api*a, int retval) {
139 pa_mainloop * m = a->userdata;
140 pa_mainloop_quit(m, retval);
141}
142
143/** Allocate a new main loop object */
144pa_mainloop *pa_mainloop_new(void) {
145 pa_mainloop * m = roar_mm_malloc(sizeof(pa_mainloop));
146
147 ROAR_DBG("pa_mainloop_new() = ?");
148
149 if ( m == NULL )
150  return NULL;
151
152 memset(m, 0, sizeof(pa_mainloop));
153
154 m->api.userdata       = m;
155
156 m->api.quit           = _roar_pa_mainloop_quit;
157
158 m->api.io_new         = _roar_pa_io_new;
159 m->api.io_enable      = _roar_pa_io_enable;
160 m->api.io_free        = _roar_pa_io_free;
161 m->api.io_set_destroy = _roar_pa_io_set_destroy;
162
163 ROAR_DBG("pa_mainloop_new() = %p", m);
164 return m;
165}
166
167/** Free a main loop object */
168void pa_mainloop_free(pa_mainloop* m) {
169 ROAR_DBG("pa_mainloop_free(m=%p) = ?", m);
170
171 if ( m == NULL )
172  return;
173
174 roar_mm_free(m);
175
176 ROAR_DBG("pa_mainloop_free(m=%p) = (void)", m);
177}
178
179/** Prepare for a single iteration of the main loop. Returns a negative value
180on error or exit request. timeout specifies a maximum timeout for the subsequent
181poll, or -1 for blocking behaviour. .*/
182int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
183 short events;
184 int i;
185
186 ROAR_DBG("pa_mainloop_prepare(m=%p, timeout=%i) = ?", m, timeout);
187
188 if ( m == NULL )
189  return -1;
190
191 if ( m->quit )
192  return -2;
193
194 m->pollfds = 0;
195
196 for (i = 0; i < MAX_IO_EVENTS; i++) {
197  if ( m->io_event[i].used ) {
198   events = 0;
199
200   if ( m->io_event[i].events & PA_IO_EVENT_INPUT )
201    events |= POLLIN;
202
203   if ( m->io_event[i].events & PA_IO_EVENT_OUTPUT )
204    events |= POLLOUT;
205
206   if ( m->io_event[i].events & PA_IO_EVENT_HANGUP )
207    events |= POLLHUP;
208
209   if ( m->io_event[i].events & PA_IO_EVENT_ERROR )
210    events |= POLLERR;
211
212   if ( events == 0 )
213    continue;
214
215   m->pollfd[m->pollfds].fd     = m->io_event[i].fd;
216   m->pollfd[m->pollfds].events = events;
217   m->pollfds++;
218  }
219 }
220
221 m->poll_timeout = timeout;
222
223 ROAR_DBG("pa_mainloop_prepare(m=%p, timeout=%i) = 0", m, timeout);
224 return 0;
225}
226
227/** Execute the previously prepared poll. Returns a negative value on error.*/
228int pa_mainloop_poll(pa_mainloop *m) {
229 int ret;
230 int alive = 1;
231
232 ROAR_DBG("pa_mainloop_poll(m=%p) = ?", m);
233
234 if ( m == NULL )
235  return -1;
236
237 if ( m->quit )
238  return -2;
239
240 while (alive) {
241  if ( m->poll_func != NULL ) {
242   ret = m->poll_func(m->pollfd, m->pollfds, m->poll_timeout, m->poll_userdata);
243  } else {
244#ifdef ROAR_HAVE_H_POLL
245   ret = poll(m->pollfd, m->pollfds, m->poll_timeout);
246#else
247   ret = -1;
248#endif
249  }
250
251  if ( ret != -1 || ( errno != EAGAIN && errno != EINTR ) ) {
252   alive = 0;
253  }
254 }
255
256 ROAR_DBG("pa_mainloop_poll(m=%p) = %i", m, ret);
257 return ret;
258}
259
260/** Dispatch timeout, io and deferred events from the previously executed poll. Returns
261a negative value on error. On success returns the number of source dispatched. */
262int pa_mainloop_dispatch(pa_mainloop *m) {
263 pa_io_event_flags_t events;
264 int count = 0;
265 int i, h;
266
267 ROAR_DBG("pa_mainloop_dispatch(m=%p) = ?", m);
268
269 if ( m == NULL )
270  return -1;
271
272 if ( m->quit )
273  return -2;
274
275 for (i = 0; i < m->pollfds; i++) {
276  if ( m->pollfd[i].revents != 0 ) {
277   for (h = 0; h < MAX_IO_EVENTS; h++) {
278    if ( m->io_event[h].fd == m->pollfd[i].fd ) {
279     events = PA_IO_EVENT_NULL;
280
281     if ( m->pollfd[i].revents & POLLIN )
282      events |= PA_IO_EVENT_INPUT;
283
284     if ( m->pollfd[i].revents & POLLOUT )
285      events |= PA_IO_EVENT_OUTPUT;
286
287     if ( m->pollfd[i].revents & POLLHUP )
288      events |= PA_IO_EVENT_HANGUP;
289
290     if ( m->pollfd[i].revents & POLLERR )
291      events |= PA_IO_EVENT_ERROR;
292
293     if ( m->io_event[h].cb != NULL )
294      m->io_event[h].cb(&(m->api), &(m->io_event[h]), m->pollfd[i].fd, events, m->io_event[h].userdata);
295
296     count++;
297    }
298   }
299  }
300 }
301
302 ROAR_DBG("pa_mainloop_dispatch(m=%p) = %i", m, count);
303 return count;
304}
305
306/** Return the return value as specified with the main loop's quit() routine. */
307int pa_mainloop_get_retval(pa_mainloop *m) {
308 if ( m == NULL )
309  return -1;
310
311 return m->quitval;
312}
313
314/** Run a single iteration of the main loop. This is a convenience function
315for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
316Returns a negative value on error or exit request. If block is nonzero,
317block for events if none are queued. Optionally return the return value as
318specified with the main loop's quit() routine in the integer variable retval points
319to. On success returns the number of sources dispatched in this iteration. */
320int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
321 int r;
322
323 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = ?", m, block, retval);
324
325 if ( m == NULL )
326  return -1;
327
328 r = pa_mainloop_prepare(m, block ? -1 : 0);
329
330 if ( r >= 0 )
331  r = pa_mainloop_poll(m);
332
333 if ( r > 0 )
334  r = pa_mainloop_dispatch(m);
335
336 if ( r == -2 && retval != NULL ) {
337  *retval = m->quitval;
338 }
339
340 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = %i", m, block, retval, r);
341 return r;
342}
343
344/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
345int pa_mainloop_run(pa_mainloop *m, int *retval) {
346 int r = 1;
347
348 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p) = ?", m, retval);
349
350 if ( m == NULL )
351  return -1;
352
353 while (!(m->quit) && r > 0) {
354  r = pa_mainloop_iterate(m, 1, retval);
355 }
356
357 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p): r=%i", m, retval, r);
358
359 if ( r == -2 )
360  return 1;
361
362 if ( r < 0 )
363  return -1;
364
365 return 0;
366}
367
368/** Return the abstract main loop abstraction layer vtable for this main loop. */
369pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
370 if ( m == NULL )
371  return NULL;
372
373 return &(m->api);
374}
375
376/** Shutdown the main loop */
377void pa_mainloop_quit(pa_mainloop *m, int r) {
378 if ( m == NULL )
379  return;
380
381 m->quitval = r;
382 m->quit    = 1;
383}
384
385/** Interrupt a running poll (for threaded systems) */
386void pa_mainloop_wakeup(pa_mainloop *m);
387
388/** Change the poll() implementation */
389void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
390 if ( m == NULL )
391  return;
392
393 m->poll_func     = poll_func;
394 m->poll_userdata = userdata;
395}
396
397//ll
Note: See TracBrowser for help on using the repository browser.