diff options
Diffstat (limited to 'player/command.c')
-rw-r--r-- | player/command.c | 170 |
1 files changed, 151 insertions, 19 deletions
diff --git a/player/command.c b/player/command.c index 3fe39642a6..4600e65236 100644 --- a/player/command.c +++ b/player/command.c @@ -60,7 +60,9 @@ #include "video/out/bitmap_packer.h" #include "options/path.h" #include "screenshot.h" +#include "misc/dispatch.h" #include "misc/node.h" +#include "misc/thread_pool.h" #include "osdep/io.h" #include "osdep/subprocess.h" @@ -4825,27 +4827,147 @@ static void cmd_cycle_values(void *p) change_property_cmd(cmd, name, M_PROPERTY_SET_STRING, cmd->args[current].v.s); } +struct cmd_list_ctx { + struct MPContext *mpctx; + + // actual list command + struct mp_cmd_ctx *parent; + + bool current_valid; + pthread_t current; + bool completed_recursive; + + // list of sub commands yet to run + struct mp_cmd **sub; + int num_sub; +}; + +static void continue_cmd_list(struct cmd_list_ctx *list); + +static void on_cmd_list_sub_completion(struct mp_cmd_ctx *cmd) +{ + struct cmd_list_ctx *list = cmd->on_completion_priv; + + if (list->current_valid && pthread_equal(list->current, pthread_self())) { + list->completed_recursive = true; + } else { + continue_cmd_list(list); + } +} + +static void continue_cmd_list(struct cmd_list_ctx *list) +{ + while (list->parent->args[0].v.p) { + struct mp_cmd *sub = list->parent->args[0].v.p; + list->parent->args[0].v.p = sub->queue_next; + + ta_xset_parent(sub, NULL); + + if (sub->flags & MP_ASYNC_CMD) { + // We run it "detached" (fire & forget) + run_command(list->mpctx, sub, NULL, NULL); + } else { + // Run the next command once this one completes. + + list->completed_recursive = false; + list->current_valid = true; + list->current = pthread_self(); + + run_command(list->mpctx, sub, on_cmd_list_sub_completion, list); + + list->current_valid = false; + + // run_command() either recursively calls the completion function, + // or lets the command continue run in the background. If it was + // completed recursively, we can just continue our loop. Otherwise + // the completion handler will invoke this loop again elsewhere. + // We could unconditionally call continue_cmd_list() in the handler + // instead, but then stack depth would grow with list length. + if (!list->completed_recursive) + return; + } + } + + mp_cmd_ctx_complete(list->parent); + talloc_free(list); +} + static void cmd_list(void *p) { struct mp_cmd_ctx *cmd = p; - for (struct mp_cmd *sub = cmd->cmd->args[0].v.p; sub; sub = sub->queue_next) - run_command(cmd->mpctx, sub, NULL); + cmd->completed = false; + + struct cmd_list_ctx *list = talloc_zero(NULL, struct cmd_list_ctx); + list->mpctx = cmd->mpctx; + list->parent = p; + + continue_cmd_list(list); +} + +const struct mp_cmd_def mp_cmd_list = { "list", cmd_list, .exec_async = true }; + +// Signal that the command is complete now. This also deallocates cmd. +// You must call this function in a state where the core is locked for the +// current thread (e.g. from the main thread, or from within mp_dispatch_lock()). +// Completion means the command is finished, even if it errored or never ran. +// Keep in mind that calling this can execute further user command that can +// change arbitrary state (due to cmd_list). +void mp_cmd_ctx_complete(struct mp_cmd_ctx *cmd) +{ + cmd->completed = true; + if (!cmd->success) + mpv_free_node_contents(&cmd->result); + if (cmd->on_completion) + cmd->on_completion(cmd); + mpv_free_node_contents(&cmd->result); + talloc_free(cmd->cmd); + talloc_free(cmd); } -const struct mp_cmd_def mp_cmd_list = { "list", cmd_list }; +static void run_command_on_worker_thread(void *p) +{ + struct mp_cmd_ctx *ctx = p; + struct MPContext *mpctx = ctx->mpctx; + + mp_core_lock(mpctx); + + bool exec_async = ctx->cmd->def->exec_async; + ctx->cmd->def->handler(ctx); + if (!exec_async) + mp_cmd_ctx_complete(ctx); -int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res) + mpctx->outstanding_async -= 1; + if (!mpctx->outstanding_async && mp_is_shutting_down(mpctx)) + mp_wakeup_core(mpctx); + + mp_core_unlock(mpctx); +} + +// Run the given command. Upon command completion, on_completion is called. This +// can happen within the function, or for async commands, some time after the +// function returns (the caller is supposed to be able to handle both cases). In +// both cases, the callback will be called while the core is locked (i.e. you +// can access the core freely). +// on_completion_priv is copied to mp_cmd_ctx.on_completion_priv and can be +// accessed from the completion callback. +// The completion callback is invoked exactly once. If it's NULL, it's ignored. +// Ownership of cmd goes to the caller. +void run_command(struct MPContext *mpctx, struct mp_cmd *cmd, + void (*on_completion)(struct mp_cmd_ctx *cmd), + void *on_completion_priv) { - struct mpv_node dummy_node = {0}; - struct mp_cmd_ctx *ctx = &(struct mp_cmd_ctx){ + struct mp_cmd_ctx *ctx = talloc(NULL, struct mp_cmd_ctx); + *ctx = (struct mp_cmd_ctx){ .mpctx = mpctx, .cmd = cmd, .args = cmd->args, .num_args = cmd->nargs, .priv = cmd->def->priv, .success = true, - .result = res ? res : &dummy_node, + .completed = true, + .on_completion = on_completion, + .on_completion_priv = on_completion_priv, }; struct MPOpts *opts = mpctx->opts; @@ -4863,22 +4985,32 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re for (int n = 0; n < cmd->nargs; n++) { if (cmd->args[n].type->type == CONF_TYPE_STRING) { char *s = mp_property_expand_string(mpctx, cmd->args[n].v.s); - if (!s) - return -1; + if (!s) { + ctx->success = false; + mp_cmd_ctx_complete(ctx); + return; + } talloc_free(cmd->args[n].v.s); cmd->args[n].v.s = s; } } } - cmd->def->handler(ctx); - - if (!ctx->success) - mpv_free_node_contents(ctx->result); - - mpv_free_node_contents(&dummy_node); - - return ctx->success ? 0 : -1; + if (cmd->def->spawn_thread) { + mpctx->outstanding_async += 1; // prevent that core disappears + if (!mp_thread_pool_queue(mpctx->thread_pool, + run_command_on_worker_thread, ctx)) + { + mpctx->outstanding_async -= 1; + ctx->success = false; + mp_cmd_ctx_complete(ctx); + } + } else { + bool exec_async = cmd->def->exec_async; + cmd->def->handler(ctx); + if (!exec_async) + mp_cmd_ctx_complete(ctx); + } } static void cmd_seek(void *p) @@ -5199,7 +5331,7 @@ static void cmd_expand_text(void *p) struct mp_cmd_ctx *cmd = p; struct MPContext *mpctx = cmd->mpctx; - *cmd->result = (mpv_node){ + cmd->result = (mpv_node){ .format = MPV_FORMAT_STRING, .u.string = mp_property_expand_string(mpctx, cmd->args[0].v.s) }; @@ -5513,7 +5645,7 @@ static void cmd_screenshot_raw(void *p) { struct mp_cmd_ctx *cmd = p; struct MPContext *mpctx = cmd->mpctx; - struct mpv_node *res = cmd->result; + struct mpv_node *res = &cmd->result; struct mp_image *img = screenshot_get_rgb(mpctx, cmd->args[0].v.i); if (!img) { |