summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--options/m_config.c620
-rw-r--r--options/m_config.h24
2 files changed, 336 insertions, 308 deletions
diff --git a/options/m_config.c b/options/m_config.c
index aa0018314d..77c0fcdc06 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -55,19 +55,42 @@ static const union m_option_value default_value;
// For use with m_config_cache.
struct m_config_shadow {
- pthread_mutex_t lock;
struct m_config *root;
- char *data;
+ pthread_mutex_t lock;
+ // -- protected by lock
+ struct m_config_data *data; // protected shadow copy of the option data
struct m_config_cache **listeners;
int num_listeners;
};
// Represents a sub-struct (OPT_SUBSTRUCT()).
struct m_config_group {
- const struct m_sub_options *group; // or NULL for top-level options
- int parent_group; // index of parent group in m_config.groups
- void *opts; // pointer to group user option struct
- atomic_llong ts; // incremented on every write access
+ const struct m_sub_options *group;
+ int group_count; // 1 + number of all sub groups owned by this (so
+ // m_config.groups[idx..idx+group_count] is used by the
+ // entire tree of sub groups included by this group)
+ int parent_group; // index of parent group into m_config.groups[], or
+ // -1 for group 0
+ int parent_ptr; // ptr offset in the parent group's data, or -1 if
+ // none
+ int co_index; // index of the first group opt into m_config.opts[]
+ int co_end_index; // index of the last group opt + 1 (i.e. exclusive)
+};
+
+// A copy of option data. Used for the main option struct, the shadow data,
+// and copies for m_config_cache.
+struct m_config_data {
+ struct m_config *root; // root config (with up-to-date data)
+ int group_index; // start index into m_config.groups[]
+ struct m_group_data *gdata; // user struct allocation (our copy of data)
+ int num_gdata; // (group_index+num_gdata = end index)
+ atomic_llong ts; // last change timestamp we've seen
+};
+
+// Per m_config_data state for each m_config_group.
+struct m_group_data {
+ char *udata; // pointer to group user option struct
+ long long ts; // incremented on every write access
};
struct m_profile {
@@ -86,6 +109,20 @@ struct m_opt_backup {
void *backup;
};
+static void add_sub_group(struct m_config *config, const char *name_prefix,
+ int parent_group_index, int parent_ptr,
+ const struct m_sub_options *subopts);
+
+static struct m_group_data *m_config_gdata(struct m_config_data *data,
+ int group_index)
+{
+ if (group_index < data->group_index ||
+ group_index >= data->group_index + data->num_gdata)
+ return NULL;
+
+ return &data->gdata[group_index - data->group_index];
+}
+
static int show_profile(struct m_config *config, bstr param)
{
struct m_profile *p;
@@ -140,30 +177,125 @@ static void substruct_write_ptr(void *ptr, void *val)
memcpy(ptr, &src, sizeof(src));
}
-static void add_options(struct m_config *config,
- struct m_config_option *parent,
- void *optstruct,
- const void *optstruct_def,
- const struct m_option *defs);
+// Initialize a field with a given value. In case this is dynamic data, it has
+// to be allocated and copied. src can alias dst, also can be NULL.
+static void init_opt_inplace(const struct m_option *opt, void *dst,
+ const void *src)
+{
+ union m_option_value temp = {0};
+ if (src)
+ memcpy(&temp, src, opt->type->size);
+ memset(dst, 0, opt->type->size);
+ m_option_copy(opt, dst, &temp);
+}
-static void config_destroy(void *p)
+static void alloc_group(struct m_config_data *data, int group_index,
+ struct m_config_data *copy)
{
- struct m_config *config = p;
- m_config_restore_backups(config);
- for (int n = 0; n < config->num_opts; n++) {
- struct m_config_option *co = &config->opts[n];
+ assert(group_index == data->group_index + data->num_gdata);
+ assert(group_index < data->root->num_groups);
+ struct m_config_group *group = &data->root->groups[group_index];
+ const struct m_sub_options *opts = group->group;
+
+ MP_TARRAY_GROW(data, data->gdata, data->num_gdata);
+ struct m_group_data *gdata = &data->gdata[data->num_gdata++];
- m_option_free(co->opt, co->data);
+ struct m_group_data *copy_gdata =
+ copy ? m_config_gdata(copy, group_index) : NULL;
- if (config->shadow && co->shadow_offset >= 0)
- m_option_free(co->opt, config->shadow->data + co->shadow_offset);
+ *gdata = (struct m_group_data){
+ .udata = talloc_zero_size(data, opts->size),
+ .ts = copy_gdata ? copy_gdata->ts : 0,
+ };
+
+ if (opts->defaults)
+ memcpy(gdata->udata, opts->defaults, opts->size);
+
+ char *copy_src = copy_gdata ? copy_gdata->udata : NULL;
+
+ for (int n = group->co_index; n < group->co_end_index; n++) {
+ assert(n >= 0 && n < data->root->num_opts);
+ struct m_config_option *co = &data->root->opts[n];
+
+ if (co->opt->offset < 0 || co->opt->type->size == 0)
+ continue;
+
+ const void *defptr = co->default_data;
+ if (copy_src)
+ defptr = copy_src + co->opt->offset;
+
+ if (defptr)
+ init_opt_inplace(co->opt, gdata->udata + co->opt->offset, defptr);
+ }
+
+ // If there's a parent, update its pointer to the new struct.
+ if (group->parent_group >= data->group_index && group->parent_ptr >= 0) {
+ struct m_group_data *parent_gdata =
+ m_config_gdata(data, group->parent_group);
+ assert(parent_gdata);
+
+ substruct_write_ptr(parent_gdata->udata + group->parent_ptr, gdata->udata);
}
+}
+
+static void free_option_data(void *p)
+{
+ struct m_config_data *data = p;
+
+ for (int i = 0; i < data->num_gdata; i++) {
+ struct m_group_data *gdata = &data->gdata[i];
+ struct m_config_group *group = &data->root->groups[data->group_index + i];
+
+ for (int n = group->co_index; n < group->co_end_index; n++) {
+ struct m_config_option *co = &data->root->opts[n];
+
+ if (co->opt->offset >= 0 && co->opt->type->size > 0)
+ m_option_free(co->opt, gdata->udata + co->opt->offset);
+ }
+ }
+}
+
+// Allocate data using the option description in root, starting at group_index
+// (index into m_config.groups[]).
+// If copy is not NULL, copy all data from there (for groups which are in both
+// m_config_data instances), in all other cases init the data with the defaults.
+static struct m_config_data *allocate_option_data(void *ta_parent,
+ struct m_config *root,
+ int group_index,
+ struct m_config_data *copy)
+{
+ assert(group_index >= 0 && group_index < root->num_groups);
+ struct m_config_data *data = talloc_zero(ta_parent, struct m_config_data);
+ talloc_set_destructor(data, free_option_data);
+
+ data->root = root;
+ data->group_index = group_index;
+
+ struct m_config_group *root_group = &root->groups[group_index];
+ assert(root_group->group_count > 0);
+
+ for (int n = group_index; n < group_index + root_group->group_count; n++)
+ alloc_group(data, n, copy);
+
+ if (copy)
+ data->ts = copy->ts;
+
+ return data;
+}
+
+static void config_destroy(void *p)
+{
+ struct m_config *config = p;
+ m_config_restore_backups(config);
if (config->shadow) {
// must all have been unregistered
assert(config->shadow->num_listeners == 0);
pthread_mutex_destroy(&config->shadow->lock);
+ talloc_free(config->shadow);
}
+
+ talloc_free(config->data);
}
struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
@@ -175,22 +307,27 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
*config = (struct m_config)
{.log = log, .size = size, .defaults = defaults, .options = options};
- // size==0 means a dummy object is created
- if (size) {
- config->optstruct = talloc_zero_size(config, size);
- if (defaults)
- memcpy(config->optstruct, defaults, size);
- }
-
- config->num_groups = 1;
- MP_TARRAY_GROW(config, config->groups, 1);
- config->groups[0] = (struct m_config_group){
- .parent_group = -1,
- .opts = config->optstruct,
+ struct m_sub_options *subopts = talloc_ptrtype(config, subopts);
+ *subopts = (struct m_sub_options){
+ .opts = options,
+ .size = size,
+ .defaults = defaults,
};
+ add_sub_group(config, NULL, -1, -1, subopts);
+
+ if (!size)
+ return config;
+
+ config->data = allocate_option_data(config, config, 0, NULL);
+ config->optstruct = config->data->gdata[0].udata;
+
+ for (int n = 0; n < config->num_opts; n++) {
+ struct m_config_option *co = &config->opts[n];
+ struct m_group_data *gdata = m_config_gdata(config->data, co->group_index);
+ if (gdata && co->opt->offset >= 0)
+ co->data = gdata->udata + co->opt->offset;
+ }
- if (options)
- add_options(config, NULL, config->optstruct, defaults, options);
return config;
}
@@ -216,14 +353,14 @@ struct m_config *m_config_from_obj_desc_noalloc(void *talloc_ctx,
return m_config_new(talloc_ctx, log, 0, desc->priv_defaults, desc->options);
}
-static struct m_config_group *find_group(struct mpv_global *global,
- const struct m_option *cfg)
+static const struct m_config_group *find_group(struct mpv_global *global,
+ const struct m_option *cfg)
{
struct m_config_shadow *shadow = global->config;
struct m_config *root = shadow->root;
for (int n = 0; n < root->num_groups; n++) {
- if (cfg && root->groups[n].group && root->groups[n].group->opts == cfg)
+ if (root->groups[n].group->opts == cfg)
return &root->groups[n];
}
@@ -238,7 +375,7 @@ static struct m_config_group *find_group(struct mpv_global *global,
void *m_config_group_from_desc(void *ta_parent, struct mp_log *log,
struct mpv_global *global, struct m_obj_desc *desc, const char *name)
{
- struct m_config_group *group = find_group(global, desc->options);
+ const struct m_config_group *group = find_group(global, desc->options);
if (group) {
return mp_get_config_group(ta_parent, global, group->group);
} else {
@@ -335,211 +472,120 @@ void m_config_backup_all_opts(struct m_config *config)
ensure_backup(config, &config->opts[n]);
}
-static void m_config_add_option(struct m_config *config,
- struct m_config_option *parent,
- void *optstruct,
- const void *optstruct_def,
- const struct m_option *arg);
-
-static void add_options(struct m_config *config,
- struct m_config_option *parent,
- void *optstruct,
- const void *optstruct_def,
- const struct m_option *defs)
-{
- for (int i = 0; defs && defs[i].name; i++)
- m_config_add_option(config, parent, optstruct, optstruct_def, &defs[i]);
-}
-
-static void add_sub_options(struct m_config *config,
- struct m_config_option *parent,
- const struct m_sub_options *subopts)
-{
- // Can't be used multiple times.
- for (int n = 0; n < config->num_groups; n++)
- assert(config->groups[n].group != subopts);
-
- // You can only use UPDATE_ flags here.
- assert(!(subopts->change_flags & ~(unsigned)UPDATE_OPTS_MASK));
-
- void *new_optstruct = NULL;
- if (config->optstruct) { // only if not noalloc
- new_optstruct = talloc_zero_size(config, subopts->size);
- if (subopts->defaults)
- memcpy(new_optstruct, subopts->defaults, subopts->size);
- }
- if (parent && parent->data)
- substruct_write_ptr(parent->data, new_optstruct);
-
- const void *new_optstruct_def = NULL;
- if (parent && parent->default_data)
- new_optstruct_def = substruct_read_ptr(parent->default_data);
- if (!new_optstruct_def)
- new_optstruct_def = subopts->defaults;
-
- int group = config->num_groups++;
- MP_TARRAY_GROW(config, config->groups, group);
- config->groups[group] = (struct m_config_group){
- .group = subopts,
- .parent_group = parent ? parent->group : 0,
- .opts = new_optstruct,
- };
-
- struct m_config_option next = {
- .name = "",
- .group = group,
- };
- if (parent && parent->name && parent->name[0])
- next.name = parent->name;
- if (subopts->prefix && subopts->prefix[0]) {
- assert(next.name);
- next.name = subopts->prefix;
- }
- add_options(config, &next, new_optstruct, new_optstruct_def, subopts->opts);
-}
-
-#define MAX_VO_AO 16
-
-struct group_entry {
- const struct m_obj_list *entry;
- struct m_sub_options subs[MAX_VO_AO];
- bool initialized;
-};
-
-static struct group_entry g_groups[2]; // limited by max. m_obj_list overall
-static int g_num_groups = 0;
-static pthread_mutex_t g_group_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static const struct m_sub_options *get_cached_group(const struct m_obj_list *list,
- int n, struct m_sub_options *v)
-{
- pthread_mutex_lock(&g_group_mutex);
-
- struct group_entry *group = NULL;
- for (int i = 0; i < g_num_groups; i++) {
- if (g_groups[i].entry == list) {
- group = &g_groups[i];
- break;
- }
- }
- if (!group) {
- assert(g_num_groups < MP_ARRAY_SIZE(g_groups));
- group = &g_groups[g_num_groups++];
- group->entry = list;
- }
-
- if (!group->initialized) {
- if (!v) {
- n = -1;
- group->initialized = true;
- } else {
- assert(n < MAX_VO_AO); // simply increase this if it fails
- group->subs[n] = *v;
- }
- }
-
- pthread_mutex_unlock(&g_group_mutex);
-
- return n >= 0 ? &group->subs[n] : NULL;
-}
-
static void init_obj_settings_list(struct m_config *config,
+ int parent_group_index,
const struct m_obj_list *list)
{
struct m_obj_desc desc;
for (int n = 0; ; n++) {
- if (!list->get_desc(&desc, n)) {
- if (list->use_global_options)
- get_cached_group(list, n, NULL);
+ if (!list->get_desc(&desc, n))
break;
+ if (desc.global_opts) {
+ add_sub_group(config, NULL, parent_group_index, -1,
+ desc.global_opts);
}
- if (desc.global_opts)
- add_sub_options(config, NULL, desc.global_opts);
if (list->use_global_options && desc.options) {
- struct m_sub_options conf = {
+ struct m_sub_options *conf = talloc_ptrtype(config, conf);
+ *conf = (struct m_sub_options){
.prefix = desc.options_prefix,
.opts = desc.options,
.defaults = desc.priv_defaults,
.size = desc.priv_size,
};
- add_sub_options(config, NULL, get_cached_group(list, n, &conf));
+ add_sub_group(config, NULL, parent_group_index, -1, conf);
}
}
}
-// Initialize a field with a given value. In case this is dynamic data, it has
-// to be allocated and copied. src can alias dst, also can be NULL.
-static void init_opt_inplace(const struct m_option *opt, void *dst,
- const void *src)
+static const char *concat_name(void *ta_parent, const char *a, const char *b)
{
- union m_option_value temp = {0};
- if (src)
- memcpy(&temp, src, opt->type->size);
- memset(dst, 0, opt->type->size);
- m_option_copy(opt, dst, &temp);
+ assert(a);
+ assert(b);
+ if (!a[0])
+ return b;
+ if (!b[0])
+ return a;
+ return talloc_asprintf(ta_parent, "%s-%s", a, b);
}
-static void m_config_add_option(struct m_config *config,
- struct m_config_option *parent,
- void *optstruct,
- const void *optstruct_def,
- const struct m_option *arg)
+static void add_sub_group(struct m_config *config, const char *name_prefix,
+ int parent_group_index, int parent_ptr,
+ const struct m_sub_options *subopts)
{
- assert(config != NULL);
- assert(arg != NULL);
+ // Can't be used multiple times.
+ for (int n = 0; n < config->num_groups; n++)
+ assert(config->groups[n].group != subopts);
+
+ // You can only use UPDATE_ flags here.
+ assert(!(subopts->change_flags & ~(unsigned)UPDATE_OPTS_MASK));
- const char *parent_name = parent ? parent->name : "";
+ assert(parent_group_index >= -1 && parent_group_index < config->num_groups);
- struct m_config_option co = {
- .opt = arg,
- .name = arg->name,
- .shadow_offset = -1,
- .group = parent ? parent->group : 0,
- .default_data = &default_value,
- .is_hidden = !!arg->deprecation_message,
+ int group_index = config->num_groups++;
+ MP_TARRAY_GROW(config, config->groups, group_index);
+ config->groups[group_index] = (struct m_config_group){
+ .group = subopts,
+ .parent_group = parent_group_index,
+ .parent_ptr = parent_ptr,
+ .co_index = config->num_opts,
};
- if (arg->offset >= 0) {
- if (optstruct)
- co.data = (char *)optstruct + arg->offset;
- if (optstruct_def)
- co.default_data = (char *)optstruct_def + arg->offset;
- }
+ const void *optstruct_def = subopts->defaults;
+
+ if (subopts->prefix && subopts->prefix[0])
+ name_prefix = subopts->prefix;
+ if (!name_prefix)
+ name_prefix = "";
- if (arg->defval)
- co.default_data = arg->defval;
+ for (int i = 0; subopts->opts && subopts->opts[i].name; i++) {
+ const struct m_option *opt = &subopts->opts[i];
- // Fill in the full name
- if (!co.name[0]) {
- co.name = parent_name;
- } else if (parent_name[0]) {
- co.name = talloc_asprintf(config, "%s-%s", parent_name, co.name);
+ if (opt->type == &m_option_type_subconfig)
+ continue;
+
+ struct m_config_option co = {
+ .name = concat_name(config, name_prefix, opt->name),
+ .opt = opt,
+ .group_index = group_index,
+ .default_data = &default_value,
+ .is_hidden = !!opt->deprecation_message,
+ };
+
+ if (opt->offset >= 0 && optstruct_def)
+ co.default_data = (char *)optstruct_def + opt->offset;
+
+ if (opt->defval)
+ co.default_data = opt->defval;
+
+ if (opt->type != &m_option_type_subconfig)
+ MP_TARRAY_APPEND(config, config->opts, config->num_opts, co);
}
- if (arg->type == &m_option_type_subconfig) {
- const struct m_sub_options *subopts = arg->priv;
- add_sub_options(config, &co, subopts);
- } else {
- int size = arg->type->size;
- if (optstruct && size) {
- // The required alignment is unknown, so go with the maximum C
- // could require. Slightly wasteful, but not that much.
- int align = (size - config->shadow_size % size) % size;
- int offset = config->shadow_size + align;
- assert(offset <= INT16_MAX);
- co.shadow_offset = offset;
- config->shadow_size = co.shadow_offset + size;
- }
+ config->groups[group_index].co_end_index = config->num_opts;
- // Initialize options
- if (co.data && co.default_data)
- init_opt_inplace(arg, co.data, co.default_data);
+ // Initialize sub-structs. These have to come after, because co_index and
+ // co_end_index must strictly be for a single struct only.
+ for (int i = 0; subopts->opts && subopts->opts[i].name; i++) {
+ const struct m_option *opt = &subopts->opts[i];
- MP_TARRAY_APPEND(config, config->opts, config->num_opts, co);
+ if (opt->type == &m_option_type_subconfig) {
+ const struct m_sub_options *new_subopts = opt->priv;
- if (arg->type == &m_option_type_obj_settings_list)
- init_obj_settings_list(config, (const struct m_obj_list *)arg->priv);
+ // Providing default structs in-place is not allowed.
+ if (opt->offset >= 0 && optstruct_def) {
+ void *ptr = (char *)optstruct_def + opt->offset;
+ assert(!substruct_read_ptr(ptr));
+ }
+
+ const char *prefix = concat_name(config, name_prefix, opt->name);
+ add_sub_group(config, prefix, group_index, opt->offset, new_subopts);
+ } else if (opt->type == &m_option_type_obj_settings_list) {
+ const struct m_obj_list *objlist = opt->priv;
+ init_obj_settings_list(config, group_index, objlist);
+ }
}
+
+ config->groups[group_index].group_count = config->num_groups - group_index;
}
struct m_config_option *m_config_get_co_raw(const struct m_config *config,
@@ -753,7 +799,6 @@ static int m_config_handle_special_options(struct m_config *config,
return M_OPT_UNKNOWN;
}
-
// Unlike m_config_set_option_raw() this does not go through the property layer
// via config.option_set_callback.
int m_config_set_option_raw_direct(struct m_config *config,
@@ -1191,33 +1236,13 @@ void m_config_create_shadow(struct m_config *config)
assert(config->global && config->options && config->size);
assert(!config->shadow && !config->global->config);
- config->shadow = talloc_zero(config, struct m_config_shadow);
- config->shadow->data = talloc_zero_size(config->shadow, config->shadow_size);
-
+ config->shadow = talloc_zero(NULL, struct m_config_shadow);
+ config->shadow->data =
+ allocate_option_data(config->shadow, config, 0, config->data);
config->shadow->root = config;
pthread_mutex_init(&config->shadow->lock, NULL);
config->global->config = config->shadow;
-
- for (int n = 0; n < config->num_opts; n++) {
- struct m_config_option *co = &config->opts[n];
- if (co->shadow_offset < 0)
- continue;
- m_option_copy(co->opt, config->shadow->data + co->shadow_offset, co->data);
- }
-}
-
-// Return whether parent is a parent of group. Also returns true if they're equal.
-static bool is_group_included(struct m_config *config, int group, int parent)
-{
- for (;;) {
- if (group == parent)
- return true;
- if (group < 0)
- break;
- group = config->groups[group].parent_group;
- }
- return false;
}
static void cache_destroy(void *p)
@@ -1236,58 +1261,60 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
{
struct m_config_shadow *shadow = global->config;
struct m_config *root = shadow->root;
+ int group_index = -1;
+
+ for (int n = 0; n < root->num_groups; n++) {
+ // group==NULL is special cased to root group.
+ if (root->groups[n].group == group || (!group && !n)) {
+ group_index = n;
+ break;
+ }
+ }
+
+ assert(group_index >= 0); // invalid group (or not in option tree)
struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache);
talloc_set_destructor(cache, cache_destroy);
cache->shadow = shadow;
- cache->shadow_config = m_config_new(cache, mp_null_log, root->size,
- root->defaults, root->options);
- struct m_config *config = cache->shadow_config;
+ pthread_mutex_lock(&shadow->lock);
+ cache->data = allocate_option_data(cache, root, group_index, shadow->data);
+ pthread_mutex_unlock(&shadow->lock);
- assert(config->num_opts == root->num_opts);
- for (int n = 0; n < root->num_opts; n++) {
- assert(config->opts[n].opt->type == root->opts[n].opt->type);
- assert(config->opts[n].shadow_offset == root->opts[n].shadow_offset);
- }
+ cache->opts = cache->data->gdata[0].udata;
- cache->ts = -1;
- cache->group = -1;
+ return cache;
+}
- for (int n = 0; n < config->num_groups; n++) {
- if (config->groups[n].group == group) {
- cache->opts = config->groups[n].opts;
- cache->group = n;
- break;
- }
- }
+static void update_options(struct m_config_data *dst, struct m_config_data *src)
+{
+ assert(dst->root == src->root);
+
+ dst->ts = src->ts;
+
+ // 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->root->num_groups);
+ for (int n = group_s; n < group_e; n++) {
+ struct m_config_group *g = &dst->root->groups[n];
+ struct m_group_data *gsrc = m_config_gdata(src, n);
+ struct m_group_data *gdst = m_config_gdata(dst, n);
+ assert(gsrc && gdst);
+
+ if (gsrc->ts <= gdst->ts)
+ continue;
+ gdst->ts = gsrc->ts;
- assert(cache->group >= 0);
- assert(cache->opts);
-
- // If we're not on the top-level, restrict set of options to the sub-group
- // to reduce update costs. (It would be better not to add them in the first
- // place.)
- if (cache->group > 0) {
- int num_opts = config->num_opts;
- config->num_opts = 0;
- for (int n = 0; n < num_opts; n++) {
- struct m_config_option *co = &config->opts[n];
- if (is_group_included(config, co->group, cache->group)) {
- config->opts[config->num_opts++] = *co;
- } else {
- m_option_free(co->opt, co->data);
+ for (int i = g->co_index; i < g->co_end_index; i++) {
+ struct m_config_option *co = &dst->root->opts[i];
+ if (co->opt->offset >= 0 && co->opt->type->size) {
+ m_option_copy(co->opt, gdst->udata + co->opt->offset,
+ gsrc->udata + co->opt->offset);
}
}
- for (int n = 0; n < config->num_groups; n++) {
- if (!is_group_included(config, n, cache->group))
- TA_FREEP(&config->groups[n].opts);
- }
}
-
- m_config_cache_update(cache);
-
- return cache;
}
bool m_config_cache_update(struct m_config_cache *cache)
@@ -1296,16 +1323,11 @@ bool m_config_cache_update(struct m_config_cache *cache)
// Using atomics and checking outside of the lock - it's unknown whether
// this makes it faster or slower. Just cargo culting it.
- if (atomic_load(&shadow->root->groups[cache->group].ts) <= cache->ts)
+ if (atomic_load(&shadow->data->ts) <= cache->data->ts)
return false;
pthread_mutex_lock(&shadow->lock);
- cache->ts = atomic_load(&shadow->root->groups[cache->group].ts);
- for (int n = 0; n < cache->shadow_config->num_opts; n++) {
- struct m_config_option *co = &cache->shadow_config->opts[n];
- if (co->shadow_offset >= 0)
- m_option_copy(co->opt, co->data, shadow->data + co->shadow_offset);
- }
+ update_options(cache->data, shadow->data);
pthread_mutex_unlock(&shadow->lock);
return true;
}
@@ -1314,35 +1336,36 @@ void m_config_notify_change_co(struct m_config *config,
struct m_config_option *co)
{
struct m_config_shadow *shadow = config->shadow;
+ assert(co->data);
if (shadow) {
pthread_mutex_lock(&shadow->lock);
- if (co->shadow_offset >= 0)
- m_option_copy(co->opt, shadow->data + co->shadow_offset, co->data);
- pthread_mutex_unlock(&shadow->lock);
- }
- int changed = co->opt->flags & UPDATE_OPTS_MASK;
+ struct m_config_data *data = shadow->data;
+ struct m_group_data *gdata = m_config_gdata(data, co->group_index);
+ assert(gdata);
- int group = co->group;
- while (group >= 0) {
- struct m_config_group *g = &config->groups[group];
- atomic_fetch_add(&g->ts, 1);
- if (g->group)
- changed |= g->group->change_flags;
- group = g->parent_group;
- }
+ gdata->ts = atomic_fetch_add(&data->ts, 1) + 1;
+
+ m_option_copy(co->opt, gdata->udata + co->opt->offset, co->data);
- if (shadow) {
- pthread_mutex_lock(&shadow->lock);
for (int n = 0; n < shadow->num_listeners; n++) {
struct m_config_cache *cache = shadow->listeners[n];
if (cache->wakeup_cb)
cache->wakeup_cb(cache->wakeup_cb_ctx);
}
+
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 = &config->groups[group_index];
+ changed |= g->group->change_flags;
+ group_index = g->parent_group;
+ }
+
if (config->option_change_callback) {
config->option_change_callback(config->option_change_callback_ctx, co,
changed);
@@ -1441,11 +1464,14 @@ void mp_read_option_raw(struct mpv_global *global, const char *name,
struct m_config_shadow *shadow = global->config;
struct m_config_option *co = m_config_get_co_raw(shadow->root, bstr0(name));
assert(co);
- assert(co->shadow_offset >= 0);
+ assert(co->opt->offset >= 0);
assert(co->opt->type == type);
+ struct m_group_data *gdata = m_config_gdata(shadow->data, co->group_index);
+ assert(gdata);
+
memset(dst, 0, co->opt->type->size);
- m_option_copy(co->opt, dst, shadow->data + co->shadow_offset);
+ m_option_copy(co->opt, dst, gdata->udata + co->opt->offset);
}
struct m_config *mp_get_root_config(struct mpv_global *global)
diff --git a/options/m_config.h b/options/m_config.h
index 1f6f5157a3..2c4531ff86 100644
--- a/options/m_config.h
+++ b/options/m_config.h
@@ -43,8 +43,7 @@ struct m_config_option {
bool is_set_from_config : 1; // Set by a config file
bool is_set_locally : 1; // Has a backup entry
bool warning_was_printed : 1;
- int16_t shadow_offset; // Offset into m_config_shadow.data
- int16_t group; // Index into m_config.groups
+ int16_t group_index; // Index into m_config.groups
const char *name; // Full name (ie option-subopt)
const struct m_option *opt; // Option description
void *data; // Raw value of the option
@@ -94,14 +93,17 @@ typedef struct m_config {
void *optstruct; // struct mpopts or other
- int shadow_size;
-
- // List of m_sub_options instances.
+ // Private. List of m_sub_options instances.
// Index 0 is the top-level and is always present.
+ // Immutable after init.
+ // Invariant: a parent is always at a lower index than any of its children.
struct m_config_group *groups;
int num_groups;
- // Thread-safe shadow memory; only set for the main m_config.
+ // Private. Non-NULL if data was allocated. m_config_option.data uses it.
+ struct m_config_data *data;
+
+ // Private. Thread-safe shadow memory; only set for the main m_config.
struct m_config_shadow *shadow;
} m_config_t;
@@ -264,14 +266,13 @@ struct mpv_node m_config_get_profiles(struct m_config *config);
// the cache itself is allowed.
struct m_config_cache {
// The struct as indicated by m_config_cache_alloc's group parameter.
+ // (Internally the same as data->gdata[0]->udata.)
void *opts;
// Internal.
- struct m_config_shadow *shadow;
- struct m_config *shadow_config;
- long long ts;
- int group;
- bool in_list;
+ struct m_config_shadow *shadow; // real data
+ struct m_config_data *data; // copy for the cache user
+ bool in_list; // registered as listener with root config
// --- Implicitly synchronized by setting/unsetting wakeup_cb.
struct mp_dispatch_queue *wakeup_dispatch_queue;
void (*wakeup_dispatch_cb)(void *ctx);
@@ -320,6 +321,7 @@ bool m_config_cache_update(struct m_config_cache *cache);
// 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.
+// group==NULL is a special case, and always returns the root group.
void *mp_get_config_group(void *ta_parent, struct mpv_global *global,
const struct m_sub_options *group);