source: roaraudio/libroarpulse/mainloop.c @ 3481:22f4d04a1114

Last change on this file since 3481:22f4d04a1114 was 3481:22f4d04a1114, checked in by phi, 14 years ago

restarted poll() in case we got interuppted

File size: 9.2 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#include <poll.h>
41
42#define MAX_IO_EVENTS    8
43
44struct pa_io_event {
45 int used;
46 pa_mainloop_api *api;
47 int fd;
48 pa_io_event_flags_t events;
49 pa_io_event_cb_t cb;
50 void *userdata;
51 pa_io_event_destroy_cb_t destroy;
52};
53
54struct pa_mainloop {
55 pa_mainloop_api api;
56 pa_poll_func    poll_func;
57 void          * poll_userdata;
58 int             poll_timeout;
59 int             quit;
60 int             quitval;
61 pa_io_event     io_event[MAX_IO_EVENTS];
62 struct pollfd   pollfd[MAX_IO_EVENTS];
63 nfds_t          pollfds;
64};
65
66// IO EVENTS:
67static void _roar_pa_io_enable(pa_io_event* e, pa_io_event_flags_t events);
68
69/** Create a new IO event source object */
70static pa_io_event* _roar_pa_io_new(pa_mainloop_api*a, int fd,
71                                    pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) {
72 pa_mainloop * mainloop = a->userdata;
73 pa_io_event * ret = NULL;
74 int i;
75
76 for (i = 0; i < MAX_IO_EVENTS; i++) {
77  if ( mainloop->io_event[i].used == 0 ) {
78   ret = &(mainloop->io_event[i]);
79  }
80 }
81
82 if ( ret == NULL )
83  return NULL;
84
85 ret->used = 1;
86
87 ret->api      = a;
88 ret->fd       = fd;
89 ret->cb       = cb;
90 ret->userdata = userdata;
91
92 // Callbacks:
93 ret->destroy  = NULL;
94
95 _roar_pa_io_enable(ret, events);
96
97 return ret;
98}
99/** Enable or disable IO events on this object */
100static void _roar_pa_io_enable(pa_io_event* e, pa_io_event_flags_t events) {
101 if ( e == NULL )
102  return;
103
104 e->events = events;
105}
106
107/** Free a IO event source object */
108static void _roar_pa_io_free(pa_io_event* e) {
109 if ( e == NULL )
110  return;
111
112 if ( e->destroy != NULL )
113  e->destroy(e->api, e, e->userdata);
114
115 e->used = 0;
116}
117/** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
118static void _roar_pa_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) {
119 if ( e == NULL )
120  return;
121
122 e->destroy = cb;
123}
124
125
126
127// API:
128
129/** Allocate a new main loop object */
130pa_mainloop *pa_mainloop_new(void) {
131 pa_mainloop * m = roar_mm_malloc(sizeof(pa_mainloop));
132
133 ROAR_DBG("pa_mainloop_new() = ?");
134
135 if ( m == NULL )
136  return NULL;
137
138 memset(m, 0, sizeof(pa_mainloop));
139
140 m->api.userdata       = m;
141 m->api.io_new         = _roar_pa_io_new;
142 m->api.io_enable      = _roar_pa_io_enable;
143 m->api.io_free        = _roar_pa_io_free;
144 m->api.io_set_destroy = _roar_pa_io_set_destroy;
145
146 ROAR_DBG("pa_mainloop_new() = %p", m);
147 return m;
148}
149
150/** Free a main loop object */
151void pa_mainloop_free(pa_mainloop* m) {
152 ROAR_DBG("pa_mainloop_free(m=%p) = ?", m);
153
154 if ( m == NULL )
155  return;
156
157 roar_mm_free(m);
158
159 ROAR_DBG("pa_mainloop_free(m=%p) = (void)", m);
160}
161
162/** Prepare for a single iteration of the main loop. Returns a negative value
163on error or exit request. timeout specifies a maximum timeout for the subsequent
164poll, or -1 for blocking behaviour. .*/
165int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
166 short events;
167 int i;
168
169 ROAR_DBG("pa_mainloop_prepare(m=%p, timeout=%i) = ?", m, timeout);
170
171 if ( m == NULL )
172  return -1;
173
174 if ( m->quit )
175  return -2;
176
177 m->pollfds = 0;
178
179 for (i = 0; i < MAX_IO_EVENTS; i++) {
180  if ( m->io_event[i].used ) {
181   events = 0;
182
183   if ( m->io_event[i].events & PA_IO_EVENT_INPUT )
184    events |= POLLIN;
185
186   if ( m->io_event[i].events & PA_IO_EVENT_OUTPUT )
187    events |= POLLOUT;
188
189   if ( m->io_event[i].events & PA_IO_EVENT_HANGUP )
190    events |= POLLHUP;
191
192   if ( m->io_event[i].events & PA_IO_EVENT_ERROR )
193    events |= POLLERR;
194
195   if ( events == 0 )
196    continue;
197
198   m->pollfd[m->pollfds].fd     = m->io_event[i].fd;
199   m->pollfd[m->pollfds].events = events;
200   m->pollfds++;
201  }
202 }
203
204 m->poll_timeout = timeout;
205
206 ROAR_DBG("pa_mainloop_prepare(m=%p, timeout=%i) = 0", m, timeout);
207 return 0;
208}
209
210/** Execute the previously prepared poll. Returns a negative value on error.*/
211int pa_mainloop_poll(pa_mainloop *m) {
212 int ret;
213 int alive = 1;
214
215 ROAR_DBG("pa_mainloop_poll(m=%p) = ?", m);
216
217 if ( m == NULL )
218  return -1;
219
220 if ( m->quit )
221  return -2;
222
223 while (alive) {
224  if ( m->poll_func != NULL ) {
225   ret = m->poll_func(m->pollfd, m->pollfds, m->poll_timeout, m->poll_userdata);
226  } else {
227   ret = poll(m->pollfd, m->pollfds, m->poll_timeout);
228  }
229
230  if ( ret != -1 || ( errno != EAGAIN && errno != EINTR ) ) {
231   alive = 0;
232  }
233 }
234
235 ROAR_DBG("pa_mainloop_poll(m=%p) = %i", m, ret);
236 return ret;
237}
238
239/** Dispatch timeout, io and deferred events from the previously executed poll. Returns
240a negative value on error. On success returns the number of source dispatched. */
241int pa_mainloop_dispatch(pa_mainloop *m) {
242 pa_io_event_flags_t events;
243 int count = 0;
244 int i, h;
245
246 ROAR_DBG("pa_mainloop_dispatch(m=%p) = ?", m);
247
248 if ( m == NULL )
249  return -1;
250
251 if ( m->quit )
252  return -2;
253
254 for (i = 0; i < m->pollfds; i++) {
255  if ( m->pollfd[i].revents != 0 ) {
256   for (h = 0; h < MAX_IO_EVENTS; h++) {
257    if ( m->io_event[h].fd == m->pollfd[i].fd ) {
258     events = PA_IO_EVENT_NULL;
259
260     if ( m->pollfd[i].revents & POLLIN )
261      events |= PA_IO_EVENT_INPUT;
262
263     if ( m->pollfd[i].revents & POLLOUT )
264      events |= PA_IO_EVENT_OUTPUT;
265
266     if ( m->pollfd[i].revents & POLLHUP )
267      events |= PA_IO_EVENT_HANGUP;
268
269     if ( m->pollfd[i].revents & POLLERR )
270      events |= PA_IO_EVENT_ERROR;
271
272     if ( m->io_event[h].cb != NULL )
273      m->io_event[h].cb(&(m->api), &(m->io_event[h]), m->pollfd[i].fd, events, m->io_event[h].userdata);
274
275     count++;
276    }
277   }
278  }
279 }
280
281 ROAR_DBG("pa_mainloop_dispatch(m=%p) = %i", m, count);
282 return count;
283}
284
285/** Return the return value as specified with the main loop's quit() routine. */
286int pa_mainloop_get_retval(pa_mainloop *m) {
287 if ( m == NULL )
288  return -1;
289
290 return m->quitval;
291}
292
293/** Run a single iteration of the main loop. This is a convenience function
294for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
295Returns a negative value on error or exit request. If block is nonzero,
296block for events if none are queued. Optionally return the return value as
297specified with the main loop's quit() routine in the integer variable retval points
298to. On success returns the number of sources dispatched in this iteration. */
299int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
300 int r;
301
302 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = ?", m, block, retval);
303
304 if ( m == NULL )
305  return -1;
306
307 r = pa_mainloop_prepare(m, block ? -1 : 0);
308
309 if ( r >= 0 )
310  r = pa_mainloop_poll(m);
311
312 if ( r > 0 )
313  r = pa_mainloop_dispatch(m);
314
315 if ( r == -2 && retval != NULL ) {
316  *retval = m->quitval;
317 }
318
319 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = %i", m, block, retval, r);
320 return r;
321}
322
323/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
324int pa_mainloop_run(pa_mainloop *m, int *retval) {
325 int r = 1;
326
327 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p) = ?", m, retval);
328
329 if ( m == NULL )
330  return -1;
331
332 while (!(m->quit) && r > 0) {
333  r = pa_mainloop_iterate(m, 1, retval);
334 }
335
336 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p): r=%i", m, retval, r);
337
338 if ( r == -2 )
339  return 1;
340
341 if ( r < 0 )
342  return -1;
343
344 return 0;
345}
346
347/** Return the abstract main loop abstraction layer vtable for this main loop. */
348pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
349 if ( m == NULL )
350  return NULL;
351
352 return &(m->api);
353}
354
355/** Shutdown the main loop */
356void pa_mainloop_quit(pa_mainloop *m, int r) {
357 if ( m == NULL )
358  return;
359
360 m->quitval = r;
361 m->quit    = 1;
362}
363
364/** Interrupt a running poll (for threaded systems) */
365void pa_mainloop_wakeup(pa_mainloop *m);
366
367/** Change the poll() implementation */
368void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
369 if ( m == NULL )
370  return;
371
372 m->poll_func     = poll_func;
373 m->poll_userdata = userdata;
374}
375
376//ll
Note: See TracBrowser for help on using the repository browser.