summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-01-19 19:54:20 +0100
committerwm4 <wm4@nowhere>2015-01-19 21:26:42 +0100
commit64f72687ce4227fc5e6f3636c8fd0078a8e098a2 (patch)
treedf27ca9566e18837a49d5320e50bb7beb73e29d3
parentffaf4af230b92eb94fdf31784ca323cc931822e3 (diff)
downloadmpv-64f72687ce4227fc5e6f3636c8fd0078a8e098a2.tar.bz2
mpv-64f72687ce4227fc5e6f3636c8fd0078a8e098a2.tar.xz
client API: notify API user on event queue overflow
Before this, we merely printed a message to the terminal. Now the API user can determine this properly. This might be important for API users which somehow maintain complex state, which all has to be invalidated if (state-changing) events are missing due to an overflow. This also forces the client API user to empty the event queue, which is good, because otherwise the event queue would reach the "filled up" state immediately again due to further asynchronous events being added to the queue. Also add some minor improvements to mpv_wait_event() documentation, and some other minor cosmetic changes.
-rw-r--r--DOCS/client-api-changes.rst1
-rw-r--r--libmpv/client.h21
-rw-r--r--player/client.c32
-rw-r--r--player/command.h2
4 files changed, 40 insertions, 16 deletions
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 10bcb942ba..8fa325a870 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -25,6 +25,7 @@ API changes
::
+ 1.13 - add MPV_EVENT_QUEUE_OVERFLOW
1.12 - add class Handle to qthelper.hpp
- improve opengl_cb.h API uninitialization behavior, and fix the qml
example
diff --git a/libmpv/client.h b/libmpv/client.h
index a86717df08..7208912ee0 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -167,7 +167,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
-#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 12)
+#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 13)
/**
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
@@ -1117,7 +1117,17 @@ typedef enum mpv_event_id {
* "chapter" property. The event is redundant, and might
* be removed in the far future.
*/
- MPV_EVENT_CHAPTER_CHANGE = 23
+ MPV_EVENT_CHAPTER_CHANGE = 23,
+ /**
+ * Happens if the internal per-mpv_handle ringbuffer overflows, and at
+ * least 1 event had to be dropped. This can happen if the client doesn't
+ * read the event queue quickly enough with mpv_wait_event(), or if the
+ * client makes a very large number of asynchronous calls at once.
+ *
+ * Event delivery will continue normally once this event was returned
+ * (this forces the client to empty the queue completely).
+ */
+ MPV_EVENT_QUEUE_OVERFLOW = 24
// Internal note: adjust INTERNAL_EVENT_BASE when adding new events.
} mpv_event_id;
@@ -1353,8 +1363,8 @@ int mpv_request_log_messages(mpv_handle *ctx, const char *min_level);
* The API won't complain if more than one thread calls this, but it will cause
* race conditions in the client when accessing the shared mpv_event struct.
* Note that most other API functions are not restricted by this, and no API
- * function internally calls mpv_wait_event(). This does not apply to concurrent
- * calls of this function on different mpv_handles: these are always safe.
+ * function internally calls mpv_wait_event(). Additionally, concurrent calls
+ * to different mpv_handles are always safe.
*
* @param timeout Timeout in seconds, after which the function returns even if
* no event was received. A MPV_EVENT_NONE is returned on
@@ -1364,7 +1374,8 @@ int mpv_request_log_messages(mpv_handle *ctx, const char *min_level);
* fields in the struct) stay valid until the next mpv_wait_event()
* call, or until the mpv_handle is destroyed. You must not write to
* the struct, and all memory referenced by it will be automatically
- * released by the API. The return value is never NULL.
+ * released by the API on the next mpv_wait_event() call, or when the
+ * context is destroyed. The return value is never NULL.
*/
mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout);
diff --git a/player/client.c b/player/client.c
index 50daf45393..c4f02b84c8 100644
--- a/player/client.c
+++ b/player/client.c
@@ -107,7 +107,6 @@ struct mpv_handle {
uint64_t event_mask;
bool queued_wakeup;
- bool choke_warning;
int suspend_count;
mpv_event *events; // ringbuffer of max_events entries
@@ -115,6 +114,7 @@ struct mpv_handle {
int first_event; // events[first_event] is the first readable event
int num_events; // number of readable events
int reserved_events; // number of entries reserved for replies
+ bool choked; // recovering from queue overflow
struct observe_property **properties;
int num_properties;
@@ -541,7 +541,8 @@ static int reserve_reply(struct mpv_handle *ctx)
{
int res = MPV_ERROR_EVENT_QUEUE_FULL;
pthread_mutex_lock(&ctx->lock);
- if (ctx->reserved_events + ctx->num_events < ctx->max_events) {
+ if (ctx->reserved_events + ctx->num_events < ctx->max_events && !ctx->choked)
+ {
ctx->reserved_events++;
res = 0;
}
@@ -567,14 +568,17 @@ static int send_event(struct mpv_handle *ctx, struct mpv_event *event, bool copy
uint64_t mask = 1ULL << event->event_id;
if (ctx->property_event_masks & mask)
notify_property_events(ctx, mask);
+ int r;
if (!(ctx->event_mask & mask)) {
- pthread_mutex_unlock(&ctx->lock);
- return 0;
- }
- int r = append_event(ctx, *event, copy);
- if (r < 0 && !ctx->choke_warning) {
- mp_err(ctx->log, "Too many events queued.\n");
- ctx->choke_warning = true;
+ r = 0;
+ } else if (ctx->choked) {
+ r = -1;
+ } else {
+ r = append_event(ctx, *event, copy);
+ if (r < 0) {
+ MP_ERR(ctx, "Too many events queued.\n");
+ ctx->choked = true;
+ }
}
pthread_mutex_unlock(&ctx->lock);
return r;
@@ -729,6 +733,12 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
while (1) {
if (ctx->queued_wakeup)
deadline = 0;
+ // Recover from overflow.
+ if (ctx->choked && !ctx->num_events) {
+ ctx->choked = false;
+ event->event_id = MPV_EVENT_QUEUE_OVERFLOW;
+ break;
+ }
// This will almost surely lead to a deadlock. (Polling is still ok.)
if (ctx->suspend_count && timeout > 0) {
MP_ERR(ctx, "attempting to wait while core is suspended");
@@ -741,10 +751,11 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
talloc_steal(event, event->data);
break;
}
+ // If there's a changed property, generate change event (never queued).
if (gen_property_change_event(ctx))
break;
+ // Pop item from message queue, and return as event.
if (ctx->messages) {
- // Poll the log message queue. Currently we can't/don't do better.
struct mp_log_buffer_entry *msg =
mp_msg_log_buffer_read(ctx->messages);
if (msg) {
@@ -1608,6 +1619,7 @@ static const char *const event_table[] = {
[MPV_EVENT_PLAYBACK_RESTART] = "playback-restart",
[MPV_EVENT_PROPERTY_CHANGE] = "property-change",
[MPV_EVENT_CHAPTER_CHANGE] = "chapter-change",
+ [MPV_EVENT_QUEUE_OVERFLOW] = "event-queue-overflow",
};
const char *mpv_event_name(mpv_event_id event)
diff --git a/player/command.h b/player/command.h
index d4097fee6f..d6891b66cd 100644
--- a/player/command.h
+++ b/player/command.h
@@ -42,7 +42,7 @@ uint64_t mp_get_property_event_mask(const char *name);
enum {
// Must start with the first unused positive value in enum mpv_event_id
// MPV_EVENT_* and MP_EVENT_* must not overlap.
- INTERNAL_EVENT_BASE = 24,
+ INTERNAL_EVENT_BASE = 25,
MP_EVENT_CACHE_UPDATE,
MP_EVENT_WIN_RESIZE,
MP_EVENT_WIN_STATE,