From f73881fa10e3ba1e43d3a8b984379533c0c40284 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 28 Nov 2019 21:59:39 +0100 Subject: [PATCH] m_config: allow writing options through m_config_cache This will allow any other threads to write to the global option data in a safe way. The typical example for this is the fullscreen option, which needs to be written by VO (or even some other thing running completely separate from the main thread). We have a complicated and annoying contraption which gets the value updated on the main thread, and this function will help get rid of it. As of this commit, this doesn't really work yet, because he main thread uses its own weird copy of the option data. --- options/m_config.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ options/m_config.h | 14 +++++++++++ 2 files changed, 74 insertions(+) diff --git a/options/m_config.c b/options/m_config.c index c6e0d02fc8..001c238464 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -1421,6 +1421,66 @@ bool m_config_cache_get_next_changed(struct m_config_cache *cache, void **opt) return !!*opt; } +static void find_opt(struct m_config_shadow *shadow, struct m_config_data *data, + void *ptr, int *group_idx, int *opt_idx) +{ + *group_idx = -1; + *opt_idx = -1; + + for (int n = data->group_index; n < data->group_index + data->num_gdata; n++) + { + struct m_group_data *gd = m_config_gdata(data, n); + struct m_config_group *g = &shadow->groups[n]; + const struct m_option *opts = g->group->opts; + + for (int i = 0; opts && opts[i].name; i++) { + const struct m_option *opt = &opts[i]; + + if (opt->offset >= 0 && opt->type->size && + ptr == gd->udata + opt->offset) + { + *group_idx = n; + *opt_idx = i; + return; + } + } + } +} + +void m_config_cache_write_opt(struct m_config_cache *cache, void *ptr) +{ + struct config_cache *in = cache->internal; + struct m_config_shadow *shadow = in->shadow; + + int group_idx = -1; + int opt_idx = -1; + find_opt(shadow, in->data, ptr, &group_idx, &opt_idx); + + // ptr was not in cache->opts, or no option declaration matching it. + assert(group_idx >= 0); + + struct m_config_group *g = &shadow->groups[group_idx]; + const struct m_option *opt = &g->group->opts[opt_idx]; + + pthread_mutex_lock(&shadow->lock); + + struct m_group_data *gdst = m_config_gdata(in->data, group_idx); + struct m_group_data *gsrc = m_config_gdata(in->src, group_idx); + assert(gdst && gsrc); + + m_option_copy(opt, gsrc->udata + opt->offset, ptr); + + gsrc->ts = atomic_fetch_add(&shadow->ts, 1) + 1; + + for (int n = 0; n < shadow->num_listeners; n++) { + struct config_cache *listener = shadow->listeners[n]; + if (listener->wakeup_cb && m_config_gdata(listener->data, group_idx)) + listener->wakeup_cb(listener->wakeup_cb_ctx); + } + + pthread_mutex_unlock(&shadow->lock); +} + void m_config_notify_change_co(struct m_config *config, struct m_config_option *co) { diff --git a/options/m_config.h b/options/m_config.h index e2dcc0e51c..0f05e117a3 100644 --- a/options/m_config.h +++ b/options/m_config.h @@ -338,6 +338,20 @@ bool m_config_cache_update(struct m_config_cache *cache); // returns: *out_ptr!=NULL (true if there was a changed option) bool m_config_cache_get_next_changed(struct m_config_cache *cache, void **out_ptr); +// Copy the option field pointed to by ptr to the global option storage. This +// is sort of similar to m_config_set_option_raw(), except doesn't require +// access to the main thread. (And you can't pass any flags.) +// You write the new value to the option struct, and then call this function +// with the pointer to it. You will not get a change notification for it (though +// you might still get a redundant wakeup callback). +// Changing the option struct and not calling this function before any update +// function (like m_config_cache_update()) will leave the value inconsistent, +// and will possibly (but not necessarily) overwrite it with the next update +// call. +// ptr: points to any field in cache->opts that is managed by an option. If +// this is not the case, the function crashes for your own good. +void m_config_cache_write_opt(struct m_config_cache *cache, void *ptr); + // Like m_config_cache_alloc(), but return the struct (m_config_cache->opts) // directly, with no way to update the config. Basically this returns a copy // with a snapshot of the current option values.