server: Convert com_mvatt() to lopsub.
[paraslash.git] / osx_write.c
index 0f9d960..18a2c08 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2012 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -43,9 +43,28 @@ struct private_osx_write_data {
        unsigned sample_format;
        /** Number of channels of the current audio stream. */
        unsigned channels;
-       /** Serializes access to buffer tree nodes. */
+       /**
+        * Serializes access to buffer tree nodes between the writer and
+        * the callback which runs in a different thread.
+        */
        int mutex;
-       /** The btr node of the callback. */
+       /**
+        * The btr node of the callback.
+        *
+        * Although access to the btr node is serialized between the writer and
+        * the callback via the above mutex, this does not stop other buffer
+        * tree nodes, for example the decoder, to race against the osx
+        * callback.
+        *
+        * However, since all operations on buffer tree nodes are local in the
+        * sense that they only affect one level in the buffer tree (i.e.
+        * parent or child nodes, but not the grandparent or the
+        * grandchildren), we may work around this problem by using another
+        * buffer tree node for the callback.
+        *
+        * The writer grabs the mutex in its post_select method and pushes down
+        * the buffers to the callback node.
+        */
        struct btr_node *callback_btrn;
 };
 
@@ -59,10 +78,14 @@ static OSStatus osx_callback(void *cb_arg, __a_unused AudioUnitRenderActionFlags
 {
        int i;
        struct writer_node *wn = cb_arg;
-       struct private_osx_write_data *powd = wn->private_data;
+       struct private_osx_write_data *powd;
        size_t samples_have, samples_want = 0;
 
+       powd = wn->private_data;
        mutex_lock(powd->mutex);
+       powd = wn->private_data;
+       if (!powd || !wn->btrn)
+               goto out;
        /*
         * We fill with zeros if no data was yet written and we do not have
         * enough to fill all buffers.
@@ -217,18 +240,18 @@ e0:
        return ret;
 }
 
-__malloc static void *osx_write_parse_config_or_die(const char *options)
+__malloc static void *osx_write_parse_config_or_die(int argc, char **argv)
 {
        struct osx_write_args_info *conf = para_calloc(sizeof(*conf));
 
        /* exits on errors */
-       osx_cmdline_parser_string(options, conf, "osx_write");
+       osx_write_cmdline_parser(argc, argv, conf);
        return conf;
 }
 
 static void osx_free_config(void *conf)
 {
-       osx_cmdline_parser_free(conf);
+       osx_write_cmdline_parser_free(conf);
 }
 
 static void osx_write_close(struct writer_node *wn)
@@ -251,9 +274,9 @@ static inline bool need_drain_delay(struct private_osx_write_data *powd)
        return btr_get_input_queue_size(powd->callback_btrn) != 0;
 }
 
-static void osx_write_pre_select(struct sched *s, struct task *t)
+static void osx_write_pre_select(struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_osx_write_data *powd = wn->private_data;
        int ret;
        bool drain_delay_nec = false;
@@ -266,7 +289,7 @@ static void osx_write_pre_select(struct sched *s, struct task *t)
        }
 
        mutex_lock(powd->mutex);
-       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0)
                drain_delay_nec = need_drain_delay(powd);
        mutex_unlock(powd->mutex);
@@ -278,44 +301,54 @@ static void osx_write_pre_select(struct sched *s, struct task *t)
        sched_request_timeout_ms(50, s);
 }
 
-static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
+static int osx_write_post_select(__a_unused struct sched *s, void *context)
 {
-       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct writer_node *wn = context;
        struct private_osx_write_data *powd = wn->private_data;
        struct btr_node *btrn = wn->btrn;
        int ret;
 
+       ret = task_get_notification(wn->task);
+       if (ret < 0)
+               goto fail;
        if (!powd) {
                ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
                if (ret == 0)
-                       return;
+                       return 0;
                if (ret < 0)
-                       goto remove_btrn;
+                       goto fail;
                ret = core_audio_init(wn);
                if (ret < 0)
-                       goto remove_btrn;
+                       goto fail;
                powd = wn->private_data;
-               AudioOutputUnitStart(powd->audio_unit);
+               ret = -E_UNIT_START;
+               if (AudioOutputUnitStart(powd->audio_unit) != noErr) {
+                       AudioUnitUninitialize(powd->audio_unit);
+                       CloseComponent(powd->audio_unit);
+                       btr_remove_node(&powd->callback_btrn);
+                       goto fail;
+               }
        }
        mutex_lock(powd->mutex);
-       btr_pushdown(btrn);
-       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_INTERNAL);
+       if (ret > 0)
+               btr_pushdown(btrn);
        if (ret < 0 && need_drain_delay(powd))
                ret = 0;
        mutex_unlock(powd->mutex);
-
        if (ret >= 0)
-               goto out;
-       AudioOutputUnitStop(powd->audio_unit);
-       AudioUnitUninitialize(powd->audio_unit);
-       CloseComponent(powd->audio_unit);
-       btr_remove_node(powd->callback_btrn);
-       btr_free_node(powd->callback_btrn);
-remove_btrn:
-       btr_remove_node(btrn);
+               return 0;
+fail:
+       assert(ret < 0);
+       if (powd && powd->callback_btrn) {
+               AudioOutputUnitStop(powd->audio_unit);
+               AudioUnitUninitialize(powd->audio_unit);
+               CloseComponent(powd->audio_unit);
+               btr_remove_node(&powd->callback_btrn);
+       }
+       btr_remove_node(&wn->btrn);
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
-out:
-       t->error = ret;
+       return ret;
 }
 
 /**
@@ -327,15 +360,12 @@ void osx_write_init(struct writer *w)
 {
        struct osx_write_args_info dummy;
 
-       osx_cmdline_parser_init(&dummy);
+       osx_write_cmdline_parser_init(&dummy);
        w->close = osx_write_close;
        w->pre_select = osx_write_pre_select;
        w->post_select = osx_write_post_select;
        w->parse_config_or_die = osx_write_parse_config_or_die;
        w->free_config = osx_free_config;
-       w->help = (struct ggo_help) {
-               .short_help = osx_write_args_info_help,
-               .detailed_help = osx_write_args_info_detailed_help
-       };
-       osx_cmdline_parser_free(&dummy);
+       w->help = (struct ggo_help)DEFINE_GGO_HELP(osx_write);
+       osx_write_cmdline_parser_free(&dummy);
 }