summaryrefslogtreecommitdiff
path: root/libavformat/libsrt.c
diff options
context:
space:
mode:
authorMarton Balint <cus@passwd.hu>2021-01-31 00:45:33 +0100
committerMarton Balint <cus@passwd.hu>2021-02-07 19:42:22 +0100
commitcae57cae1f70371591223a5f9fe3f370ce8dfd50 (patch)
tree4b0af18ae2784ce41439e1c37ec7c24e4f7bfec5 /libavformat/libsrt.c
parent5cf01ed5cdfafbb494ca1725b5408555abc9f223 (diff)
avformat/libsrt: fix race condition with libsrt_network_wait_fd and epoll
The way SRT's async / epoll-based IO works is that the event status is stored in the epoll containers. That is, if an event occurs on an SRT socket, and that SRT socket isn't part of any epoll container, then that event is lost. If we later add that socket to an epoll container, we still won't receive the event even if it wasn't serviced. Therefore we create the epoll and put the fd into it right after the connection is established. See http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2021-January/275334.html Signed-off-by: Marton Balint <cus@passwd.hu>
Diffstat (limited to 'libavformat/libsrt.c')
-rw-r--r--libavformat/libsrt.c63
1 files changed, 39 insertions, 24 deletions
diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c
index f10b043c36..233e9096fa 100644
--- a/libavformat/libsrt.c
+++ b/libavformat/libsrt.c
@@ -163,15 +163,25 @@ static int libsrt_socket_nonblock(int socket, int enable)
return srt_setsockopt(socket, 0, SRTO_RCVSYN, &blocking, sizeof(blocking));
}
-static int libsrt_network_wait_fd(URLContext *h, int eid, int fd, int write)
+static int libsrt_epoll_create(URLContext *h, int fd, int write)
{
- int ret, len = 1, errlen = 1;
int modes = SRT_EPOLL_ERR | (write ? SRT_EPOLL_OUT : SRT_EPOLL_IN);
+ int eid = srt_epoll_create();
+ if (eid < 0)
+ return libsrt_neterrno(h);
+ if (srt_epoll_add_usock(eid, fd, &modes) < 0) {
+ srt_epoll_release(eid);
+ return libsrt_neterrno(h);
+ }
+ return eid;
+}
+
+static int libsrt_network_wait_fd(URLContext *h, int eid, int write)
+{
+ int ret, len = 1, errlen = 1;
SRTSOCKET ready[1];
SRTSOCKET error[1];
- if (srt_epoll_add_usock(eid, fd, &modes) < 0)
- return libsrt_neterrno(h);
if (write) {
ret = srt_epoll_wait(eid, error, &errlen, ready, &len, POLLING_TIME, 0, 0, 0, 0);
} else {
@@ -185,14 +195,12 @@ static int libsrt_network_wait_fd(URLContext *h, int eid, int fd, int write)
} else {
ret = errlen ? AVERROR(EIO) : 0;
}
- if (srt_epoll_remove_usock(eid, fd) < 0)
- return libsrt_neterrno(h);
return ret;
}
/* TODO de-duplicate code from ff_network_wait_fd_timeout() */
-static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb)
+static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int write, int64_t timeout, AVIOInterruptCB *int_cb)
{
int ret;
int64_t wait_start = 0;
@@ -200,7 +208,7 @@ static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int wr
while (1) {
if (ff_check_interrupt(int_cb))
return AVERROR_EXIT;
- ret = libsrt_network_wait_fd(h, eid, fd, write);
+ ret = libsrt_network_wait_fd(h, eid, write);
if (ret != AVERROR(EAGAIN))
return ret;
if (timeout > 0) {
@@ -225,7 +233,7 @@ static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t
if (srt_listen(fd, 1))
return libsrt_neterrno(h);
- ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback);
+ ret = libsrt_network_wait_fd_timeout(h, eid, 1, timeout, &h->interrupt_callback);
if (ret < 0)
return ret;
@@ -245,7 +253,7 @@ static int libsrt_listen_connect(int eid, int fd, const struct sockaddr *addr, s
if (srt_connect(fd, addr, addrlen) < 0)
return libsrt_neterrno(h);
- ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback);
+ ret = libsrt_network_wait_fd_timeout(h, eid, 1, timeout, &h->interrupt_callback);
if (ret < 0) {
if (will_try_next) {
av_log(h, AV_LOG_WARNING,
@@ -359,7 +367,7 @@ static int libsrt_set_options_pre(URLContext *h, int fd)
static int libsrt_setup(URLContext *h, const char *uri, int flags)
{
struct addrinfo hints = { 0 }, *ai, *cur_ai;
- int port, fd = -1;
+ int port, fd;
SRTContext *s = h->priv_data;
const char *p;
char buf[256];
@@ -367,7 +375,7 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
char hostname[1024],proto[1024],path[1024];
char portstr[10];
int64_t open_timeout = 0;
- int eid;
+ int eid, write_eid;
av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
&port, path, sizeof(path), uri);
@@ -404,11 +412,6 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
cur_ai = ai;
- eid = srt_epoll_create();
- if (eid < 0)
- return libsrt_neterrno(h);
- s->eid = eid;
-
restart:
fd = srt_socket(cur_ai->ai_family, cur_ai->ai_socktype, 0);
@@ -432,9 +435,14 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
if (libsrt_socket_nonblock(fd, 1) < 0)
av_log(h, AV_LOG_DEBUG, "libsrt_socket_nonblock failed\n");
+ ret = write_eid = libsrt_epoll_create(h, fd, 1);
+ if (ret < 0)
+ goto fail1;
if (s->mode == SRT_MODE_LISTENER) {
// multi-client
- if ((ret = libsrt_listen(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, s->listen_timeout)) < 0)
+ ret = libsrt_listen(write_eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, s->listen_timeout);
+ srt_epoll_release(write_eid);
+ if (ret < 0)
goto fail1;
srt_close(fd);
fd = ret;
@@ -442,12 +450,15 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
if (s->mode == SRT_MODE_RENDEZVOUS) {
if (srt_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) {
ret = libsrt_neterrno(h);
+ srt_epoll_release(write_eid);
goto fail1;
}
}
- if ((ret = libsrt_listen_connect(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
- open_timeout, h, !!cur_ai->ai_next)) < 0) {
+ ret = libsrt_listen_connect(write_eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
+ open_timeout, h, !!cur_ai->ai_next);
+ srt_epoll_release(write_eid);
+ if (ret < 0) {
if (ret == AVERROR_EXIT)
goto fail1;
else
@@ -468,8 +479,13 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
h->max_packet_size = packet_size;
}
+ ret = eid = libsrt_epoll_create(h, fd, flags & AVIO_FLAG_WRITE);
+ if (eid < 0)
+ goto fail1;
+
h->is_streamed = 1;
s->fd = fd;
+ s->eid = eid;
freeaddrinfo(ai);
return 0;
@@ -487,7 +503,6 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
if (fd >= 0)
srt_close(fd);
freeaddrinfo(ai);
- srt_epoll_release(s->eid);
return ret;
}
@@ -644,7 +659,7 @@ static int libsrt_read(URLContext *h, uint8_t *buf, int size)
int ret;
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
- ret = libsrt_network_wait_fd_timeout(h, s->eid, s->fd, 0, h->rw_timeout, &h->interrupt_callback);
+ ret = libsrt_network_wait_fd_timeout(h, s->eid, 0, h->rw_timeout, &h->interrupt_callback);
if (ret)
return ret;
}
@@ -663,7 +678,7 @@ static int libsrt_write(URLContext *h, const uint8_t *buf, int size)
int ret;
if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
- ret = libsrt_network_wait_fd_timeout(h, s->eid, s->fd, 1, h->rw_timeout, &h->interrupt_callback);
+ ret = libsrt_network_wait_fd_timeout(h, s->eid, 1, h->rw_timeout, &h->interrupt_callback);
if (ret)
return ret;
}
@@ -680,8 +695,8 @@ static int libsrt_close(URLContext *h)
{
SRTContext *s = h->priv_data;
- srt_close(s->fd);
srt_epoll_release(s->eid);
+ srt_close(s->fd);
srt_cleanup();