source: roaraudio/libroar/scheduler.c @ 5692:0480fc56e68a

Last change on this file since 5692:0480fc56e68a was 5692:0480fc56e68a, checked in by phi, 12 years ago

Added scheduler (mainly for IO events) (Closes: #206)

File size: 17.9 KB
Line 
1//scheduler.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012
5 *
6 *  This file is part of libroar a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  libroar is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 *  NOTE for everyone want's to change something and send patches:
25 *  read README and HACKING! There a addition information on
26 *  the license of this document you need to read before you send
27 *  any patches.
28 *
29 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
30 *  or libpulse*:
31 *  The libs libroaresd, libroararts and libroarpulse link this lib
32 *  and are therefore GPL. Because of this it may be illigal to use
33 *  them with any software that uses libesd, libartsc or libpulse*.
34 */
35
36#include "libroar.h"
37
38#define INIT_SIZE   8
39#define MAX_PROTOS 16
40
41struct protocol {
42 const struct roar_keyval * para;
43 ssize_t paralen;
44 struct roar_dl_lhandle * lhandle;
45 const struct roar_dl_proto * impl;
46};
47
48struct roar_scheduler {
49 size_t refc;
50 struct roar_scheduler_source ** sources;
51 size_t sources_len;
52 struct roar_vio_select * vios;
53 size_t vios_len;
54 struct roar_dl_fnreg callback;
55 struct protocol protos[MAX_PROTOS];
56};
57
58struct roar_scheduler * roar_scheduler_new(void) {
59 struct roar_scheduler * sched = roar_mm_malloc(sizeof(struct roar_scheduler));
60
61 if ( sched == NULL )
62  return NULL;
63
64 memset(sched, 0, sizeof(struct roar_scheduler));
65 sched->refc = 1;
66
67 sched->sources = roar_mm_malloc(INIT_SIZE*sizeof(struct roar_scheduler_source *));
68 if ( sched->sources != NULL ) {
69  sched->sources_len = INIT_SIZE;
70  memset(sched->sources, 0, INIT_SIZE*sizeof(struct roar_scheduler_source *));
71 }
72
73 sched->vios = roar_mm_malloc(sched->sources_len*sizeof(struct roar_vio_select));
74 if ( sched->vios != 0 )
75  sched->vios_len = sched->sources_len;
76
77 return sched;
78}
79
80#define _CHKSCHED(extra) if ( sched == NULL || (extra) ) { roar_err_set(ROAR_ERROR_FAULT); return -1; }
81
82int                     roar_scheduler_ref(struct roar_scheduler * sched) {
83 _CHKSCHED(0);
84
85 sched->refc++;
86
87 return 0;
88}
89
90int                     roar_scheduler_unref(struct roar_scheduler * sched) {
91 size_t i;
92
93 _CHKSCHED(0);
94
95 sched->refc--;
96
97 if (sched->refc)
98  return 0;
99
100 for (i = 0; i < sched->sources_len; i++)
101  if ( sched->sources[i] != NULL )
102   roar_scheduler_source_del(sched, sched->sources[i]);
103
104 if ( sched->vios != NULL )
105  roar_mm_free(sched->vios);
106 if ( sched->sources != NULL )
107  roar_mm_free(sched->sources);
108 roar_mm_free(sched);
109 return 0;
110}
111
112static void __delete_cpi_client(struct roar_scheduler * sched, struct roar_scheduler_source * cur, struct roar_dl_librarypara * para) {
113 if ( cur->handle.cpi.impl->unset_proto != NULL )
114  cur->handle.cpi.impl->unset_proto(cur->handle.cpi.client, cur->vio, &(cur->handle.cpi.obuffer), &(cur->handle.cpi.userdata), cur->handle.cpi.protopara, cur->handle.cpi.protoparalen, para);
115
116 roar_vio_close(cur->vio);
117 cur->vio = NULL;
118
119 if ( cur->handle.cpi.obuffer != NULL ) {
120  roar_buffer_free(cur->handle.cpi.obuffer);
121  cur->handle.cpi.obuffer = NULL;
122 }
123
124 if ( cur->handle.cpi.userdata != NULL ) {
125  roar_mm_free(cur->handle.cpi.userdata);
126  cur->handle.cpi.userdata = NULL;
127 }
128
129 roar_scheduler_source_del(sched, cur);
130}
131
132static int __flush_cpi_client(struct roar_scheduler * sched, struct roar_scheduler_source * cur, struct roar_dl_librarypara * para) {
133 size_t len;
134 ssize_t ret;
135 void * buf;
136
137 if ( roar_buffer_get_len(cur->handle.cpi.obuffer, &len) == -1 )
138  return 0;
139
140 if ( roar_buffer_get_data(cur->handle.cpi.obuffer, &buf) == -1 )
141  return 0;
142
143 ret = roar_vio_write(cur->vio, buf, len);
144
145 if ( ret < 1 )
146  return -1;
147
148 if ( ret == (ssize_t)len ) {
149  if ( roar_buffer_next(&(cur->handle.cpi.obuffer)) == -1 )
150   return -1;
151 } else {
152  if ( roar_buffer_set_offset(cur->handle.cpi.obuffer, ret) == -1 )
153   return -1;
154 }
155
156 return 0;
157}
158
159// what to do?:
160// 0. get all VIOs.
161// 1. get timeout or use internal default.
162// 2. run roar_vio_select().
163// 3. send all events based on results.
164// 4. send UPDATE to all plugins and containers.
165int                     roar_scheduler_iterate(struct roar_scheduler * sched) {
166 struct roar_vio_selecttv timeout = {8, 0}; // default: 8 sec. Just a random value.
167 struct roar_scheduler_source * cur, * new_client;
168 struct roar_dl_librarypara * para;
169 struct roar_dl_lhandle * lhandle;
170 ssize_t ret;
171 size_t i;
172 int have_timeout = 0;
173 size_t todo = 0;
174 int tmp;
175
176 _CHKSCHED(0);
177
178 if ( sched->vios == NULL || sched->vios_len < sched->sources_len ) {
179  if ( sched->vios != NULL ) {
180   roar_mm_free(sched->vios);
181   sched->vios = NULL;
182   sched->vios_len = 0;
183  }
184
185  sched->vios = roar_mm_malloc(sched->sources_len*sizeof(struct roar_vio_select));
186  if ( sched->vios != NULL )
187   sched->vios_len = sched->sources_len;
188 }
189
190 // error from roar_mm_malloc() is still set.
191 if ( sched->vios == NULL )
192  return -1;
193
194 memset(sched->vios, 0, sched->vios_len*sizeof(struct roar_vio_select));
195 for (i = 0; i < sched->vios_len; i++)
196  sched->vios[i].eventsq = ROAR_VIO_SELECT_NO_RETEST;
197
198 for (i = 0; i < sched->sources_len; i++) {
199  if ( (cur = sched->sources[i]) == NULL )
200   continue;
201  switch (cur->type) {
202   case ROAR_SCHEDULER_VIO:
203     ROAR_VIO_SELECT_SETVIO(&(sched->vios[i]), cur->vio, cur->handle.eventsq);
204     todo++;
205    break;
206   case ROAR_SCHEDULER_TIMEOUT:
207     timeout = cur->handle.timeout;
208     have_timeout = 1;
209     todo++;
210    break;
211   case ROAR_SCHEDULER_CPI_LISTEN:
212     ROAR_VIO_SELECT_SETVIO(&(sched->vios[i]), cur->vio, ROAR_VIO_SELECT_READ);
213     todo++;
214    break;
215   case ROAR_SCHEDULER_CPI_CLIENT:
216     tmp = 0;
217
218     if ( cur->handle.cpi.impl->status != NULL ) {
219      if ( cur->lhandle != NULL )
220       roar_dl_context_restore(cur->lhandle);
221
222      para = roar_dl_getpara(cur->lhandle);
223      if ( cur->handle.cpi.impl->status(cur->handle.cpi.client, cur->vio, &(cur->handle.cpi.obuffer), &(cur->handle.cpi.userdata), cur->handle.cpi.protopara, cur->handle.cpi.protoparalen, para) & ROAR_DL_PROTO_STATUS_RX_READY )
224       tmp |= ROAR_VIO_SELECT_READ;
225      if ( para != NULL )
226       roar_dl_para_unref(para);
227
228      if ( cur->lhandle != NULL )
229       roar_dl_context_store(cur->lhandle);
230     } else {
231      tmp |= ROAR_VIO_SELECT_READ;
232     }
233
234     if ( sched->sources[i]->handle.cpi.obuffer != NULL )
235      tmp |= ROAR_VIO_SELECT_WRITE;
236
237     ROAR_VIO_SELECT_SETVIO(&(sched->vios[i]), sched->sources[i]->vio, tmp);
238     todo++;
239    break;
240   case ROAR_SCHEDULER_PLUGIN:
241   case ROAR_SCHEDULER_PLUGINCONTAINER:
242     todo++;
243    break;
244#ifndef DEBUG
245   default: /* noop */ break;
246#endif
247  }
248 }
249
250 if (!todo) {
251  roar_err_set(ROAR_ERROR_NOENT);
252  return 0;
253 }
254
255 ret = roar_vio_select(sched->vios, sched->vios_len, &timeout, NULL);
256 if ( ret == -1 )
257  return -1;
258 if ( ret == 0 && !have_timeout )
259  return 1;
260
261 for (i = 0; i < sched->sources_len; i++) {
262  if ( (cur = sched->sources[i]) == NULL )
263   continue;
264  switch (cur->type) {
265   case ROAR_SCHEDULER_VIO:
266     if ( sched->vios[i].eventsa )
267      if ( cur->cb != NULL )
268       cur->cb(sched->sources[i], sched->sources[i]->userdata, sched->vios[i].eventsa);
269    break;
270   case ROAR_SCHEDULER_TIMEOUT:
271     if ( ret == 0 )
272      if ( cur->cb != NULL )
273       cur->cb(sched->sources[i], sched->sources[i]->userdata, 0);
274    break;
275   case ROAR_SCHEDULER_PLUGIN:
276     roar_dl_appsched_trigger(cur->lhandle, ROAR_DL_APPSCHED_UPDATE);
277    break;
278   case ROAR_SCHEDULER_PLUGINCONTAINER:
279     roar_plugincontainer_appsched_trigger(cur->handle.container, ROAR_DL_APPSCHED_UPDATE);
280    break;
281   case ROAR_SCHEDULER_CPI_LISTEN:
282     if ( !sched->vios[i].eventsa )
283      continue;
284
285     if ( cur->cb != NULL )
286      cur->cb(sched->sources[i], sched->sources[i]->userdata, sched->vios[i].eventsa);
287
288     new_client = roar_mm_malloc(sizeof(struct roar_scheduler_source));
289     if ( new_client == NULL )
290      continue;
291     memcpy(new_client, cur, sizeof(struct roar_scheduler_source));
292     new_client->type = ROAR_SCHEDULER_CPI_CLIENT;
293     new_client->flags = ROAR_SCHEDULER_FLAG_FREE;
294     new_client->vio   = roar_mm_malloc(sizeof(struct roar_vio_calls));
295
296     if ( new_client->vio == NULL ) {
297      roar_mm_free(new_client);
298      continue;
299     }
300
301     if ( roar_vio_accept(new_client->vio, cur->vio) == -1 ) {
302      roar_mm_free(new_client->vio);
303      roar_mm_free(new_client);
304      continue;
305     }
306
307     new_client->vio->flags |= ROAR_VIO_FLAGS_FREESELF;
308
309     if ( roar_scheduler_source_add(sched, new_client) == -1 ) {
310      roar_vio_close(new_client->vio);
311      roar_mm_free(new_client);
312     }
313     roar_vio_unref(new_client->vio);
314
315     if ( new_client->cb != NULL )
316      new_client->cb(new_client, new_client->userdata, 0);
317
318     if ( cur->handle.cpi.impl->set_proto != NULL ) {
319      lhandle = new_client->lhandle;
320      para = roar_dl_getpara(lhandle);
321
322      if ( lhandle != NULL )
323       roar_dl_context_restore(lhandle);
324      new_client->handle.cpi.impl->set_proto(new_client->handle.cpi.client, new_client->vio, &(new_client->handle.cpi.obuffer), &(new_client->handle.cpi.userdata), new_client->handle.cpi.protopara, new_client->handle.cpi.protoparalen, para);
325      if ( lhandle != NULL )
326       roar_dl_context_store(lhandle);
327
328      if ( para != NULL )
329       roar_dl_para_unref(para);
330     }
331    break;
332   case ROAR_SCHEDULER_CPI_CLIENT:
333     if ( !sched->vios[i].eventsa )
334      continue;
335
336     if ( cur->cb != NULL )
337      cur->cb(sched->sources[i], sched->sources[i]->userdata, sched->vios[i].eventsa);
338
339     lhandle = cur->lhandle;
340     para = roar_dl_getpara(lhandle);
341     tmp = 0;
342
343     if ( sched->vios[i].eventsa & ROAR_VIO_SELECT_WRITE ) {
344      if ( cur->handle.cpi.impl->flush != NULL ) {
345       if ( lhandle != NULL )
346        roar_dl_context_restore(lhandle);
347
348       cur->handle.cpi.impl->flush(cur->handle.cpi.client, cur->vio, &(cur->handle.cpi.obuffer), &(cur->handle.cpi.userdata), cur->handle.cpi.protopara, cur->handle.cpi.protoparalen, para);
349
350       if ( lhandle != NULL )
351        roar_dl_context_store(lhandle);
352      } else {
353        tmp = __flush_cpi_client(sched, cur, para);
354      }
355
356      if ( tmp == 0 && cur->handle.cpi.obuffer == NULL && cur->handle.cpi.impl->flushed != NULL ) {
357       if ( lhandle != NULL )
358        roar_dl_context_restore(lhandle);
359
360       cur->handle.cpi.impl->flushed(cur->handle.cpi.client, cur->vio, &(cur->handle.cpi.obuffer), &(cur->handle.cpi.userdata), cur->handle.cpi.protopara, cur->handle.cpi.protoparalen, para);
361
362       if ( lhandle != NULL )
363        roar_dl_context_store(lhandle);
364      }
365     }
366
367     if ( sched->vios[i].eventsa & ROAR_VIO_SELECT_READ ) {
368      if ( cur->handle.cpi.impl->handle != NULL ) {
369       if ( lhandle != NULL )
370        roar_dl_context_restore(lhandle);
371
372       if ( cur->handle.cpi.impl->handle(cur->handle.cpi.client, cur->vio, &(cur->handle.cpi.obuffer), &(cur->handle.cpi.userdata), cur->handle.cpi.protopara, cur->handle.cpi.protoparalen, para) == -1 ) {
373        tmp = -1;
374       }
375
376       if ( lhandle != NULL )
377        roar_dl_context_store(lhandle);
378      }
379     }
380
381     if ( tmp == -1 ) {
382      if ( lhandle != NULL )
383       roar_dl_context_restore(lhandle);
384      __delete_cpi_client(sched, cur, para);
385      if ( lhandle != NULL )
386       roar_dl_context_store(lhandle);
387     }
388
389     if ( para != NULL )
390      roar_dl_para_unref(para);
391    break;
392#ifndef DEBUG
393   default: /* noop */ break;
394#endif
395  }
396 }
397
398 return 1;
399}
400
401int                     roar_scheduler_run(struct roar_scheduler * sched) {
402 int ret;
403
404 _CHKSCHED(0);
405
406 while ((ret = roar_scheduler_iterate(sched)) > 0);
407
408 return ret;
409}
410
411static int __cpi_callback(enum roar_dl_fnreg_action action, int fn, int subtype, const void * object, size_t objectlen, int version, int options, void * userdata, struct roar_dl_lhandle * lhandle) {
412 const struct roar_dl_proto * impl = object;
413 struct roar_scheduler * sched = userdata;
414 struct roar_dl_librarypara * para;
415 size_t i;
416
417 (void)fn, (void)subtype, (void)version, (void)options;
418
419 if ( sched == NULL ) {
420  roar_err_set(ROAR_ERROR_FAULT);
421  return -1;
422 }
423
424 if ( objectlen != ROAR_DL_PROTO_SIZE ) {
425  ROAR_WARN("__cpi_callback(*): Library %p tries to register protocol with bad object length.", lhandle);
426  roar_err_set(ROAR_ERROR_BADLIB);
427  return -1;
428 }
429
430 switch (action)  {
431  case ROAR_DL_FNREG:
432    for (i = 0; i < MAX_PROTOS; i++) {
433     if ( sched->protos[i].impl == NULL ) {
434      memset(&(sched->protos[i]), 0, sizeof(sched->protos[i]));
435      sched->protos[i].para = NULL;
436      sched->protos[i].paralen = -1;
437      sched->protos[i].lhandle = lhandle;
438      sched->protos[i].impl = impl;
439      return 0;
440     }
441    }
442   break;
443  case ROAR_DL_FNUNREG:
444    for (i = 0; i < sched->sources_len; i++) {
445     if ( sched->sources[i] == NULL )
446      continue;
447     if ( sched->sources[i]->type != ROAR_SCHEDULER_CPI_LISTEN && sched->sources[i]->type != ROAR_SCHEDULER_CPI_CLIENT )
448      continue;
449     if ( sched->sources[i]->handle.cpi.proto != impl->proto )
450      continue;
451
452     para = roar_dl_getpara(lhandle);
453
454     if ( lhandle != NULL )
455      roar_dl_context_restore(lhandle);
456     __delete_cpi_client(sched, sched->sources[i], para);
457     if ( lhandle != NULL )
458      roar_dl_context_store(lhandle);
459
460     if ( para != NULL )
461      roar_dl_para_unref(para);
462    }
463
464    for (i = 0; i < MAX_PROTOS; i++) {
465     if ( sched->protos[i].impl != NULL && sched->protos[i].lhandle == lhandle ) {
466      memset(&(sched->protos[i]), 0, sizeof(sched->protos[i]));
467      sched->protos[i].impl = NULL;
468     }
469    }
470    return 0;
471   break;
472 }
473
474 roar_err_set(ROAR_ERROR_NOSPC);
475 return -1;
476}
477
478static int __update_cpi_service (struct roar_scheduler * sched, struct roar_scheduler_source * source, int del) {
479 if ( del ) {
480  roar_err_set(ROAR_ERROR_NOTSUP);
481  return -1;
482 }
483
484 sched->callback.fn = ROAR_DL_FN_PROTO;
485 sched->callback.subtype = ROAR_DL_PROTO_SUBTYPE;
486 sched->callback.version = ROAR_DL_PROTO_VERSION;
487 sched->callback.callback = __cpi_callback;
488 sched->callback.userdata = sched;
489
490 ROAR_DL_RFNREG(ROAR_DL_HANDLE_LIBROAR, sched->callback);
491
492 return -1;
493}
494
495static int __update_cpi_listen_client (struct roar_scheduler * sched, struct roar_scheduler_source * source) {
496 size_t i;
497
498 ROAR_DBG("__update_cpi_listen_client(sched=%p, source=%p): proto=%i, impl=%p", sched, source, source->handle.cpi.proto, source->handle.cpi.impl);
499
500 if ( source->handle.cpi.proto < 1 && source->handle.cpi.impl != NULL )
501  source->handle.cpi.proto = source->handle.cpi.impl->proto;
502
503 if ( source->handle.cpi.proto > 0 && source->handle.cpi.impl == NULL ) {
504  for (i = 0; i < MAX_PROTOS; i++) {
505   if ( sched->protos[i].impl == NULL )
506    continue;
507   ROAR_DBG("__update_cpi_listen_client(sched=%p, source=%p): proto=%i<->%i", sched, source, sched->protos[i].impl->proto, source->handle.cpi.proto);
508   if ( sched->protos[i].impl->proto != source->handle.cpi.proto )
509    continue;
510   source->handle.cpi.impl = sched->protos[i].impl;
511   break;
512  }
513 }
514
515 if ( source->handle.cpi.proto > 0 && source->handle.cpi.impl != NULL )
516  return 0;
517
518 roar_err_set(ROAR_ERROR_INVAL);
519 return -1;
520}
521
522int                     roar_scheduler_source_add(struct roar_scheduler * sched,
523                                                  struct roar_scheduler_source * source) {
524 size_t i;
525 struct roar_scheduler_source ** next = NULL;
526 int err;
527 
528 _CHKSCHED(source == NULL);
529
530 ROAR_DBG("roar_scheduler_source_add(sched=%p, source=%p): proto=%i, impl=%p", sched, source, source->handle.cpi.proto, source->handle.cpi.impl);
531
532 for (i = 0; i < sched->sources_len; i++) {
533  if ( sched->sources[i] != NULL )
534   continue;
535  next = &(sched->sources[i]);
536  break;
537 }
538
539 if ( next == NULL ) {
540  // TODO: re-allocate some space here.
541  roar_err_set(ROAR_ERROR_NOSPC);
542  return -1;
543 }
544
545 switch (source->type) {
546  case ROAR_SCHEDULER_CPI_LISTEN:
547  case ROAR_SCHEDULER_CPI_CLIENT:
548    if ( __update_cpi_listen_client(sched, source) == -1 )
549     return -1;
550   break;
551  case ROAR_SCHEDULER_CPI_SERVICE:
552    if ( __update_cpi_service(sched, source, 0) == -1 )
553     return -1;
554   break;
555#ifndef DEBUG
556   default: /* noop */ break;
557#endif
558 }
559
560 if ( source->lhandle != NULL )
561  if ( roar_dl_ref(source->lhandle) == -1 )
562   return -1;
563
564 if ( source->vio != NULL ) {
565  if ( roar_vio_ref(source->vio) == -1 ) {
566   err = roar_error;
567   if ( source->lhandle != NULL )
568    roar_dl_unref(source->lhandle);
569   roar_err_set(err);
570   return -1;
571  }
572 }
573
574 if ( source->type == ROAR_SCHEDULER_PLUGINCONTAINER ) {
575  if ( roar_plugincontainer_ref(source->handle.container) == -1 ) {
576   err = roar_error;
577   if ( source->lhandle != NULL )
578    roar_dl_unref(source->lhandle);
579   if ( source->vio != NULL )
580    roar_vio_unref(source->vio);
581   roar_err_set(err);
582   return -1;
583  }
584 }
585
586 *next = source;
587
588 return 0;
589}
590
591int                     roar_scheduler_source_del(struct roar_scheduler * sched,
592                                                  struct roar_scheduler_source * source) {
593 size_t i;
594 struct roar_scheduler_source ** next = NULL;
595
596 _CHKSCHED(source == NULL);
597
598 for (i = 0; i < sched->sources_len; i++) {
599  if ( sched->sources[i] != source )
600   continue;
601  next = &(sched->sources[i]);
602  break;
603 }
604
605 if ( next == NULL ) {
606  roar_err_set(ROAR_ERROR_NOENT);
607  return -1;
608 }
609
610 switch (source->type) {
611  case ROAR_SCHEDULER_PLUGINCONTAINER:
612    roar_plugincontainer_ref(source->handle.container);
613   break;
614  case ROAR_SCHEDULER_CPI_SERVICE:
615    if ( __update_cpi_service(sched, source, 1) == -1 )
616     return -1;
617   break;
618#ifndef DEBUG
619   default: /* noop */ break;
620#endif
621 }
622
623 if ( source->lhandle != NULL )
624  roar_dl_unref(source->lhandle);
625 if ( source->vio != NULL )
626  roar_vio_unref(source->vio);
627
628 if ( source->flags & ROAR_SCHEDULER_FLAG_FREE )
629  roar_mm_free(source);
630
631 *next = NULL;
632 return 0;
633}
634
635//ll
Note: See TracBrowser for help on using the repository browser.