summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-08-02 01:39:28 +0200
committerwm4 <wm4@nowhere>2014-08-02 01:53:22 +0200
commit733bdebcb94e485317e210cfd79ec058b097e12d (patch)
treebec9b4181e8e9ffdf7d74cabdd12dfad7295f93e
parent82a223e4e0bf45827c20592aaa7d5690593802ff (diff)
downloadmpv-733bdebcb94e485317e210cfd79ec058b097e12d.tar.bz2
mpv-733bdebcb94e485317e210cfd79ec058b097e12d.tar.xz
client API: minor optimizations for property notification
Internally, there are two mechanisms which can trigger property notification as used with "observed" properties in the client API. The first mechanism associates events with a group of properties that are potentially changed by a certain event. mp_event_property_change[] declares these associations, and maps each event to a set of strings. When an event happens, the set of strings is matched against the list of observed properties of each client. Make this more efficient by comparing bitsets of events instead. This way, only a bit-wise "and" is needed for each observed property. Even better, we can completely skip clients which have no observed properties that match. The second mechanism just updates individual properties explicitly by name. Optimize this by using the property index instead. It would be nice if we could reuse the first mechanism for the second one, but there are too many properties to fit into a 64 bit mask. (Though the limit on 64 events might get us into trouble later...)
-rw-r--r--player/client.c76
-rw-r--r--player/client.h2
-rw-r--r--player/command.c47
-rw-r--r--player/command.h4
4 files changed, 91 insertions, 38 deletions
diff --git a/player/client.c b/player/client.c
index 49cb866edb..bccf2d9605 100644
--- a/player/client.c
+++ b/player/client.c
@@ -63,6 +63,8 @@ struct mp_client_api {
struct observe_property {
char *name;
+ int id; // ==mp_get_property_id(name)
+ uint64_t event_mask; // ==mp_get_property_event_mask(name)
int64_t reply_id;
mpv_format format;
bool changed; // property change should be signaled to user
@@ -111,13 +113,15 @@ struct mpv_handle {
struct observe_property **properties;
int num_properties;
- int lowest_changed;
+ int lowest_changed; // attempt at making change processing incremental
int properties_updating;
+ uint64_t property_event_masks; // or-ed together event masks of all properties
struct mp_log_buffer *messages;
};
static bool gen_property_change_event(struct mpv_handle *ctx);
+static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask);
void mp_clients_init(struct MPContext *mpctx)
{
@@ -188,13 +192,15 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name
.cur_event = talloc_zero(client, struct mpv_event),
.events = talloc_array(client, mpv_event, num_events),
.max_events = num_events,
- .event_mask = ((uint64_t)-1) & ~(1ULL << MPV_EVENT_TICK),
+ .event_mask = (1ULL << INTERNAL_EVENT_BASE) - 1, // exclude internal events
.wakeup_pipe = {-1, -1},
};
pthread_mutex_init(&client->lock, NULL);
pthread_mutex_init(&client->wakeup_lock, NULL);
pthread_cond_init(&client->wakeup, NULL);
+ mpv_request_event(client, MPV_EVENT_TICK, 0);
+
MP_TARRAY_APPEND(clients, clients->clients, clients->num_clients, client);
pthread_mutex_unlock(&clients->lock);
@@ -429,7 +435,10 @@ static int append_event(struct mpv_handle *ctx, struct mpv_event *event)
static int send_event(struct mpv_handle *ctx, struct mpv_event *event)
{
pthread_mutex_lock(&ctx->lock);
- if (!(ctx->event_mask & (1ULL << event->event_id))) {
+ uint64_t mask = 1ULL << event->event_id;
+ if (ctx->property_event_masks & mask)
+ notify_property_events(ctx, mask);
+ if (!(ctx->event_mask & mask)) {
pthread_mutex_unlock(&ctx->lock);
return 0;
}
@@ -541,8 +550,9 @@ int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable)
{
if (!mpv_event_name(event) || enable < 0 || enable > 1)
return MPV_ERROR_INVALID_PARAMETER;
+ assert(event < INTERNAL_EVENT_BASE); // excluded above; they have no name
pthread_mutex_lock(&ctx->lock);
- uint64_t bit = 1LLU << event;
+ uint64_t bit = 1ULL << event;
ctx->event_mask = enable ? ctx->event_mask | bit : ctx->event_mask & ~bit;
pthread_mutex_unlock(&ctx->lock);
return 0;
@@ -1120,12 +1130,15 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
*prop = (struct observe_property){
.client = ctx,
.name = talloc_strdup(prop, name),
+ .id = mp_get_property_id(name),
+ .event_mask = mp_get_property_event_mask(name),
.reply_id = userdata,
.format = format,
.changed = true,
.need_new_value = true,
};
MP_TARRAY_APPEND(ctx, ctx->properties, ctx->num_properties, prop);
+ ctx->property_event_masks |= prop->event_mask;
ctx->lowest_changed = 0;
pthread_mutex_unlock(&ctx->lock);
return 0;
@@ -1134,6 +1147,7 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata)
{
pthread_mutex_lock(&ctx->lock);
+ ctx->property_event_masks = 0;
int count = 0;
for (int n = ctx->num_properties - 1; n >= 0; n--) {
struct observe_property *prop = ctx->properties[n];
@@ -1150,52 +1164,38 @@ int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata)
MP_TARRAY_REMOVE_AT(ctx->properties, ctx->num_properties, n);
count++;
}
+ if (!prop->dead)
+ ctx->property_event_masks |= prop->event_mask;
}
ctx->lowest_changed = 0;
pthread_mutex_unlock(&ctx->lock);
return count;
}
-static int prefix_len(const char *p)
-{
- const char *end = strchr(p, '/');
- return end ? end - p : strlen(p);
-}
-
-static bool match_property(const char *a, const char *b)
+static void mark_property_changed(struct mpv_handle *client, int index)
{
- if (strcmp(b, "*") == 0)
- return true;
- int len_a = prefix_len(a);
- int len_b = prefix_len(b);
- return strncmp(a, b, MPMIN(len_a, len_b)) == 0;
+ struct observe_property *prop = client->properties[index];
+ if (!prop->changed && !prop->need_new_value) {
+ prop->changed = true;
+ prop->need_new_value = prop->format != 0;
+ client->lowest_changed = MPMIN(client->lowest_changed, index);
+ }
}
-// Broadcast that properties have changed.
-void mp_client_property_change(struct MPContext *mpctx, const char *const *list)
+// Broadcast that a property has changed.
+void mp_client_property_change(struct MPContext *mpctx, const char *name)
{
struct mp_client_api *clients = mpctx->clients;
+ int id = mp_get_property_id(name);
pthread_mutex_lock(&clients->lock);
for (int n = 0; n < clients->num_clients; n++) {
struct mpv_handle *client = clients->clients[n];
pthread_mutex_lock(&client->lock);
-
- client->lowest_changed = client->num_properties;
for (int i = 0; i < client->num_properties; i++) {
- struct observe_property *prop = client->properties[i];
- if (!prop->changed && !prop->need_new_value) {
- for (int x = 0; list && list[x]; x++) {
- if (match_property(prop->name, list[x])) {
- prop->changed = true;
- prop->need_new_value = prop->format != 0;
- break;
- }
- }
- }
- if ((prop->changed || prop->updating) && i < client->lowest_changed)
- client->lowest_changed = i;
+ if (client->properties[i]->id == id)
+ mark_property_changed(client, i);
}
if (client->lowest_changed < client->num_properties)
wakeup_client(client);
@@ -1205,6 +1205,18 @@ void mp_client_property_change(struct MPContext *mpctx, const char *const *list)
pthread_mutex_unlock(&clients->lock);
}
+// Mark properties as changed in reaction to specific events.
+// Called with ctx->lock held.
+static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask)
+{
+ for (int i = 0; i < ctx->num_properties; i++) {
+ if (ctx->properties[i]->event_mask & event_mask)
+ mark_property_changed(ctx, i);
+ }
+ if (ctx->lowest_changed < ctx->num_properties)
+ wakeup_client(ctx);
+}
+
static void update_prop(void *p)
{
struct observe_property *prop = p;
diff --git a/player/client.h b/player/client.h
index af94778a55..b329ae6d7b 100644
--- a/player/client.h
+++ b/player/client.h
@@ -17,7 +17,7 @@ int mp_clients_num(struct MPContext *mpctx);
void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data);
int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
int event, void *data);
-void mp_client_property_change(struct MPContext *mpctx, const char *const *list);
+void mp_client_property_change(struct MPContext *mpctx, const char *name);
struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name);
struct mp_log *mp_client_get_log(struct mpv_handle *ctx);
diff --git a/player/command.c b/player/command.c
index df44df5f69..df59e34714 100644
--- a/player/command.c
+++ b/player/command.c
@@ -2834,6 +2834,46 @@ static const char *const *const mp_event_property_change[] = {
};
#undef E
+static int prefix_len(const char *p)
+{
+ const char *end = strchr(p, '/');
+ return end ? end - p : strlen(p);
+}
+
+static bool match_property(const char *a, const char *b)
+{
+ if (strcmp(a, "*") == 0)
+ return true;
+ int len_a = prefix_len(a);
+ int len_b = prefix_len(b);
+ return strncmp(a, b, MPMIN(len_a, len_b)) == 0;
+}
+
+// Return a bitset of events which change the property.
+uint64_t mp_get_property_event_mask(const char *name)
+{
+ uint64_t mask = 0;
+ for (int n = 0; n < MP_ARRAY_SIZE(mp_event_property_change); n++) {
+ const char *const *const list = mp_event_property_change[n];
+ for (int i = 0; list && list[i]; i++) {
+ if (match_property(list[i], name))
+ mask |= 1ULL << n;
+ }
+ }
+ return mask;
+}
+
+// Return an ID for the property. It might not be unique, but is good enough
+// for property change handling. Return -1 if property unknown.
+int mp_get_property_id(const char *name)
+{
+ for (int n = 0; mp_properties[n].name; n++) {
+ if (match_property(mp_properties[n].name, name))
+ return n;
+ }
+ return -1;
+}
+
static bool is_property_set(int action, void *val)
{
switch (action) {
@@ -3921,13 +3961,10 @@ void mp_notify(struct MPContext *mpctx, int event, void *arg)
if (event == MPV_EVENT_START_FILE)
ctx->last_seek_pts = MP_NOPTS_VALUE;
- if (event < INTERNAL_EVENT_BASE)
- mp_client_broadcast_event(mpctx, event, arg);
- if (event >= 0 && event < MP_ARRAY_SIZE(mp_event_property_change))
- mp_client_property_change(mpctx, mp_event_property_change[event]);
+ mp_client_broadcast_event(mpctx, event, arg);
}
void mp_notify_property(struct MPContext *mpctx, const char *property)
{
- mp_client_property_change(mpctx, (const char*[]){property, NULL});
+ mp_client_property_change(mpctx, property);
}
diff --git a/player/command.h b/player/command.h
index 2f9770b714..6e3312b806 100644
--- a/player/command.h
+++ b/player/command.h
@@ -36,6 +36,10 @@ int mp_property_do(const char* name, int action, void* val,
void mp_notify(struct MPContext *mpctx, int event, void *arg);
void mp_notify_property(struct MPContext *mpctx, const char *property);
+int mp_get_property_id(const char *name);
+uint64_t mp_get_property_event_mask(const char *name);
+
+// Must start with the first unused positive value in enum mpv_event_id
#define INTERNAL_EVENT_BASE 24
#define MP_EVENT_CACHE_UPDATE (INTERNAL_EVENT_BASE + 0)