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

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

Issue 2913303002: Avoid unsafe heap access from audio thread. (Closed)
Patch Set: bring back comment 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 644 matching lines...) Expand 10 before | Expand all | Expand 10 after
752 753
753 NOTREACHED(); 754 NOTREACHED();
754 return false; 755 return false;
755 } 756 }
756 757
757 bool BaseAudioContext::ReleaseFinishedSourceNodes() { 758 bool BaseAudioContext::ReleaseFinishedSourceNodes() {
758 DCHECK(IsGraphOwner()); 759 DCHECK(IsGraphOwner());
759 DCHECK(IsAudioThread()); 760 DCHECK(IsAudioThread());
760 bool did_remove = false; 761 bool did_remove = false;
761 for (AudioHandler* handler : finished_source_handlers_) { 762 for (AudioHandler* handler : finished_source_handlers_) {
763 // A main thread GC must not be running while the audio
764 // thread iterates over the |active_source_nodes_| heap object.
765 ThreadState::GCLockScope gc_lock(ThreadState::MainThreadState());
haraken 2017/06/08 01:07:55 Add a TODO to remove this somehow. We should refac
766
762 for (AudioNode* node : active_source_nodes_) { 767 for (AudioNode* node : active_source_nodes_) {
763 if (finished_source_nodes_.Contains(node)) 768 if (finished_source_nodes_.Contains(node))
764 continue; 769 continue;
765 if (handler == &node->Handler()) { 770 if (handler == &node->Handler()) {
766 handler->BreakConnection(); 771 handler->BreakConnection();
767 finished_source_nodes_.insert(node); 772 finished_source_nodes_.insert(node);
768 did_remove = true; 773 did_remove = true;
769 break; 774 break;
770 } 775 }
771 } 776 }
(...skipping 12 matching lines...) Expand all
784 789
785 void BaseAudioContext::ReleaseActiveSourceNodes() { 790 void BaseAudioContext::ReleaseActiveSourceNodes() {
786 DCHECK(IsMainThread()); 791 DCHECK(IsMainThread());
787 for (auto& source_node : active_source_nodes_) 792 for (auto& source_node : active_source_nodes_)
788 source_node->Handler().BreakConnection(); 793 source_node->Handler().BreakConnection();
789 794
790 active_source_nodes_.clear(); 795 active_source_nodes_.clear();
791 } 796 }
792 797
793 void BaseAudioContext::HandleStoppableSourceNodes() { 798 void BaseAudioContext::HandleStoppableSourceNodes() {
799 DCHECK(IsAudioThread());
794 DCHECK(IsGraphOwner()); 800 DCHECK(IsGraphOwner());
795 801
796 // Find AudioBufferSourceNodes to see if we can stop playing them. 802 ScheduleMainThreadCleanup();
797 for (AudioNode* node : active_source_nodes_) {
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 } 803 }
812 804
813 void BaseAudioContext::HandlePreRenderTasks( 805 void BaseAudioContext::HandlePreRenderTasks(
814 const AudioIOPosition& output_position) { 806 const AudioIOPosition& output_position) {
815 DCHECK(IsAudioThread()); 807 DCHECK(IsAudioThread());
816 808
817 // At the beginning of every render quantum, try to update the internal 809 // 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() 810 // 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. 811 // fails, we'll just take slightly longer to pick up the changes.
820 if (TryLock()) { 812 if (TryLock()) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
854 846
855 GetDeferredTaskHandler().HandleDeferredTasks(); 847 GetDeferredTaskHandler().HandleDeferredTasks();
856 GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread(); 848 GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread();
857 849
858 unlock(); 850 unlock();
859 } 851 }
860 852
861 RemoveFinishedSourceNodes(did_remove); 853 RemoveFinishedSourceNodes(did_remove);
862 } 854 }
863 855
864 void BaseAudioContext::ResolvePromisesForResumeOnMainThread() { 856 void BaseAudioContext::PerformCleanupOnMainThread() {
865 DCHECK(IsMainThread()); 857 DCHECK(IsMainThread());
866 AutoLocker locker(this); 858 AutoLocker locker(this);
867 859
868 for (auto& resolver : resume_resolvers_) { 860 if (is_resolving_resume_promises_) {
869 if (context_state_ == kClosed) { 861 for (auto& resolver : resume_resolvers_) {
870 resolver->Reject(DOMException::Create( 862 if (context_state_ == kClosed) {
871 kInvalidStateError, "Cannot resume a context that has been closed")); 863 resolver->Reject(DOMException::Create(
872 } else { 864 kInvalidStateError,
873 SetContextState(kRunning); 865 "Cannot resume a context that has been closed"));
874 resolver->Resolve(); 866 } else {
867 SetContextState(kRunning);
868 resolver->Resolve();
869 }
870 }
871 resume_resolvers_.clear();
872 is_resolving_resume_promises_ = false;
873 }
874
875 // Find AudioBufferSourceNodes to see if we can stop playing them.
876 for (AudioNode* node : active_source_nodes_) {
877 // If the AudioNode has been marked as finished and released by
878 // the audio thread, but not yet removed by the main thread
879 // (see releaseActiveSourceNodes() above), |node| must not be
880 // touched as its handler may have been released already.
881 if (finished_source_nodes_.Contains(node))
882 continue;
883 if (node->Handler().GetNodeType() ==
884 AudioHandler::kNodeTypeAudioBufferSource) {
885 AudioBufferSourceNode* source_node =
886 static_cast<AudioBufferSourceNode*>(node);
887 source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode();
875 } 888 }
876 } 889 }
890 has_posted_cleanup_task_ = false;
891 }
877 892
878 resume_resolvers_.clear(); 893 void BaseAudioContext::ScheduleMainThreadCleanup() {
879 is_resolving_resume_promises_ = false; 894 if (has_posted_cleanup_task_)
895 return;
896 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
897 BLINK_FROM_HERE,
898 CrossThreadBind(&BaseAudioContext::PerformCleanupOnMainThread,
899 WrapCrossThreadPersistent(this)));
900 has_posted_cleanup_task_ = true;
880 } 901 }
881 902
882 void BaseAudioContext::ResolvePromisesForResume() { 903 void BaseAudioContext::ResolvePromisesForResume() {
883 // This runs inside the BaseAudioContext's lock when handling pre-render 904 // This runs inside the BaseAudioContext's lock when handling pre-render
884 // tasks. 905 // tasks.
885 DCHECK(IsAudioThread()); 906 DCHECK(IsAudioThread());
886 DCHECK(IsGraphOwner()); 907 DCHECK(IsGraphOwner());
887 908
888 // Resolve any pending promises created by resume(). Only do this if we 909 // Resolve any pending promises created by resume(). Only do this if we
889 // haven't already started resolving these promises. This gets called very 910 // 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. 911 // often and it takes some time to resolve the promises in the main thread.
891 if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) { 912 if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) {
892 is_resolving_resume_promises_ = true; 913 is_resolving_resume_promises_ = true;
893 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( 914 ScheduleMainThreadCleanup();
894 BLINK_FROM_HERE,
895 CrossThreadBind(&BaseAudioContext::ResolvePromisesForResumeOnMainThread,
896 WrapCrossThreadPersistent(this)));
897 } 915 }
898 } 916 }
899 917
900 void BaseAudioContext::RejectPendingDecodeAudioDataResolvers() { 918 void BaseAudioContext::RejectPendingDecodeAudioDataResolvers() {
901 // Now reject any pending decodeAudioData resolvers 919 // Now reject any pending decodeAudioData resolvers
902 for (auto& resolver : decode_audio_resolvers_) 920 for (auto& resolver : decode_audio_resolvers_)
903 resolver->Reject(DOMException::Create(kInvalidStateError, 921 resolver->Reject(DOMException::Create(kInvalidStateError,
904 "Audio context is going away")); 922 "Audio context is going away"));
905 decode_audio_resolvers_.clear(); 923 decode_audio_resolvers_.clear();
906 } 924 }
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
1022 } 1040 }
1023 1041
1024 SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const { 1042 SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const {
1025 if (GetExecutionContext()) 1043 if (GetExecutionContext())
1026 return GetExecutionContext()->GetSecurityOrigin(); 1044 return GetExecutionContext()->GetSecurityOrigin();
1027 1045
1028 return nullptr; 1046 return nullptr;
1029 } 1047 }
1030 1048
1031 } // namespace blink 1049 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698