Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1044)

Unified Diff: media/gpu/android_video_decode_accelerator.cc

Issue 2245333004: Convert AVDAs thread hang detection to be timer based (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: clarified comment Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/gpu/android_video_decode_accelerator.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/gpu/android_video_decode_accelerator.cc
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc
index 41d3f550768b03219f98c57608a678c0e74ca8ad..44b4471cc69c69eccda3b13ef2553f56e51a12ed 100644
--- a/media/gpu/android_video_decode_accelerator.cc
+++ b/media/gpu/android_video_decode_accelerator.cc
@@ -16,6 +16,7 @@
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_checker.h"
@@ -105,9 +106,13 @@ constexpr base::TimeDelta IdleTimerTimeOut = base::TimeDelta::FromSeconds(1);
// from breaking the pipeline, if we're about to be reset anyway.
constexpr base::TimeDelta ErrorPostingDelay = base::TimeDelta::FromSeconds(2);
-// Maximum number of concurrent, incomplete codec creations that we'll allow
-// before turning off autodection of codec type.
-enum { kMaxConcurrentCodecAutodetections = 4 };
+// Give tasks on the construction thread 800ms before considering them hung.
+// MediaCodec.configure() calls typically take 100-200ms on a N5, so 800ms is
+// expected to very rarely result in false positives. Also, false positives have
+// low impact because we resume using the thread if its apparently hung task
+// completes.
+constexpr base::TimeDelta kHungTaskDetectionTimeout =
+ base::TimeDelta::FromMilliseconds(800);
// For RecordFormatChangedMetric.
enum FormatChangedValue {
@@ -176,28 +181,65 @@ class AndroidVideoDecodeAccelerator::OnFrameAvailableHandler
// Its responsibilities include:
// - Starting and stopping a shared "construction" thread for instantiating and
// releasing MediaCodecs.
-// - Tracking the number of outstanding tasks running on the construction
-// thread. (For detecting when one of those tasks has hung indefinitely.)
+// - Detecting when a task has hung on the construction thread so AVDAs can
+// stop using it.
// - Running a RepeatingTimer so that AVDAs can get a regular callback to
// DoIOTask().
// - Tracking the allocation of surfaces to AVDAs and delivering callbacks when
// surfaces are released.
class AVDAManager {
public:
- // Make sure that the construction thread is started for |avda|.
+ class HangDetector : public base::MessageLoop::TaskObserver {
+ public:
+ HangDetector() {}
+
+ void WillProcessTask(const base::PendingTask& pending_task) override {
+ base::AutoLock l(lock_);
+ task_start_time_ = base::TimeTicks::Now();
+ }
+
+ void DidProcessTask(const base::PendingTask& pending_task) override {
+ base::AutoLock l(lock_);
+ task_start_time_ = base::TimeTicks();
+ }
+
+ bool IsThreadLikelyHung() {
+ base::AutoLock l(lock_);
+ if (task_start_time_.is_null())
+ return false;
+
+ return (base::TimeTicks::Now() - task_start_time_) >
+ kHungTaskDetectionTimeout;
+ }
+
+ private:
+ base::Lock lock_;
+ // Non-null when a task is currently running.
+ base::TimeTicks task_start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(HangDetector);
+ };
+
+ // Make sure the construction thread is started for |avda|.
bool StartThread(AndroidVideoDecodeAccelerator* avda) {
DCHECK(thread_checker_.CalledOnValidThread());
- // If we chose not to shut it down due to pending codec constructions, then
- // the thread might already be started even if there are no avda instances.
- // Plus, sometimes we just fail to start the thread.
if (!construction_thread_.IsRunning()) {
if (!construction_thread_.Start()) {
LOG(ERROR) << "Failed to start construction thread.";
return false;
}
+ // Register |hang_detector_| to observe the thread's MessageLoop.
+ construction_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&base::MessageLoop::AddTaskObserver,
+ base::Unretained(construction_thread_.message_loop()),
+ &hang_detector_));
}
+ // Cancel any pending StopThreadTask()s because we need the thread now.
+ weak_this_factory_.InvalidateWeakPtrs();
+
thread_avda_instances_.insert(avda);
UMA_HISTOGRAM_ENUMERATION("Media.AVDA.NumAVDAInstances",
thread_avda_instances_.size(),
@@ -205,23 +247,25 @@ class AVDAManager {
return true;
}
- // |avda| will no longer need the construction thread. Stop the thread if
- // this is the last instance.
void StopThread(AndroidVideoDecodeAccelerator* avda) {
DCHECK(thread_checker_.CalledOnValidThread());
thread_avda_instances_.erase(avda);
- if (!thread_avda_instances_.empty())
- return;
-
- // Don't stop the thread if there are outstanding requests, since they
- // might be hung. They also might simply be incomplete, and the thread
- // will stay running until we try to shut it down again.
- base::AutoLock auto_lock(autodetection_info_.lock_);
- if (autodetection_info_.outstanding_)
- return;
-
- construction_thread_.Stop();
+ // Post a task to stop the thread through the thread's task runner and back
+ // to this thread. This ensures that all pending tasks are run first. If the
+ // thread is hung we don't post a task to avoid leaking an unbounded number
+ // of tasks on its queue. If the thread is not hung, but appears to be, it
+ // will stay alive until next time an AVDA tries to stop it. We're
+ // guaranteed to not run StopThreadTask() when the thread is hung because if
+ // an AVDA queues tasks after DoNothing(), the StopThreadTask() reply will
+ // be canceled by invalidating its weak pointer.
+ if (thread_avda_instances_.empty() &&
+ !hang_detector_.IsThreadLikelyHung()) {
+ construction_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE, base::Bind(&base::DoNothing),
+ base::Bind(&AVDAManager::StopThreadTask,
+ weak_this_factory_.GetWeakPtr()));
+ }
}
// Request periodic callback of |avda|->DoIOTask(). Does nothing if the
@@ -264,33 +308,10 @@ class AVDAManager {
return construction_thread_.task_runner();
}
- // Called on the main thread when the construction thread will be doing work
- // that can potentially hang (e.g., autodetection). There may be several
- // calls to this before any call to DoneUsingConstructionThread.
- // Note that this should only be called from the main thread, else it's a race
- // with IsCodecAutodetectionProbablySafe.
- void StartUsingConstructionThread() {
+ // Returns a hint about whether the construction thread has hung.
+ bool IsConstructionThreadLikelyHung() {
DCHECK(thread_checker_.CalledOnValidThread());
- base::AutoLock auto_lock(autodetection_info_.lock_);
- ++autodetection_info_.outstanding_;
- }
-
- // Called on any thread after the potentially dangerous construction thread
- // work completes safely. May be called on any thread, including the
- // construction thread.
- // This assumes that requests are ordered, so please don't mix sync and async
- // codec construction here.
- void DoneUsingConstructionThread() {
- base::AutoLock auto_lock_l(autodetection_info_.lock_);
- DCHECK_GT(autodetection_info_.outstanding_, 0);
- --autodetection_info_.outstanding_;
- }
-
- // Return a hint about whether autodetecting the codec type is safe or not.
- bool IsCodecAutodetectionProbablySafe() {
- base::AutoLock auto_lock_l(autodetection_info_.lock_);
-
- return autodetection_info_.outstanding_ < kMaxConcurrentCodecAutodetections;
+ return hang_detector_.IsThreadLikelyHung();
}
// |avda| would like to use |surface_id|. If it is not busy, then mark it
@@ -354,7 +375,8 @@ class AVDAManager {
private:
friend struct base::DefaultLazyInstanceTraits<AVDAManager>;
- AVDAManager() : construction_thread_("AVDAThread") {}
+ AVDAManager()
+ : construction_thread_("AVDAThread"), weak_this_factory_(this) {}
~AVDAManager() { NOTREACHED(); }
void RunTimer() {
@@ -376,12 +398,14 @@ class AVDAManager {
// takes too long for the combined timer.
}
+ void StopThreadTask() { construction_thread_.Stop(); }
+
+ // All registered AVDA instances.
+ std::set<AndroidVideoDecodeAccelerator*> thread_avda_instances_;
+
// All AVDA instances that would like us to poll DoIOTask.
std::set<AndroidVideoDecodeAccelerator*> timer_avda_instances_;
- // All AVDA instances that might like to use the construction thread.
- std::set<AndroidVideoDecodeAccelerator*> thread_avda_instances_;
-
struct OwnerRecord {
AndroidVideoDecodeAccelerator* owner = nullptr;
AndroidVideoDecodeAccelerator* waiter = nullptr;
@@ -398,19 +422,18 @@ class AVDAManager {
// Repeating timer responsible for draining pending IO to the codecs.
base::RepeatingTimer io_timer_;
- // Data for determining if codec creation is hanging.
- struct {
- // Lock that protects other members of this struct.
- base::Lock lock_;
-
- // Number of currently pending work items of the construction thread.
- int outstanding_ = 0;
- } autodetection_info_;
-
+ // A thread for posting MediaCodec construction and releases to. It's created
+ // lazily when requested.
base::Thread construction_thread_;
+ // For detecting when a task has hung on |construction_thread_|.
+ HangDetector hang_detector_;
+
base::ThreadChecker thread_checker_;
+ // For canceling pending StopThreadTask()s.
+ base::WeakPtrFactory<AVDAManager> weak_this_factory_;
+
DISALLOW_COPY_AND_ASSIGN(AVDAManager);
};
@@ -586,12 +609,8 @@ bool AndroidVideoDecodeAccelerator::InitializeStrategy() {
new OnFrameAvailableHandler(this, surface_texture);
}
- // Start the thread for async configuration, even if we don't need it now.
- // ResetCodecState might rebuild the codec later, for example.
- if (!g_avda_manager.Get().StartThread(this)) {
- LOG(ERROR) << "Failed to start AVDA thread";
+ if (!g_avda_manager.Get().StartThread(this))
return false;
- }
// If we are encrypted, then we aren't able to create the codec yet.
if (config_.is_encrypted) {
@@ -1100,7 +1119,7 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
// releasing any outgoing codec, so that |codec_config_| still matches the
// outgoing codec for ReleaseMediaCodec().
codec_config_->allow_autodetection_ =
- g_avda_manager.Get().IsCodecAutodetectionProbablySafe();
+ !g_avda_manager.Get().IsConstructionThreadLikelyHung();
// If autodetection is disallowed, fall back to Chrome's software decoders
// instead of using the software decoders provided by MediaCodec.
@@ -1110,12 +1129,7 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
return;
}
- codec_config_->notify_completion_ = codec_config_->allow_autodetection_;
- if (codec_config_->allow_autodetection_)
- g_avda_manager.Get().StartUsingConstructionThread();
-
- // If we're not trying autodetection, then use the main thread. The original
- // might be blocked.
+ // If we're not trying autodetection, then use the main thread.
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
codec_config_->allow_autodetection_
? g_avda_manager.Get().ConstructionTaskRunner()
@@ -1133,13 +1147,8 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() {
state_ = WAITING_FOR_CODEC;
- // Decide whether to allow autodetection or not. Since we're on the main
- // thread, and this request is unordered with respect to pending async config
- // attempts, don't record it. It may break book-keeping, and there's not
- // much we can do anyway.
codec_config_->allow_autodetection_ =
- g_avda_manager.Get().IsCodecAutodetectionProbablySafe();
- codec_config_->notify_completion_ = false;
+ !g_avda_manager.Get().IsConstructionThreadLikelyHung();
ReleaseMediaCodec();
std::unique_ptr<VideoCodecBridge> media_codec =
@@ -1168,11 +1177,6 @@ AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread(
codec_config->surface_.j_surface().obj(), media_crypto, true,
require_software_codec));
- // If we successfully completed after an autodetect, then let the other
- // instances know that we didn't get stuck.
- if (codec_config->notify_completion_)
- g_avda_manager.Get().DoneUsingConstructionThread();
-
return codec;
}
@@ -1683,13 +1687,8 @@ void AndroidVideoDecodeAccelerator::ReleaseMediaCodec() {
if (!codec_config_->allow_autodetection_) {
media_codec_.reset();
} else {
- g_avda_manager.Get().StartUsingConstructionThread();
- scoped_refptr<base::SingleThreadTaskRunner> task_runner =
- g_avda_manager.Get().ConstructionTaskRunner();
- task_runner->DeleteSoon(FROM_HERE, media_codec_.release());
- task_runner->PostTask(
- FROM_HERE, base::Bind(&AVDAManager::DoneUsingConstructionThread,
- base::Unretained(g_avda_manager.Pointer())));
+ g_avda_manager.Get().ConstructionTaskRunner()->DeleteSoon(
+ FROM_HERE, media_codec_.release());
}
}
« no previous file with comments | « media/gpu/android_video_decode_accelerator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698