input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
/*****************************************************************************
|
|
|
|
|
* vlc_interrupt.h:
|
|
|
|
|
*****************************************************************************
|
|
|
|
|
* Copyright (C) 2015 Remlab T:mi
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#ifndef VLC_INTERRUPT_H
|
|
|
|
|
# define VLC_INTERRUPT_H 1
|
|
|
|
|
# include <vlc_threads.h>
|
|
|
|
|
# ifndef _WIN32
|
|
|
|
|
# include <sys/socket.h> /* socklen_t */
|
|
|
|
|
# else
|
|
|
|
|
# include <ws2tcpip.h>
|
|
|
|
|
# endif
|
|
|
|
|
|
|
|
|
|
struct pollfd;
|
|
|
|
|
struct iovec;
|
|
|
|
|
struct sockaddr;
|
|
|
|
|
struct msghdr;
|
|
|
|
|
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
/**
|
|
|
|
|
* @defgroup interrupt Interruptible sleep
|
|
|
|
|
* @ingroup thread
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
* @{
|
|
|
|
|
* @file
|
|
|
|
|
* This file declares interruptible sleep functions.
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
* @defgroup interrupt_sleep Interruptible sleep functions
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interruptible variant of vlc_sem_wait().
|
|
|
|
|
*
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
* Waits on a semaphore like vlc_sem_wait(). If the calling thread has an
|
|
|
|
|
* interruption context (as set by vlc_interrupt_set()), and another thread
|
|
|
|
|
* invokes vlc_interrupt_raise() on that context, the semaphore is incremented.
|
|
|
|
|
*
|
|
|
|
|
* @warning The calling thread should be the only thread ever to wait on the
|
|
|
|
|
* specified semaphore. Otherwise, interruptions may not be delivered
|
|
|
|
|
* accurately (the wrong thread may be woken up).
|
|
|
|
|
*
|
|
|
|
|
* @note This function is (always) a cancellation point.
|
|
|
|
|
*
|
|
|
|
|
* @return EINTR if the semaphore was incremented due to an interruption,
|
|
|
|
|
* otherwise zero.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API int vlc_sem_wait_i11e(vlc_sem_t *);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interruptible variant of vlc_tick_wait().
|
|
|
|
|
*
|
|
|
|
|
* Waits for a specified timestamp or, if the calling thread has an
|
|
|
|
|
* interruption context, an interruption.
|
|
|
|
|
*
|
|
|
|
|
* @return EINTR if an interruption occurred, otherwise 0 once the timestamp is
|
|
|
|
|
* reached.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API int vlc_mwait_i11e(vlc_tick_t);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interruptible variant of vlc_tick_sleep().
|
|
|
|
|
*
|
|
|
|
|
* Waits for a specified timeout duration or, if the calling thread has an
|
|
|
|
|
* interruption context, an interruption.
|
|
|
|
|
*
|
|
|
|
|
* @param delay timeout value (in microseconds)
|
|
|
|
|
*
|
|
|
|
|
* @return EINTR if an interruption occurred, otherwise 0 once the timeout
|
|
|
|
|
* expired.
|
|
|
|
|
*/
|
|
|
|
|
static inline int vlc_msleep_i11e(vlc_tick_t delay)
|
|
|
|
|
{
|
|
|
|
|
return vlc_mwait_i11e(vlc_tick_now() + delay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interruptible variant of poll().
|
|
|
|
|
*
|
|
|
|
|
* Waits for file descriptors I/O events, a timeout, a signal or a VLC I/O
|
|
|
|
|
* interruption. Except for VLC I/O interruptions, this function behaves
|
|
|
|
|
* just like the standard poll().
|
|
|
|
|
*
|
|
|
|
|
* @note This function is always a cancellation point (as poll()).
|
|
|
|
|
* @see poll() manual page
|
|
|
|
|
*
|
|
|
|
|
* @param fds table of events to wait for
|
|
|
|
|
* @param nfds number of entries in the table
|
|
|
|
|
* @param timeout time to wait in milliseconds or -1 for infinite
|
|
|
|
|
*
|
|
|
|
|
* @return A strictly positive result represent the number of pending events.
|
|
|
|
|
* 0 is returned if the time-out is reached without events.
|
|
|
|
|
* -1 is returned if a VLC I/O interrupt occurs (and errno is set to EINTR)
|
|
|
|
|
* or if an error occurs.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API int vlc_poll_i11e(struct pollfd *, unsigned, int);
|
|
|
|
|
|
|
|
|
|
VLC_API ssize_t vlc_readv_i11e(int fd, struct iovec *, int);
|
|
|
|
|
VLC_API ssize_t vlc_writev_i11e(int fd, const struct iovec *, int);
|
|
|
|
|
VLC_API ssize_t vlc_read_i11e(int fd, void *, size_t);
|
|
|
|
|
VLC_API ssize_t vlc_write_i11e(int fd, const void *, size_t);
|
|
|
|
|
|
|
|
|
|
VLC_API ssize_t vlc_recvmsg_i11e(int fd, struct msghdr *, int flags);
|
|
|
|
|
VLC_API ssize_t vlc_sendmsg_i11e(int fd, const struct msghdr *, int flags);
|
|
|
|
|
|
|
|
|
|
VLC_API ssize_t vlc_recvfrom_i11e(int fd, void *, size_t, int flags,
|
|
|
|
|
struct sockaddr *, socklen_t *);
|
|
|
|
|
VLC_API ssize_t vlc_sendto_i11e(int fd, const void *, size_t, int flags,
|
|
|
|
|
const struct sockaddr *, socklen_t);
|
|
|
|
|
|
|
|
|
|
static inline ssize_t vlc_recv_i11e(int fd, void *buf, size_t len, int flags)
|
|
|
|
|
{
|
|
|
|
|
return vlc_recvfrom_i11e(fd, buf, len, flags, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline
|
|
|
|
|
ssize_t vlc_send_i11e(int fd, const void *buf, size_t len, int flags)
|
|
|
|
|
{
|
|
|
|
|
return vlc_sendto_i11e(fd, buf, len, flags, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VLC_API int vlc_accept_i11e(int fd, struct sockaddr *, socklen_t *, bool);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Registers a custom interrupt handler.
|
|
|
|
|
*
|
|
|
|
|
* Registers a custom callback as interrupt handler for the calling thread.
|
|
|
|
|
* The callback must be unregistered with vlc_interrupt_unregister() before
|
|
|
|
|
* thread termination and before any further callback registration.
|
|
|
|
|
*
|
|
|
|
|
* If the calling thread has no interruption context, this function has no
|
|
|
|
|
* effects.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_interrupt_register(void (*cb)(void *), void *opaque);
|
|
|
|
|
|
|
|
|
|
VLC_API int vlc_interrupt_unregister(void);
|
|
|
|
|
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
/**
|
|
|
|
|
* @}
|
|
|
|
|
* @defgroup interrupt_context Interrupt context signaling and manipulation
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
typedef struct vlc_interrupt vlc_interrupt_t;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates an interruption context.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API vlc_interrupt_t *vlc_interrupt_create(void) VLC_USED;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Destroys an interrupt context.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_interrupt_destroy(vlc_interrupt_t *);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the interruption context for the calling thread.
|
|
|
|
|
* @param newctx the interruption context to attach or NULL for none
|
|
|
|
|
* @return the previous interruption context or NULL if none
|
|
|
|
|
*
|
|
|
|
|
* @note This function is not a cancellation point.
|
|
|
|
|
* @warning A context can be attached to no more than one thread at a time.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API vlc_interrupt_t *vlc_interrupt_set(vlc_interrupt_t *);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Raises an interruption through a specified context.
|
|
|
|
|
*
|
|
|
|
|
* This is used to asynchronously wake a thread up while it is waiting on some
|
|
|
|
|
* other events (typically I/O events).
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
*
|
|
|
|
|
* @note This function is thread-safe.
|
|
|
|
|
* @note This function is not a cancellation point.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_interrupt_raise(vlc_interrupt_t *);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Marks the interruption context as "killed".
|
|
|
|
|
*
|
|
|
|
|
* This is not reversible.
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_interrupt_kill(vlc_interrupt_t *);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if the interruption context was "killed".
|
|
|
|
|
*
|
|
|
|
|
* Indicates whether the interruption context of the calling thread (if any)
|
|
|
|
|
* was killed with vlc_interrupt_kill().
|
|
|
|
|
*/
|
|
|
|
|
VLC_API bool vlc_killed(void) VLC_USED;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enables forwarding of interruption.
|
|
|
|
|
*
|
|
|
|
|
* If an interruption is raised through the context of the calling thread,
|
|
|
|
|
* it will be forwarded to the specified other context. This is used to cross
|
|
|
|
|
* thread boundaries.
|
|
|
|
|
*
|
|
|
|
|
* If the calling thread has no interrupt context, this function does nothing.
|
|
|
|
|
*
|
|
|
|
|
* @param to context to forward to
|
|
|
|
|
* @param data opaque data pointer for the callback
|
|
|
|
|
*/
|
|
|
|
|
VLC_API void vlc_interrupt_forward_start(vlc_interrupt_t *to,
|
|
|
|
|
void *data[2]);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Undoes vlc_interrupt_forward_start().
|
|
|
|
|
*
|
|
|
|
|
* This function must be called after each successful call to
|
|
|
|
|
* vlc_interrupt_forward_start() before any other interruptible call is made
|
|
|
|
|
* in the same thread.
|
|
|
|
|
*
|
|
|
|
|
* If an interruption was raised against the context of the calling thread
|
|
|
|
|
* (after the previous call to vlc_interrupt_forward_start()), it is dequeued.
|
|
|
|
|
*
|
|
|
|
|
* If the calling thread has no interrupt context, this function does nothing
|
|
|
|
|
* and returns zero.
|
|
|
|
|
*
|
|
|
|
|
* @return 0 if no interrupt was raised, EINTR if an interrupt was raised
|
|
|
|
|
*/
|
|
|
|
|
VLC_API int vlc_interrupt_forward_stop(void *const data[2]);
|
|
|
|
|
|
|
|
|
|
/** @} */
|
|
|
|
|
/** @} */
|
input: add per-thread sleep interruption framework
For the sake of simplicity and for historical reasons, access and demux
modules perform I/O in blocking mode. If no data is available (or more
generally no I/O events), the blocking I/O calls will sleep and hold
the whole input thread. This can lead to long time-outs or even
complete deadlocks, e.g. notably in case of network error.
Originally, a volatile flag (b_die) was checked at frequent interval to
ascertain whether to abort. This violated the threaded memory model,
and was incompatible with race-to-idle power management.
In 2007, the VLC object thread signaling functions were introduced
(vlc_object_wait, vlc_object_signal, ...) in an attempt to solve this.
They proved inflexible and were not compatible with poll/select-style
I/O events multiplexing. Those functions were ultimately removed a
little over a year later.
In the mean time, the "wait pipe" had been introduced. It was focused
on network/socket data reception. While it continues to be used, it
suffers several limitations:
- it affects other threads using the same VLC object,
and indistinctly disrupts all I/O after the "kill",
- it incorrectly assumes that the same VLC object is used everywhere
(leading to race conditions and live loops),
- the convenience wrappers around the wait pipe can only wait on one
single I/O event direction on a single file descriptor at a time,
- it is currently tied to the VLC input thread.
Also at about the same time, thread cancellation was reintroduced.
Thread cancellation has proven helpful for simple thread main loops.
But it ranges from impractical to unusable when sleeping deep within
layers of code, such as in access and stream modules.
Generally the problem of interrupting I/O is an intractable halting
problem. And in practice a given reading operations inside a demuxer
cannot be interrupted without breaking the state machine of the
demuxer - in many or most cases. This changes set is only an attempt
to complement thread cancellation, This does overcome most limitations
of the existing "wait pipe" system and of former VLC object signals:
- It is triggered by a function call specifying a target context.
The context is tied to the thread that needs to be woken up from
sleep. This works quite well because the problem essentially relates
to the call flow of the sleeping thread. On the trigger side, this is
similar to thread cancellation.
- It leaves some flexibility w.r.t. choice of sleeping primitives.
This initial change uses semaphores. Low-level file I/O will be
introduced later.
- The wake-up mechanism is edge-triggered and can be fired multiple
times. Thus it does not irreversibly prevent all I/O and sleeping
operations once fired. It only interrupts the ongoing or next sleep.
In principles non-fatal interruptions could be handled that way, for
instance input thread seek (rather than forceful stop) although that
is not part of the changes set.
- It is not tied to any specific event. The initial use case is
stopping the input thread and checking vlc_object_alive() but it can
be used for other purposes.
11 years ago
|
|
|
#endif
|