From b4304f243c6006eec3ca4b4ce2b19cfca703861a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Thu, 12 Apr 2007 22:14:06 +0200 Subject: Extend ticket system for nonblocking ticket acquiries. The current code has a race condition which can block arbitrary threads that call for example xine_get_current_frame() until the stream gets unpaused again. This can happen when the internal ticket acquiration collides with a ticket revokation for example when another thread is going to pause the stream. There are a few situations where a port ticket needs to be acquired for calling a port function but where it is absolutely undesireable to get blocked for an undetermined period of time. Therefore the ticket system should be extended by nonblocking functions which allow ticket acquiration even when a ticket revokation is in progress. And in the case where blocking is not avoidable, it should simply be indicated that no ticket was acquired. The caller can then choose to repeat the call at a later point in time. --- src/xine-engine/xine.c | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) (limited to 'src/xine-engine/xine.c') diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index f49a988c9..612bf8dcc 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -127,23 +127,42 @@ void _x_extra_info_merge( extra_info_t *dst, extra_info_t *src ) { } } -static void ticket_acquire(xine_ticket_t *this, int irrevocable) { +static int ticket_acquire_internal(xine_ticket_t *this, int irrevocable, int nonblocking) { + int must_wait = 0; pthread_mutex_lock(&this->lock); if (this->ticket_revoked && !this->irrevocable_tickets) - pthread_cond_wait(&this->issued, &this->lock); + must_wait = !nonblocking; else if (this->atomic_revoke && !pthread_equal(this->atomic_revoker_thread, pthread_self())) + must_wait = 1; + + if (must_wait) { + if (nonblocking) { + pthread_mutex_unlock(&this->lock); + return 0; + } + pthread_cond_wait(&this->issued, &this->lock); + } this->tickets_granted++; if (irrevocable) this->irrevocable_tickets++; pthread_mutex_unlock(&this->lock); + return 1; } -static void ticket_release(xine_ticket_t *this, int irrevocable) { +static int ticket_acquire_nonblocking(xine_ticket_t *this, int irrevocable) { + return ticket_acquire_internal(this, irrevocable, 1); +} + +static void ticket_acquire(xine_ticket_t *this, int irrevocable) { + ticket_acquire_internal(this, irrevocable, 0); +} + +static void ticket_release_internal(xine_ticket_t *this, int irrevocable, int nonblocking) { pthread_mutex_lock(&this->lock); @@ -153,12 +172,20 @@ static void ticket_release(xine_ticket_t *this, int irrevocable) { if (this->ticket_revoked && !this->tickets_granted) pthread_cond_broadcast(&this->revoked); - if (this->ticket_revoked && !this->irrevocable_tickets) + if (this->ticket_revoked && !this->irrevocable_tickets && !nonblocking) pthread_cond_wait(&this->issued, &this->lock); pthread_mutex_unlock(&this->lock); } +static void ticket_release_nonblocking(xine_ticket_t *this, int irrevocable) { + ticket_release_internal(this, irrevocable, 1); +} + +static void ticket_release(xine_ticket_t *this, int irrevocable) { + ticket_release_internal(this, irrevocable, 0); +} + static void ticket_renew(xine_ticket_t *this, int irrevocable) { pthread_mutex_lock(&this->lock); @@ -227,12 +254,14 @@ static xine_ticket_t *ticket_init(void) { port_ticket = (xine_ticket_t *) xine_xmalloc(sizeof(xine_ticket_t)); - port_ticket->acquire = ticket_acquire; - port_ticket->release = ticket_release; - port_ticket->renew = ticket_renew; - port_ticket->issue = ticket_issue; - port_ticket->revoke = ticket_revoke; - port_ticket->dispose = ticket_dispose; + port_ticket->acquire_nonblocking = ticket_acquire_nonblocking; + port_ticket->acquire = ticket_acquire; + port_ticket->release_nonblocking = ticket_release_nonblocking; + port_ticket->release = ticket_release; + port_ticket->renew = ticket_renew; + port_ticket->issue = ticket_issue; + port_ticket->revoke = ticket_revoke; + port_ticket->dispose = ticket_dispose; pthread_mutex_init(&port_ticket->lock, NULL); pthread_mutex_init(&port_ticket->revoke_lock, NULL); -- cgit v1.2.3 From bd88a5c94af0af727680606a22ec9414fba68366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Thu, 12 Apr 2007 22:33:26 +0200 Subject: Provide a function to query buffer usage. This function shall be used to poll the number of remaining frames from a certain point in time on until the reported numbers are all 0. At that point in time, the content on screen is identical to a certain state of the stream, at which for example, a hardcopy may be taken. --- src/xine-engine/xine.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/xine-engine/xine.c') diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index 612bf8dcc..9623668dc 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -2069,3 +2069,31 @@ int xine_stream_master_slave(xine_stream_t *master, xine_stream_t *slave, slave->master = master->master; return 1; } + +int _x_query_buffer_usage(xine_stream_t *stream, int *num_video_buffers, int *num_audio_buffers, int *num_video_frames, int *num_audio_frames) +{ + int ticket_acquired = -1; + + if (num_video_buffers) + *num_video_buffers = (stream->video_fifo ? stream->video_fifo->size(stream->video_fifo) : 0); + + if (num_audio_buffers) + *num_audio_buffers = (stream->audio_fifo ? stream->audio_fifo->size(stream->audio_fifo) : 0); + + if ((num_video_frames && stream->video_out) + || (num_audio_frames && stream->audio_out)) { + + ticket_acquired = stream->xine->port_ticket->acquire_nonblocking(stream->xine->port_ticket, 1); + } + + if (num_video_frames) + *num_video_frames = ((ticket_acquired && stream->video_out) ? stream->video_out->get_property(stream->video_out, VO_PROP_BUFS_IN_FIFO) : 0); + + if (num_audio_frames) + *num_audio_frames = ((ticket_acquired && stream->audio_out) ? stream->audio_out->get_property(stream->audio_out, AO_PROP_BUFS_IN_FIFO) : 0); + + if (ticket_acquired > 0) + stream->xine->port_ticket->release_nonblocking(stream->xine->port_ticket, 1); + + return ticket_acquired != 0; +} -- cgit v1.2.3 From 0638ad373d88c3ed45273a505df56626daba19a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Fri, 13 Apr 2007 00:17:30 +0200 Subject: Provide ability to lock port rewiring. The idea is to allow only a "single" frontend to rewire ports at a certain point in time. Regarding a stream, frontend_lock is used for example to allow only a single frontend to change the speed. Unfortunately, frontend_lock cannot be used as the rewire functions are not stream related. Therefore a new port_rewiring_lock was introduced and used at appropriate locations. When an arbitrary thread now holds the frontend_lock and the port_rewiring_lock, it is safe that acquiring a port ticket in functions like xine_get_current_frame() will never block the thread. --- src/xine-engine/xine.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'src/xine-engine/xine.c') diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index 612bf8dcc..aa84917bb 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -239,8 +239,37 @@ static void ticket_revoke(xine_ticket_t *this, int atomic) { pthread_mutex_unlock(&this->revoke_lock); } +static int ticket_lock_port_rewiring(xine_ticket_t *this, int ms_timeout) { + + if (ms_timeout >= 0) { + struct timespec abstime; + + struct timeval now; + gettimeofday(&now, 0); + + abstime.tv_sec = now.tv_sec + ms_timeout / 1000; + abstime.tv_nsec = now.tv_usec * 1000 + (ms_timeout % 1000) * 1e6; + + if (abstime.tv_nsec > 1e9) { + abstime.tv_nsec -= 1e9; + abstime.tv_sec++; + } + + return (0 == pthread_mutex_timedlock(&this->port_rewiring_lock, &abstime)); + } + + pthread_mutex_lock(&this->port_rewiring_lock); + return 1; +} + +static void ticket_unlock_port_rewiring(xine_ticket_t *this) { + + pthread_mutex_unlock(&this->port_rewiring_lock); +} + static void ticket_dispose(xine_ticket_t *this) { + pthread_mutex_destroy(&this->port_rewiring_lock); pthread_mutex_destroy(&this->lock); pthread_mutex_destroy(&this->revoke_lock); pthread_cond_destroy(&this->issued); @@ -261,10 +290,13 @@ static xine_ticket_t *ticket_init(void) { port_ticket->renew = ticket_renew; port_ticket->issue = ticket_issue; port_ticket->revoke = ticket_revoke; + port_ticket->lock_port_rewiring = ticket_lock_port_rewiring; + port_ticket->unlock_port_rewiring = ticket_unlock_port_rewiring; port_ticket->dispose = ticket_dispose; pthread_mutex_init(&port_ticket->lock, NULL); pthread_mutex_init(&port_ticket->revoke_lock, NULL); + pthread_mutex_init(&port_ticket->port_rewiring_lock, NULL); pthread_cond_init(&port_ticket->issued, NULL); pthread_cond_init(&port_ticket->revoked, NULL); @@ -458,6 +490,7 @@ static int stream_rewire_audio(xine_post_out_t *output, void *data) if (!data) return 0; + stream->xine->port_ticket->lock_port_rewiring(stream->xine->port_ticket, -1); stream->xine->port_ticket->revoke(stream->xine->port_ticket, 1); if (stream->audio_out->status(stream->audio_out, stream, &bits, &rate, &mode)) { @@ -468,6 +501,7 @@ static int stream_rewire_audio(xine_post_out_t *output, void *data) stream->audio_out = new_port; stream->xine->port_ticket->issue(stream->xine->port_ticket, 1); + stream->xine->port_ticket->unlock_port_rewiring(stream->xine->port_ticket); return 1; } @@ -482,6 +516,7 @@ static int stream_rewire_video(xine_post_out_t *output, void *data) if (!data) return 0; + stream->xine->port_ticket->lock_port_rewiring(stream->xine->port_ticket, -1); stream->xine->port_ticket->revoke(stream->xine->port_ticket, 1); if (stream->video_out->status(stream->video_out, stream, &width, &height, &img_duration)) { @@ -492,6 +527,7 @@ static int stream_rewire_video(xine_post_out_t *output, void *data) stream->video_out = new_port; stream->xine->port_ticket->issue(stream->xine->port_ticket, 1); + stream->xine->port_ticket->unlock_port_rewiring(stream->xine->port_ticket); return 1; } -- cgit v1.2.3 From 31bb62fae17fe849704c5d3fe78090a3df92cead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Fri, 13 Apr 2007 00:32:19 +0200 Subject: Provide internal functions to lock port rewiring. The introduced function give "frontend like" plugins a chance to lock and unlock port rewiring. This protects such threads (when combined with holding the frontend lock) from beeing blocked when calling functions like xine_get_current_frame(). --- src/xine-engine/xine.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/xine-engine/xine.c') diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index a780aa4dc..1a9a2be4d 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -2133,3 +2133,13 @@ int _x_query_buffer_usage(xine_stream_t *stream, int *num_video_buffers, int *nu return ticket_acquired != 0; } + +int _x_lock_port_rewiring(xine_t *xine, int ms_timeout) +{ + return xine->port_ticket->lock_port_rewiring(xine->port_ticket, ms_timeout); +} + +void _x_unlock_port_rewiring(xine_t *xine) +{ + xine->port_ticket->unlock_port_rewiring(xine->port_ticket); +} -- cgit v1.2.3 From f8e051109fc70cbf7acbfd6582bc2f6d03e6d93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Fri, 13 Apr 2007 00:39:06 +0200 Subject: Provide internal functions to lock frontend_lock. The introduced functions give "frontend like" plugins a chance to lock and unlock frontend_lock. This protects such threads for example from beeing blocked when changing the streams speed. --- src/xine-engine/xine.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/xine-engine/xine.c') diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index 1a9a2be4d..ce7713810 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -2143,3 +2143,31 @@ void _x_unlock_port_rewiring(xine_t *xine) { xine->port_ticket->unlock_port_rewiring(xine->port_ticket); } + +int _x_lock_frontend(xine_stream_t *stream, int ms_to_time_out) +{ + if (ms_to_time_out >= 0) { + struct timespec abstime; + + struct timeval now; + gettimeofday(&now, 0); + + abstime.tv_sec = now.tv_sec + ms_to_time_out / 1000; + abstime.tv_nsec = now.tv_usec * 1000 + (ms_to_time_out % 1000) * 1e6; + + if (abstime.tv_nsec > 1e9) { + abstime.tv_nsec -= 1e9; + abstime.tv_sec++; + } + + return (0 == pthread_mutex_timedlock(&stream->frontend_lock, &abstime)); + } + + pthread_mutex_lock(&stream->frontend_lock); + return 1; +} + +void _x_unlock_frontend(xine_stream_t *stream) +{ + pthread_mutex_unlock(&stream->frontend_lock); +} -- cgit v1.2.3 From ee7faf25388d4a2890bf3fa55288391cfe04851a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Sun, 15 Apr 2007 20:43:42 +0200 Subject: Provide a function to query for outstanding OSD events. This function shall be used to poll the number of outstanding OSD events from a certain point in time on until the reported number is 0. At that point in time, the content on screen is identical to a certain state of the stream, at which for example, a hardcopy may be taken. --- src/xine-engine/xine.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/xine-engine/xine.c') diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c index ce7713810..840c91f3f 100644 --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -2171,3 +2171,22 @@ void _x_unlock_frontend(xine_stream_t *stream) { pthread_mutex_unlock(&stream->frontend_lock); } + +int _x_query_unprocessed_osd_events(xine_stream_t *stream) +{ + video_overlay_manager_t *ovl; + int redraw_needed; + + if (!stream->xine->port_ticket->acquire_nonblocking(stream->xine->port_ticket, 1)) + return -1; + + ovl = stream->video_out->get_overlay_manager(stream->video_out); + redraw_needed = ovl->redraw_needed(ovl, 0); + + if (redraw_needed) + stream->video_out->trigger_drawing(stream->video_out); + + stream->xine->port_ticket->release_nonblocking(stream->xine->port_ticket, 1); + + return redraw_needed; +} -- cgit v1.2.3