Commit 2307a76a authored by Hirokazu Honda's avatar Hirokazu Honda Committed by Commit Bot

media/gpu/V4L2VEA: Post task to encoder_thread_task_runner with WeakPtr of this

At V4L2VEA::Destroy(), encoder thread process all the pending tasks. If
PumpBitstreamBuffers() is in the pending tasks, Enqueue() is posted and
processed after DestroyTask(). That leads |input_queue_| and |output_queue_| are
nullptr and causes SIGSEGV.

This fixes the issue by posting tasks to encoder_thread_.task_runner() with
WeakPtr of this. This makes V4L2VEA not execute any tasks posted to the task
runner after DestroyTask() because the WeakPtr is invalidated in DestroyTask().

Bug: 1019437
Test: VEA unittest on guado with dcheck_always_on=true
Change-Id: Ie2e43d1dd9c74114cbd0d2b51e260213b60136e5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1890462
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: default avatarChih-Yu Huang <akahuang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711148}
parent 52320a3c
...@@ -216,9 +216,8 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config, ...@@ -216,9 +216,8 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
bool result = false; bool result = false;
base::WaitableEvent done; base::WaitableEvent done;
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::InitializeTask,
base::BindOnce(&V4L2VideoEncodeAccelerator::InitializeTask, weak_this_, config, &result, &done));
base::Unretained(this), config, &result, &done));
done.Wait(); done.Wait();
return result; return result;
} }
...@@ -317,11 +316,8 @@ bool V4L2VideoEncodeAccelerator::CreateImageProcessor( ...@@ -317,11 +316,8 @@ bool V4L2VideoEncodeAccelerator::CreateImageProcessor(
{ImageProcessor::OutputMode::ALLOCATE, {ImageProcessor::OutputMode::ALLOCATE,
ImageProcessor::OutputMode::IMPORT}, ImageProcessor::OutputMode::IMPORT},
kImageProcBufferCount, kImageProcBufferCount,
// Unretained(this) is safe here, because image_processor is destroyed BindToCurrentLoop(base::BindRepeating(
// before video_encoder_thread stops. &V4L2VideoEncodeAccelerator::ImageProcessorError, weak_this_)));
BindToCurrentLoop(
base::BindRepeating(&V4L2VideoEncodeAccelerator::ImageProcessorError,
base::Unretained(this))));
if (!image_processor_) { if (!image_processor_) {
VLOGF(1) << "Failed initializing image processor"; VLOGF(1) << "Failed initializing image processor";
return false; return false;
...@@ -421,9 +417,8 @@ void V4L2VideoEncodeAccelerator::Encode(scoped_refptr<VideoFrame> frame, ...@@ -421,9 +417,8 @@ void V4L2VideoEncodeAccelerator::Encode(scoped_refptr<VideoFrame> frame,
DCHECK(child_task_runner_->BelongsToCurrentThread()); DCHECK(child_task_runner_->BelongsToCurrentThread());
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::EncodeTask,
base::BindOnce(&V4L2VideoEncodeAccelerator::EncodeTask, weak_this_, std::move(frame), force_keyframe));
base::Unretained(this), std::move(frame), force_keyframe));
} }
void V4L2VideoEncodeAccelerator::UseOutputBitstreamBuffer( void V4L2VideoEncodeAccelerator::UseOutputBitstreamBuffer(
...@@ -434,7 +429,7 @@ void V4L2VideoEncodeAccelerator::UseOutputBitstreamBuffer( ...@@ -434,7 +429,7 @@ void V4L2VideoEncodeAccelerator::UseOutputBitstreamBuffer(
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask, base::BindOnce(&V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask,
base::Unretained(this), std::move(buffer))); weak_this_, std::move(buffer)));
} }
void V4L2VideoEncodeAccelerator::RequestEncodingParametersChange( void V4L2VideoEncodeAccelerator::RequestEncodingParametersChange(
...@@ -447,7 +442,7 @@ void V4L2VideoEncodeAccelerator::RequestEncodingParametersChange( ...@@ -447,7 +442,7 @@ void V4L2VideoEncodeAccelerator::RequestEncodingParametersChange(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
&V4L2VideoEncodeAccelerator::RequestEncodingParametersChangeTask, &V4L2VideoEncodeAccelerator::RequestEncodingParametersChangeTask,
base::Unretained(this), bitrate, framerate)); weak_this_, bitrate, framerate));
} }
void V4L2VideoEncodeAccelerator::Destroy() { void V4L2VideoEncodeAccelerator::Destroy() {
...@@ -456,13 +451,12 @@ void V4L2VideoEncodeAccelerator::Destroy() { ...@@ -456,13 +451,12 @@ void V4L2VideoEncodeAccelerator::Destroy() {
// We're destroying; cancel all callbacks. // We're destroying; cancel all callbacks.
client_ptr_factory_.reset(); client_ptr_factory_.reset();
weak_this_ptr_factory_.InvalidateWeakPtrs();
// If the encoder thread is running, destroy using posted task. // If the encoder thread is running, destroy using posted task.
if (encoder_thread_.IsRunning()) { if (encoder_thread_.IsRunning()) {
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::DestroyTask, FROM_HERE,
base::Unretained(this))); base::BindOnce(&V4L2VideoEncodeAccelerator::DestroyTask, weak_this_));
// DestroyTask() will put the encoder into kError state and cause all tasks // DestroyTask() will put the encoder into kError state and cause all tasks
// to no-op. // to no-op.
encoder_thread_.Stop(); encoder_thread_.Stop();
...@@ -486,9 +480,8 @@ void V4L2VideoEncodeAccelerator::Flush(FlushCallback flush_callback) { ...@@ -486,9 +480,8 @@ void V4L2VideoEncodeAccelerator::Flush(FlushCallback flush_callback) {
DCHECK(child_task_runner_->BelongsToCurrentThread()); DCHECK(child_task_runner_->BelongsToCurrentThread());
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::FlushTask,
base::BindOnce(&V4L2VideoEncodeAccelerator::FlushTask, weak_this_, std::move(flush_callback)));
base::Unretained(this), std::move(flush_callback)));
} }
void V4L2VideoEncodeAccelerator::FlushTask(FlushCallback flush_callback) { void V4L2VideoEncodeAccelerator::FlushTask(FlushCallback flush_callback) {
...@@ -533,8 +526,8 @@ void V4L2VideoEncodeAccelerator::FrameProcessed( ...@@ -533,8 +526,8 @@ void V4L2VideoEncodeAccelerator::FrameProcessed(
encoder_input_queue_.emplace(std::move(frame), force_keyframe, encoder_input_queue_.emplace(std::move(frame), force_keyframe,
output_buffer_index); output_buffer_index);
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::Enqueue, FROM_HERE,
base::Unretained(this))); base::BindOnce(&V4L2VideoEncodeAccelerator::Enqueue, weak_this_));
} }
void V4L2VideoEncodeAccelerator::ReuseImageProcessorOutputBuffer( void V4L2VideoEncodeAccelerator::ReuseImageProcessorOutputBuffer(
...@@ -723,6 +716,7 @@ bool V4L2VideoEncodeAccelerator::ReconfigureFormatIfNeeded( ...@@ -723,6 +716,7 @@ bool V4L2VideoEncodeAccelerator::ReconfigureFormatIfNeeded(
} }
void V4L2VideoEncodeAccelerator::InputImageProcessorTask() { void V4L2VideoEncodeAccelerator::InputImageProcessorTask() {
DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
if (free_image_processor_output_buffer_indices_.empty()) if (free_image_processor_output_buffer_indices_.empty())
return; return;
if (image_processor_input_queue_.empty()) if (image_processor_input_queue_.empty())
...@@ -741,22 +735,18 @@ void V4L2VideoEncodeAccelerator::InputImageProcessorTask() { ...@@ -741,22 +735,18 @@ void V4L2VideoEncodeAccelerator::InputImageProcessorTask() {
auto output_frame = VideoFrame::WrapVideoFrame( auto output_frame = VideoFrame::WrapVideoFrame(
buf, buf->format(), buf->visible_rect(), buf->natural_size()); buf, buf->format(), buf->visible_rect(), buf->natural_size());
// Unretained(this) is safe here, because image_processor is destroyed
// before video_encoder_thread stops.
if (!image_processor_->Process( if (!image_processor_->Process(
std::move(frame), std::move(output_frame), std::move(frame), std::move(output_frame),
BindToCurrentLoop( BindToCurrentLoop(base::BindOnce(
base::BindOnce(&V4L2VideoEncodeAccelerator::FrameProcessed, &V4L2VideoEncodeAccelerator::FrameProcessed, weak_this_,
base::Unretained(this), force_keyframe, force_keyframe, timestamp, output_buffer_index)))) {
timestamp, output_buffer_index)))) {
NOTIFY_ERROR(kPlatformFailureError); NOTIFY_ERROR(kPlatformFailureError);
} }
} else { } else {
if (!image_processor_->Process( if (!image_processor_->Process(
std::move(frame), std::move(frame), BindToCurrentLoop(base::BindOnce(
BindToCurrentLoop(base::BindOnce( &V4L2VideoEncodeAccelerator::FrameProcessed,
&V4L2VideoEncodeAccelerator::FrameProcessed, weak_this_, force_keyframe, timestamp)))) {
base::Unretained(this), force_keyframe, timestamp)))) {
NOTIFY_ERROR(kPlatformFailureError); NOTIFY_ERROR(kPlatformFailureError);
} }
} }
...@@ -792,6 +782,10 @@ void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask( ...@@ -792,6 +782,10 @@ void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask(
void V4L2VideoEncodeAccelerator::DestroyTask() { void V4L2VideoEncodeAccelerator::DestroyTask() {
VLOGF(2); VLOGF(2);
// Make tasks posted to encoder_thread_.task_runner() after DestroyTask() not
// be executed.
weak_this_ptr_factory_.InvalidateWeakPtrs();
// DestroyTask() should run regardless of encoder_state_. // DestroyTask() should run regardless of encoder_state_.
// Stop streaming and the device_poll_thread_. // Stop streaming and the device_poll_thread_.
...@@ -841,6 +835,8 @@ void V4L2VideoEncodeAccelerator::ServiceDeviceTask() { ...@@ -841,6 +835,8 @@ void V4L2VideoEncodeAccelerator::ServiceDeviceTask() {
// already. // already.
DCHECK(device_poll_thread_.task_runner()); DCHECK(device_poll_thread_.task_runner());
// Queue the DevicePollTask() now. // Queue the DevicePollTask() now.
// base::Unretained(this) is safe, because device_poll_thread_ is owned by
// *this and stops before *this destruction.
device_poll_thread_.task_runner()->PostTask( device_poll_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::DevicePollTask, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::DevicePollTask,
base::Unretained(this), poll_device)); base::Unretained(this), poll_device));
...@@ -857,11 +853,10 @@ void V4L2VideoEncodeAccelerator::ServiceDeviceTask() { ...@@ -857,11 +853,10 @@ void V4L2VideoEncodeAccelerator::ServiceDeviceTask() {
void V4L2VideoEncodeAccelerator::Enqueue() { void V4L2VideoEncodeAccelerator::Enqueue() {
DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread()); DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
DCHECK(input_queue_ && output_queue_);
TRACE_EVENT0("media,gpu", "V4L2VEA::Enqueue"); TRACE_EVENT0("media,gpu", "V4L2VEA::Enqueue");
DVLOGF(4) << "free_input_buffers: " << input_queue_->FreeBuffersCount() DVLOGF(4) << "free_input_buffers: " << input_queue_->FreeBuffersCount()
<< "input_queue: " << encoder_input_queue_.size(); << "input_queue: " << encoder_input_queue_.size();
bool do_streamon = false; bool do_streamon = false;
// Enqueue all the inputs we can. // Enqueue all the inputs we can.
const size_t old_inputs_queued = input_queue_->QueuedBuffersCount(); const size_t old_inputs_queued = input_queue_->QueuedBuffersCount();
...@@ -1048,8 +1043,8 @@ void V4L2VideoEncodeAccelerator::PumpBitstreamBuffers() { ...@@ -1048,8 +1043,8 @@ void V4L2VideoEncodeAccelerator::PumpBitstreamBuffers() {
// We may free some V4L2 output buffers above. Enqueue them if needed. // We may free some V4L2 output buffers above. Enqueue them if needed.
if (output_queue_->FreeBuffersCount() > 0) { if (output_queue_->FreeBuffersCount() > 0) {
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::Enqueue, FROM_HERE,
base::Unretained(this))); base::BindOnce(&V4L2VideoEncodeAccelerator::Enqueue, weak_this_));
} }
} }
...@@ -1206,6 +1201,8 @@ bool V4L2VideoEncodeAccelerator::StartDevicePoll() { ...@@ -1206,6 +1201,8 @@ bool V4L2VideoEncodeAccelerator::StartDevicePoll() {
} }
// Enqueue a poll task with no devices to poll on -- it will wait only on the // Enqueue a poll task with no devices to poll on -- it will wait only on the
// interrupt fd. // interrupt fd.
// base::Unretained(this) is safe, because device_poll_thread_ is owned by
// *this and stops before *this destruction.
device_poll_thread_.task_runner()->PostTask( device_poll_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::DevicePollTask, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::DevicePollTask,
base::Unretained(this), false)); base::Unretained(this), false));
...@@ -1261,23 +1258,25 @@ void V4L2VideoEncodeAccelerator::DevicePollTask(bool poll_device) { ...@@ -1261,23 +1258,25 @@ void V4L2VideoEncodeAccelerator::DevicePollTask(bool poll_device) {
// touch encoder state from this thread. // touch encoder state from this thread.
encoder_thread_.task_runner()->PostTask( encoder_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::ServiceDeviceTask, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::ServiceDeviceTask,
base::Unretained(this))); weak_this_));
} }
void V4L2VideoEncodeAccelerator::NotifyError(Error error) { void V4L2VideoEncodeAccelerator::NotifyError(Error error) {
// Note that NotifyError() must be called from SetErrorState() only, so that
// NotifyError() will not be called twice.
VLOGF(1) << "error=" << error; VLOGF(1) << "error=" << error;
DCHECK(child_task_runner_);
if (!child_task_runner_->BelongsToCurrentThread()) { if (child_task_runner_->BelongsToCurrentThread()) {
child_task_runner_->PostTask( if (client_) {
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::NotifyError, client_->NotifyError(error);
weak_this_, error)); client_ptr_factory_.reset();
}
return; return;
} }
if (client_) { // Called on encoder_thread_.task_runner().
client_->NotifyError(error); child_task_runner_->PostTask(
client_ptr_factory_.reset(); FROM_HERE, base::BindOnce(&Client::NotifyError, client_, error));
}
} }
void V4L2VideoEncodeAccelerator::SetErrorState(Error error) { void V4L2VideoEncodeAccelerator::SetErrorState(Error error) {
...@@ -1288,7 +1287,7 @@ void V4L2VideoEncodeAccelerator::SetErrorState(Error error) { ...@@ -1288,7 +1287,7 @@ void V4L2VideoEncodeAccelerator::SetErrorState(Error error) {
if (task_runner && !task_runner->BelongsToCurrentThread()) { if (task_runner && !task_runner->BelongsToCurrentThread()) {
task_runner->PostTask( task_runner->PostTask(
FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::SetErrorState, FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::SetErrorState,
base::Unretained(this), error)); weak_this_, error));
return; return;
} }
......
...@@ -347,10 +347,9 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator ...@@ -347,10 +347,9 @@ class MEDIA_GPU_EXPORT V4L2VideoEncodeAccelerator
base::WeakPtr<Client> client_; base::WeakPtr<Client> client_;
std::unique_ptr<base::WeakPtrFactory<Client>> client_ptr_factory_; std::unique_ptr<base::WeakPtrFactory<Client>> client_ptr_factory_;
// WeakPtr<> pointing to |this| for use in posting tasks from the // WeakPtr<> pointing to |this| for use in posting tasks to
// image_processor_ back to the child thread. // |encoder_thread_.task_runner()|. It guarantees no task will be executed
// Tasks posted onto encoder and poll threads can use base::Unretained(this), // after DestroyTask().
// as both threads will not outlive this object.
base::WeakPtr<V4L2VideoEncodeAccelerator> weak_this_; base::WeakPtr<V4L2VideoEncodeAccelerator> weak_this_;
base::WeakPtrFactory<V4L2VideoEncodeAccelerator> weak_this_ptr_factory_; base::WeakPtrFactory<V4L2VideoEncodeAccelerator> weak_this_ptr_factory_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment