summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--options/m_config.c140
-rw-r--r--options/m_config.h29
2 files changed, 135 insertions, 34 deletions
diff --git a/options/m_config.c b/options/m_config.c
index e3f6a8e7b7..c6e0d02fc8 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -102,6 +102,9 @@ struct config_cache {
struct m_config_shadow *shadow; // global metadata
uint64_t ts; // timestamp of this data copy
bool in_list; // part of m_config_shadow->listeners[]
+ int upd_group; // for "incremental" change notification
+ int upd_opt;
+
// --- Implicitly synchronized by setting/unsetting wakeup_cb.
struct mp_dispatch_queue *wakeup_dispatch_queue;
@@ -611,6 +614,19 @@ static void add_sub_group(struct m_config *config, const char *name_prefix,
shadow->groups[group_index].group_count = shadow->num_groups - group_index;
}
+static uint64_t get_option_change_mask(struct m_config_shadow *shadow,
+ int group_index, int group_root,
+ const struct m_option *opt)
+{
+ uint64_t changed = opt->flags & UPDATE_OPTS_MASK;
+ while (group_index != group_root) {
+ struct m_config_group *g = &shadow->groups[group_index];
+ changed |= g->group->change_flags;
+ group_index = g->parent_group;
+ }
+ return changed;
+}
+
struct m_config_option *m_config_get_co_raw(const struct m_config *config,
struct bstr name)
{
@@ -1285,46 +1301,74 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
cache->opts = in->data->gdata[0].udata;
+ in->upd_group = -1;
+
return cache;
}
-static bool update_options(struct m_config_data *dst, struct m_config_data *src)
+static void update_next_option(struct m_config_cache *cache, void **p_opt)
{
- assert(dst->shadow == src->shadow);
+ struct config_cache *in = cache->internal;
+ struct m_config_data *dst = in->data;
+ struct m_config_data *src = in->src;
- bool res = false;
+ assert(src->group_index == 0); // must be the option root currently
- // Must be from same root, but they can have arbitrary overlap.
- int group_s = MPMAX(dst->group_index, src->group_index);
- int group_e = MPMIN(dst->group_index + dst->num_gdata,
- src->group_index + src->num_gdata);
- assert(group_s >= 0 && group_e <= dst->shadow->num_groups);
- for (int n = group_s; n < group_e; n++) {
- struct m_config_group *g = &dst->shadow->groups[n];
- const struct m_option *opts = g->group->opts;
- struct m_group_data *gsrc = m_config_gdata(src, n);
- struct m_group_data *gdst = m_config_gdata(dst, n);
- assert(gsrc && gdst);
+ *p_opt = NULL;
- if (gdst->ts >= gsrc->ts)
- continue;
- gdst->ts = gsrc->ts;
- res = true;
-
- for (int i = 0; opts && opts[i].name; i++) {
- const struct m_option *opt = &opts[i];
+ while (in->upd_group < dst->group_index + dst->num_gdata) {
+ struct m_group_data *gsrc = m_config_gdata(src, in->upd_group);
+ struct m_group_data *gdst = m_config_gdata(dst, in->upd_group);
+ assert(gsrc && gdst);
- if (opt->offset >= 0 && opt->type->size) {
- m_option_copy(opt, gdst->udata + opt->offset,
- gsrc->udata + opt->offset);
+ if (gdst->ts < gsrc->ts) {
+ struct m_config_group *g = &dst->shadow->groups[in->upd_group];
+ const struct m_option *opts = g->group->opts;
+
+ while (opts && opts[in->upd_opt].name) {
+ const struct m_option *opt = &opts[in->upd_opt];
+
+ if (opt->offset >= 0 && opt->type->size) {
+ void *dsrc = gsrc->udata + opt->offset;
+ void *ddst = gdst->udata + opt->offset;
+
+ if (!m_option_equal(opt, ddst, dsrc)) {
+ uint64_t ch = get_option_change_mask(dst->shadow,
+ in->upd_group, dst->group_index, opt);
+
+ if (cache->debug) {
+ char *vdst = m_option_print(opt, ddst);
+ char *vsrc = m_option_print(opt, dsrc);
+ mp_warn(cache->debug, "Option '%s' changed from "
+ "'%s' to' %s' (flags = 0x%"PRIx64")\n",
+ opt->name, vdst, vsrc, ch);
+ talloc_free(vdst);
+ talloc_free(vsrc);
+ }
+
+ m_option_copy(opt, ddst, dsrc);
+ cache->change_flags |= ch;
+
+ in->upd_opt++; // skip this next time
+ *p_opt = ddst;
+ return;
+ }
+ }
+
+ in->upd_opt++;
}
+
+ gdst->ts = gsrc->ts;
}
+
+ in->upd_group++;
+ in->upd_opt = 0;
}
- return res;
+ in->upd_group = -1;
}
-bool m_config_cache_update(struct m_config_cache *cache)
+static bool cache_check_update(struct m_config_cache *cache)
{
struct config_cache *in = cache->internal;
struct m_config_shadow *shadow = in->shadow;
@@ -1336,13 +1380,47 @@ bool m_config_cache_update(struct m_config_cache *cache)
return false;
in->ts = new_ts;
+ in->upd_group = in->data->group_index;
+ in->upd_opt = 0;
+ return true;
+}
+
+bool m_config_cache_update(struct m_config_cache *cache)
+{
+ struct config_cache *in = cache->internal;
+ struct m_config_shadow *shadow = in->shadow;
+
+ if (!cache_check_update(cache))
+ return false;
pthread_mutex_lock(&shadow->lock);
- bool res = update_options(in->data, in->src);
+ bool res = false;
+ while (1) {
+ void *p;
+ update_next_option(cache, &p);
+ if (!p)
+ break;
+ res = true;
+ }
pthread_mutex_unlock(&shadow->lock);
return res;
}
+bool m_config_cache_get_next_changed(struct m_config_cache *cache, void **opt)
+{
+ struct config_cache *in = cache->internal;
+ struct m_config_shadow *shadow = in->shadow;
+
+ *opt = NULL;
+ if (!cache_check_update(cache) && in->upd_group < 0)
+ return false;
+
+ pthread_mutex_lock(&shadow->lock);
+ update_next_option(cache, opt);
+ pthread_mutex_unlock(&shadow->lock);
+ return !!*opt;
+}
+
void m_config_notify_change_co(struct m_config *config,
struct m_config_option *co)
{
@@ -1369,13 +1447,7 @@ void m_config_notify_change_co(struct m_config *config,
pthread_mutex_unlock(&shadow->lock);
}
- int changed = co->opt->flags & UPDATE_OPTS_MASK;
- int group_index = co->group_index;
- while (group_index >= 0) {
- struct m_config_group *g = &shadow->groups[group_index];
- changed |= g->group->change_flags;
- group_index = g->parent_group;
- }
+ int changed = get_option_change_mask(shadow, co->group_index, 0, co->opt);
if (config->option_change_callback) {
config->option_change_callback(config->option_change_callback_ctx, co,
diff --git a/options/m_config.h b/options/m_config.h
index 42734e8000..e2dcc0e51c 100644
--- a/options/m_config.h
+++ b/options/m_config.h
@@ -266,6 +266,14 @@ struct m_config_cache {
// The struct as indicated by m_config_cache_alloc's group parameter.
// (Internally the same as internal->gdata[0]->udata.)
void *opts;
+ // Accumulated change flags. The user can set this to 0 to unset all flags.
+ // They are set when calling any of the update functions. A flag is only set
+ // once the new value is visible in ->opts.
+ uint64_t change_flags;
+
+ // Set to non-NULL for logging all option changes as they are retrieved
+ // with one of the update functions (like m_config_cache_update()).
+ struct mp_log *debug;
// Do not access.
struct config_cache *internal;
@@ -277,6 +285,9 @@ struct m_config_cache {
// Keep in mind that a m_config_cache object is not thread-safe; it merely
// provides thread-safe access to the global options. All API functions for
// the same m_config_cache object must synchronized, unless otherwise noted.
+// This does not create an initial change event (m_config_cache_update() will
+// return false), but note that a change might be asynchronously signaled at any
+// time.
// ta_parent: parent for the returned allocation
// global: option data source
// group: the option group to return. This can be GLOBAL_CONFIG for the global
@@ -307,8 +318,26 @@ void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache,
// some options have changed).
// Keep in mind that while the cache->opts pointer does not change, the option
// data itself will (e.g. string options might be reallocated).
+// New change flags are or-ed into cache->change_flags with this call (if you
+// use them, you should probably do cache->change_flags=0 before this call).
bool m_config_cache_update(struct m_config_cache *cache);
+// Check for changes and return fine grained change information.
+// Warning: this conflicts with m_config_cache_update(). If you call
+// m_config_cache_update(), all options will be marked as "not changed",
+// and this function will return false. Also, calling this function and
+// then m_config_cache_update() is not supported, and may skip updating
+// some fields.
+// This returns true as long as there is a changed option, and false if all
+// changed options have been returned.
+// If multiple options have changed, the new option value is visible only once
+// this function has returned the change for it.
+// out_ptr: pointer to a void*, which is set to the cache->opts field associated
+// with the changed option if the function returns true; set to NULL
+// if no option changed.
+// 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);
+
// 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.