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

Side by Side Diff: third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp

Issue 2913303002: Avoid unsafe heap access from audio thread. (Closed)
Patch Set: improve method documentation Created 3 years, 6 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 unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2010, Google Inc. All rights reserved. 2 * Copyright (C) 2010, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 88
89 // FIXME(dominicc): Devolve these constructors to AudioContext 89 // FIXME(dominicc): Devolve these constructors to AudioContext
90 // and OfflineAudioContext respectively. 90 // and OfflineAudioContext respectively.
91 91
92 // Constructor for rendering to the audio hardware. 92 // Constructor for rendering to the audio hardware.
93 BaseAudioContext::BaseAudioContext(Document* document) 93 BaseAudioContext::BaseAudioContext(Document* document)
94 : SuspendableObject(document), 94 : SuspendableObject(document),
95 destination_node_(nullptr), 95 destination_node_(nullptr),
96 is_cleared_(false), 96 is_cleared_(false),
97 is_resolving_resume_promises_(false), 97 is_resolving_resume_promises_(false),
98 has_posted_cleanup_task_(false),
98 user_gesture_required_(false), 99 user_gesture_required_(false),
99 connection_count_(0), 100 connection_count_(0),
100 deferred_task_handler_(DeferredTaskHandler::Create()), 101 deferred_task_handler_(DeferredTaskHandler::Create()),
101 context_state_(kSuspended), 102 context_state_(kSuspended),
102 closed_context_sample_rate_(-1), 103 closed_context_sample_rate_(-1),
103 periodic_wave_sine_(nullptr), 104 periodic_wave_sine_(nullptr),
104 periodic_wave_square_(nullptr), 105 periodic_wave_square_(nullptr),
105 periodic_wave_sawtooth_(nullptr), 106 periodic_wave_sawtooth_(nullptr),
106 periodic_wave_triangle_(nullptr), 107 periodic_wave_triangle_(nullptr),
107 output_position_() { 108 output_position_() {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
142 periodic_wave_sawtooth_(nullptr), 143 periodic_wave_sawtooth_(nullptr),
143 periodic_wave_triangle_(nullptr), 144 periodic_wave_triangle_(nullptr),
144 output_position_() {} 145 output_position_() {}
145 146
146 BaseAudioContext::~BaseAudioContext() { 147 BaseAudioContext::~BaseAudioContext() {
147 GetDeferredTaskHandler().ContextWillBeDestroyed(); 148 GetDeferredTaskHandler().ContextWillBeDestroyed();
148 // AudioNodes keep a reference to their context, so there should be no way to 149 // AudioNodes keep a reference to their context, so there should be no way to
149 // be in the destructor if there are still AudioNodes around. 150 // be in the destructor if there are still AudioNodes around.
150 DCHECK(!IsDestinationInitialized()); 151 DCHECK(!IsDestinationInitialized());
151 DCHECK(!active_source_nodes_.size()); 152 DCHECK(!active_source_nodes_.size());
152 DCHECK(!finished_source_handlers_.size());
153 DCHECK(!is_resolving_resume_promises_); 153 DCHECK(!is_resolving_resume_promises_);
154 DCHECK(!resume_resolvers_.size()); 154 DCHECK(!resume_resolvers_.size());
155 DCHECK(!autoplay_status_.has_value()); 155 DCHECK(!autoplay_status_.has_value());
156 } 156 }
157 157
158 void BaseAudioContext::Initialize() { 158 void BaseAudioContext::Initialize() {
159 if (IsDestinationInitialized()) 159 if (IsDestinationInitialized())
160 return; 160 return;
161 161
162 FFTFrame::Initialize(); 162 FFTFrame::Initialize();
(...skipping 530 matching lines...) Expand 10 before | Expand all | Expand 10 after
693 WrapPersistent(this))); 693 WrapPersistent(this)));
694 } 694 }
695 695
696 void BaseAudioContext::NotifyStateChange() { 696 void BaseAudioContext::NotifyStateChange() {
697 DispatchEvent(Event::Create(EventTypeNames::statechange)); 697 DispatchEvent(Event::Create(EventTypeNames::statechange));
698 } 698 }
699 699
700 void BaseAudioContext::NotifySourceNodeFinishedProcessing( 700 void BaseAudioContext::NotifySourceNodeFinishedProcessing(
701 AudioHandler* handler) { 701 AudioHandler* handler) {
702 DCHECK(IsAudioThread()); 702 DCHECK(IsAudioThread());
703 MutexLocker lock(finished_source_handlers_mutex_);
703 finished_source_handlers_.push_back(handler); 704 finished_source_handlers_.push_back(handler);
704 } 705 }
705 706
706 void BaseAudioContext::RemoveFinishedSourceNodes(bool needs_removal) {
707 DCHECK(IsAudioThread());
708
709 if (needs_removal) {
710 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
711 BLINK_FROM_HERE,
712 CrossThreadBind(
713 &BaseAudioContext::RemoveFinishedSourceNodesOnMainThread,
714 WrapCrossThreadPersistent(this)));
715 }
716 }
717
718 void BaseAudioContext::RemoveFinishedSourceNodesOnMainThread() {
719 DCHECK(IsMainThread());
720 AutoLocker locker(this);
721 // Quadratic worst case, but sizes of both vectors are considered
722 // manageable, especially |m_finishedSourceNodes| is likely to be short.
723 for (AudioNode* node : finished_source_nodes_) {
724 size_t i = active_source_nodes_.Find(node);
725 if (i != kNotFound)
726 active_source_nodes_.erase(i);
727 }
728 finished_source_nodes_.clear();
729 }
730
731 Document* BaseAudioContext::GetDocument() const { 707 Document* BaseAudioContext::GetDocument() const {
732 return ToDocument(GetExecutionContext()); 708 return ToDocument(GetExecutionContext());
733 } 709 }
734 710
735 AutoplayPolicy::Type BaseAudioContext::GetAutoplayPolicy() const { 711 AutoplayPolicy::Type BaseAudioContext::GetAutoplayPolicy() const {
736 Document* document = GetDocument(); 712 Document* document = GetDocument();
737 DCHECK(document); 713 DCHECK(document);
738 return AutoplayPolicy::GetAutoplayPolicyForDocument(*document); 714 return AutoplayPolicy::GetAutoplayPolicyForDocument(*document);
739 } 715 }
740 716
741 bool BaseAudioContext::AreAutoplayRequirementsFulfilled() const { 717 bool BaseAudioContext::AreAutoplayRequirementsFulfilled() const {
742 switch (GetAutoplayPolicy()) { 718 switch (GetAutoplayPolicy()) {
743 case AutoplayPolicy::Type::kNoUserGestureRequired: 719 case AutoplayPolicy::Type::kNoUserGestureRequired:
744 return true; 720 return true;
745 case AutoplayPolicy::Type::kUserGestureRequired: 721 case AutoplayPolicy::Type::kUserGestureRequired:
746 case AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin: 722 case AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin:
747 return UserGestureIndicator::ProcessingUserGesture(); 723 return UserGestureIndicator::ProcessingUserGesture();
748 case AutoplayPolicy::Type::kDocumentUserActivationRequired: 724 case AutoplayPolicy::Type::kDocumentUserActivationRequired:
749 return GetDocument()->GetFrame() && 725 return GetDocument()->GetFrame() &&
750 GetDocument()->GetFrame()->HasReceivedUserGesture(); 726 GetDocument()->GetFrame()->HasReceivedUserGesture();
751 } 727 }
752 728
753 NOTREACHED(); 729 NOTREACHED();
754 return false; 730 return false;
755 } 731 }
756 732
757 bool BaseAudioContext::ReleaseFinishedSourceNodes() {
758 DCHECK(IsGraphOwner());
759 DCHECK(IsAudioThread());
760 bool did_remove = false;
761 for (AudioHandler* handler : finished_source_handlers_) {
762 for (AudioNode* node : active_source_nodes_) {
763 if (finished_source_nodes_.Contains(node))
764 continue;
765 if (handler == &node->Handler()) {
766 handler->BreakConnection();
767 finished_source_nodes_.insert(node);
768 did_remove = true;
769 break;
770 }
771 }
772 }
773 finished_source_handlers_.clear();
774 return did_remove;
775 }
776
777 void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) { 733 void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) {
778 DCHECK(IsMainThread()); 734 DCHECK(IsMainThread());
779 AutoLocker locker(this); 735 AutoLocker locker(this);
780 736
781 active_source_nodes_.push_back(node); 737 active_source_nodes_.push_back(node);
782 node->Handler().MakeConnection(); 738 node->Handler().MakeConnection();
783 } 739 }
784 740
785 void BaseAudioContext::ReleaseActiveSourceNodes() { 741 void BaseAudioContext::ReleaseActiveSourceNodes() {
786 DCHECK(IsMainThread()); 742 DCHECK(IsMainThread());
787 for (auto& source_node : active_source_nodes_) 743 for (auto& source_node : active_source_nodes_)
788 source_node->Handler().BreakConnection(); 744 source_node->Handler().BreakConnection();
789 745
790 active_source_nodes_.clear(); 746 active_source_nodes_.clear();
791 } 747 }
792 748
793 void BaseAudioContext::HandleStoppableSourceNodes() { 749 void BaseAudioContext::HandleStoppableSourceNodes() {
750 DCHECK(IsAudioThread());
794 DCHECK(IsGraphOwner()); 751 DCHECK(IsGraphOwner());
795 752
796 // Find AudioBufferSourceNodes to see if we can stop playing them. 753 if (active_source_nodes_.size())
797 for (AudioNode* node : active_source_nodes_) { 754 ScheduleMainThreadCleanup();
798 // If the AudioNode has been marked as finished and released by
799 // the audio thread, but not yet removed by the main thread
800 // (see releaseActiveSourceNodes() above), |node| must not be
801 // touched as its handler may have been released already.
802 if (finished_source_nodes_.Contains(node))
803 continue;
804 if (node->Handler().GetNodeType() ==
805 AudioHandler::kNodeTypeAudioBufferSource) {
806 AudioBufferSourceNode* source_node =
807 static_cast<AudioBufferSourceNode*>(node);
808 source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode();
809 }
810 }
811 } 755 }
812 756
813 void BaseAudioContext::HandlePreRenderTasks( 757 void BaseAudioContext::HandlePreRenderTasks(
814 const AudioIOPosition& output_position) { 758 const AudioIOPosition& output_position) {
815 DCHECK(IsAudioThread()); 759 DCHECK(IsAudioThread());
816 760
817 // At the beginning of every render quantum, try to update the internal 761 // At the beginning of every render quantum, try to update the internal
818 // rendering graph state (from main thread changes). It's OK if the tryLock() 762 // rendering graph state (from main thread changes). It's OK if the tryLock()
819 // fails, we'll just take slightly longer to pick up the changes. 763 // fails, we'll just take slightly longer to pick up the changes.
820 if (TryLock()) { 764 if (TryLock()) {
(...skipping 16 matching lines...) Expand all
837 } 781 }
838 782
839 void BaseAudioContext::HandlePostRenderTasks() { 783 void BaseAudioContext::HandlePostRenderTasks() {
840 DCHECK(IsAudioThread()); 784 DCHECK(IsAudioThread());
841 785
842 // Must use a tryLock() here too. Don't worry, the lock will very rarely be 786 // Must use a tryLock() here too. Don't worry, the lock will very rarely be
843 // contended and this method is called frequently. The worst that can happen 787 // contended and this method is called frequently. The worst that can happen
844 // is that there will be some nodes which will take slightly longer than usual 788 // is that there will be some nodes which will take slightly longer than usual
845 // to be deleted or removed from the render graph (in which case they'll 789 // to be deleted or removed from the render graph (in which case they'll
846 // render silence). 790 // render silence).
847 bool did_remove = false;
848 if (TryLock()) { 791 if (TryLock()) {
849 // Take care of AudioNode tasks where the tryLock() failed previously. 792 // Take care of AudioNode tasks where the tryLock() failed previously.
850 GetDeferredTaskHandler().BreakConnections(); 793 GetDeferredTaskHandler().BreakConnections();
851 794
852 // Dynamically clean up nodes which are no longer needed.
853 did_remove = ReleaseFinishedSourceNodes();
854
855 GetDeferredTaskHandler().HandleDeferredTasks(); 795 GetDeferredTaskHandler().HandleDeferredTasks();
856 GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread(); 796 GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread();
857 797
858 unlock(); 798 unlock();
859 } 799 }
860
861 RemoveFinishedSourceNodes(did_remove);
862 } 800 }
863 801
864 void BaseAudioContext::ResolvePromisesForResumeOnMainThread() { 802 void BaseAudioContext::PerformCleanupOnMainThread() {
865 DCHECK(IsMainThread()); 803 DCHECK(IsMainThread());
866 AutoLocker locker(this); 804 AutoLocker locker(this);
867 805
868 for (auto& resolver : resume_resolvers_) { 806 if (is_resolving_resume_promises_) {
869 if (context_state_ == kClosed) { 807 for (auto& resolver : resume_resolvers_) {
870 resolver->Reject(DOMException::Create( 808 if (context_state_ == kClosed) {
871 kInvalidStateError, "Cannot resume a context that has been closed")); 809 resolver->Reject(DOMException::Create(
872 } else { 810 kInvalidStateError,
873 SetContextState(kRunning); 811 "Cannot resume a context that has been closed"));
874 resolver->Resolve(); 812 } else {
813 SetContextState(kRunning);
814 resolver->Resolve();
815 }
875 } 816 }
817 resume_resolvers_.clear();
818 is_resolving_resume_promises_ = false;
876 } 819 }
877 820
878 resume_resolvers_.clear(); 821 if (active_source_nodes_.size()) {
879 is_resolving_resume_promises_ = false; 822 // Find AudioBufferSourceNodes to see if we can stop playing them.
823 for (AudioNode* node : active_source_nodes_) {
824 if (node->Handler().GetNodeType() ==
825 AudioHandler::kNodeTypeAudioBufferSource) {
826 AudioBufferSourceNode* source_node =
827 static_cast<AudioBufferSourceNode*>(node);
828 source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode();
829 }
830 }
831
832 Vector<AudioHandler*> finished_handlers;
833 {
834 MutexLocker lock(finished_source_handlers_mutex_);
835 finished_source_handlers_.swap(finished_handlers);
836 }
837 // Break the connection and release active nodes that have finished
838 // playing.
839 unsigned remove_count = 0;
840 Vector<bool> removables;
841 removables.resize(active_source_nodes_.size());
842 for (AudioHandler* handler : finished_handlers) {
843 for (unsigned i = 0; i < active_source_nodes_.size(); ++i) {
844 if (handler == &active_source_nodes_[i]->Handler()) {
845 handler->BreakConnection();
846 removables[i] = true;
847 remove_count++;
848 break;
849 }
850 }
851 }
852
853 // Copy over the surviving active nodes.
854 HeapVector<Member<AudioNode>> actives;
855 actives.ReserveInitialCapacity(active_source_nodes_.size() - remove_count);
856 for (unsigned i = 0; i < removables.size(); ++i) {
857 if (!removables[i])
858 actives.push_back(active_source_nodes_[i]);
859 }
860 active_source_nodes_.swap(actives);
861 }
862 has_posted_cleanup_task_ = false;
863 }
864
865 void BaseAudioContext::ScheduleMainThreadCleanup() {
866 if (has_posted_cleanup_task_)
867 return;
868 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
869 BLINK_FROM_HERE,
870 CrossThreadBind(&BaseAudioContext::PerformCleanupOnMainThread,
871 WrapCrossThreadPersistent(this)));
872 has_posted_cleanup_task_ = true;
880 } 873 }
881 874
882 void BaseAudioContext::ResolvePromisesForResume() { 875 void BaseAudioContext::ResolvePromisesForResume() {
883 // This runs inside the BaseAudioContext's lock when handling pre-render 876 // This runs inside the BaseAudioContext's lock when handling pre-render
884 // tasks. 877 // tasks.
885 DCHECK(IsAudioThread()); 878 DCHECK(IsAudioThread());
886 DCHECK(IsGraphOwner()); 879 DCHECK(IsGraphOwner());
887 880
888 // Resolve any pending promises created by resume(). Only do this if we 881 // Resolve any pending promises created by resume(). Only do this if we
889 // haven't already started resolving these promises. This gets called very 882 // haven't already started resolving these promises. This gets called very
890 // often and it takes some time to resolve the promises in the main thread. 883 // often and it takes some time to resolve the promises in the main thread.
891 if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) { 884 if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) {
892 is_resolving_resume_promises_ = true; 885 is_resolving_resume_promises_ = true;
893 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( 886 ScheduleMainThreadCleanup();
894 BLINK_FROM_HERE,
895 CrossThreadBind(&BaseAudioContext::ResolvePromisesForResumeOnMainThread,
896 WrapCrossThreadPersistent(this)));
897 } 887 }
898 } 888 }
899 889
900 void BaseAudioContext::RejectPendingDecodeAudioDataResolvers() { 890 void BaseAudioContext::RejectPendingDecodeAudioDataResolvers() {
901 // Now reject any pending decodeAudioData resolvers 891 // Now reject any pending decodeAudioData resolvers
902 for (auto& resolver : decode_audio_resolvers_) 892 for (auto& resolver : decode_audio_resolvers_)
903 resolver->Reject(DOMException::Create(kInvalidStateError, 893 resolver->Reject(DOMException::Create(kInvalidStateError,
904 "Audio context is going away")); 894 "Audio context is going away"));
905 decode_audio_resolvers_.clear(); 895 decode_audio_resolvers_.clear();
906 } 896 }
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
1022 } 1012 }
1023 1013
1024 SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const { 1014 SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const {
1025 if (GetExecutionContext()) 1015 if (GetExecutionContext())
1026 return GetExecutionContext()->GetSecurityOrigin(); 1016 return GetExecutionContext()->GetSecurityOrigin();
1027 1017
1028 return nullptr; 1018 return nullptr;
1029 } 1019 }
1030 1020
1031 } // namespace blink 1021 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698