Exit from RTP packet dispatcher gracefully

Indicate to the main thread through a mutex that the packet dispatcher
object can be destroyed. If this is not done, there's race condition
that can result into a segmentation fault as the dispatcher object
could be destroyed before it stops running.
This commit is contained in:
Aaro Altonen 2020-08-05 11:06:15 +03:00
parent bdfe027352
commit eb33b7edf7
3 changed files with 36 additions and 6 deletions

View File

@ -35,7 +35,13 @@ namespace uvg_rtp {
* Return RTP_MEMORY_ERROR if allocation of a thread object fails */
rtp_error_t start(uvg_rtp::socket *socket, int flags);
/* Fetchj frame from the frame queue that contains all received frame.
/* Stop the RTP packet dispatcher and wait until the receive loop is exited
* to make sure that destroying the object in media_stream.cc is safe
*
* Return RTP_OK on success */
rtp_error_t stop();
/* Fetch frame from the frame queue that contains all received frame.
* pull_frame() will block until there is a frame that can be returned.
* If "timeout" is given, pull_frame() will block only for however long
* that value tells it to.
@ -53,7 +59,12 @@ namespace uvg_rtp {
void return_frame(uvg_rtp::frame::rtp_frame *frame);
/* RTP packet dispatcher thread */
static void runner(uvg_rtp::pkt_dispatcher *dispatcher, uvg_rtp::socket *socket, int flags);
static void runner(
uvg_rtp::pkt_dispatcher *dispatcher,
uvg_rtp::socket *socket,
int flags,
std::mutex *exit_mtx
);
private:
std::vector<packet_handler> packet_handlers_;
@ -62,6 +73,7 @@ namespace uvg_rtp {
* and they can be retrieved using pull_frame() */
std::vector<uvg_rtp::frame::rtp_frame *> frames_;
std::mutex frames_mtx_;
std::mutex exit_mtx_;
void *recv_hook_arg_;
void (*recv_hook_)(void *arg, uvg_rtp::frame::rtp_frame *frame);

View File

@ -50,8 +50,7 @@ uvg_rtp::media_stream::media_stream(
uvg_rtp::media_stream::~media_stream()
{
if (initialized_) {
}
pkt_dispatcher_->stop();
delete rtcp_;
delete rtp_;

View File

@ -23,13 +23,23 @@ uvg_rtp::pkt_dispatcher::~pkt_dispatcher()
rtp_error_t uvg_rtp::pkt_dispatcher::start(uvg_rtp::socket *socket, int flags)
{
if (!(runner_ = new std::thread(runner, this, socket, flags)))
if (!(runner_ = new std::thread(runner, this, socket, flags, &exit_mtx_)))
return RTP_MEMORY_ERROR;
runner_->detach();
return uvg_rtp::runner::start();
}
rtp_error_t uvg_rtp::pkt_dispatcher::stop()
{
active_ = false;
while (!exit_mtx_.try_lock())
;
return RTP_OK;
}
rtp_error_t uvg_rtp::pkt_dispatcher::install_receive_hook(
void *arg,
void (*hook)(void *, uvg_rtp::frame::rtp_frame *)
@ -139,7 +149,12 @@ void uvg_rtp::pkt_dispatcher::return_frame(uvg_rtp::frame::rtp_frame *frame)
*
* If a handler receives a non-null "out", it can safely ignore "packet" and operate just on
* the "out" parameter because at that point it already contains all needed information. */
void uvg_rtp::pkt_dispatcher::runner(uvg_rtp::pkt_dispatcher *dispatcher, uvg_rtp::socket *socket, int flags)
void uvg_rtp::pkt_dispatcher::runner(
uvg_rtp::pkt_dispatcher *dispatcher,
uvg_rtp::socket *socket,
int flags,
std::mutex *exit_mtx
)
{
int nread;
fd_set read_fds;
@ -158,6 +173,8 @@ void uvg_rtp::pkt_dispatcher::runner(uvg_rtp::pkt_dispatcher *dispatcher, uvg_rt
while (!dispatcher->active())
;
exit_mtx->lock();
while (dispatcher->active()) {
FD_SET(socket->get_raw_socket(), &read_fds);
int sret = ::select(socket->get_raw_socket() + 1, &read_fds, nullptr, nullptr, &t_val);
@ -205,4 +222,6 @@ void uvg_rtp::pkt_dispatcher::runner(uvg_rtp::pkt_dispatcher *dispatcher, uvg_rt
}
} while (ret == RTP_OK);
}
exit_mtx->unlock();
}