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

Side by Side Diff: Source/modules/webaudio/AudioContext.cpp

Issue 1123603002: Process suspend() and resume() in call order (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Update according to review. Created 5 years, 7 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 | Annotate | Revision Log
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 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 #include <stdio.h> 71 #include <stdio.h>
72 #endif 72 #endif
73 73
74 namespace blink { 74 namespace blink {
75 75
76 // Don't allow more than this number of simultaneous AudioContexts talking to ha rdware. 76 // Don't allow more than this number of simultaneous AudioContexts talking to ha rdware.
77 const unsigned MaxHardwareContexts = 6; 77 const unsigned MaxHardwareContexts = 6;
78 unsigned AudioContext::s_hardwareContextCount = 0; 78 unsigned AudioContext::s_hardwareContextCount = 0;
79 unsigned AudioContext::s_contextId = 0; 79 unsigned AudioContext::s_contextId = 0;
80 80
81 AudioContext::SuspendResumeResolver::SuspendResumeResolver(
82 ResolverType resolverType,
83 PassRefPtrWillBeRawPtr<ScriptPromiseResolver> resolver)
84 : m_resolverType(resolverType)
85 , m_resolver(resolver)
86 {
87 }
88
89 AudioContext::SuspendResumeResolver* AudioContext::SuspendResumeResolver::create SuspendResolver(
90 PassRefPtrWillBeRawPtr<ScriptPromiseResolver> resolver)
91 {
92 return new SuspendResumeResolver(ResolverTypeSuspend, resolver);
93 }
94
95 AudioContext::SuspendResumeResolver* AudioContext::SuspendResumeResolver::create ResumeResolver(
96 PassRefPtrWillBeRawPtr<ScriptPromiseResolver> resolver)
97 {
98 return new SuspendResumeResolver(ResolverTypeResume, resolver);
99 }
100
81 AudioContext* AudioContext::create(Document& document, ExceptionState& exception State) 101 AudioContext* AudioContext::create(Document& document, ExceptionState& exception State)
82 { 102 {
83 ASSERT(isMainThread()); 103 ASSERT(isMainThread());
84 if (s_hardwareContextCount >= MaxHardwareContexts) { 104 if (s_hardwareContextCount >= MaxHardwareContexts) {
85 exceptionState.throwDOMException( 105 exceptionState.throwDOMException(
86 NotSupportedError, 106 NotSupportedError,
87 ExceptionMessages::indexExceedsMaximumBound( 107 ExceptionMessages::indexExceedsMaximumBound(
88 "number of hardware contexts", 108 "number of hardware contexts",
89 s_hardwareContextCount, 109 s_hardwareContextCount,
90 MaxHardwareContexts)); 110 MaxHardwareContexts));
91 return nullptr; 111 return nullptr;
92 } 112 }
93 113
94 AudioContext* audioContext = new AudioContext(&document); 114 AudioContext* audioContext = new AudioContext(&document);
95 audioContext->suspendIfNeeded(); 115 audioContext->suspendIfNeeded();
96 return audioContext; 116 return audioContext;
97 } 117 }
98 118
99 // Constructor for rendering to the audio hardware. 119 // Constructor for rendering to the audio hardware.
100 AudioContext::AudioContext(Document* document) 120 AudioContext::AudioContext(Document* document)
101 : ActiveDOMObject(document) 121 : ActiveDOMObject(document)
102 , m_isStopScheduled(false) 122 , m_isStopScheduled(false)
103 , m_isCleared(false) 123 , m_isCleared(false)
104 , m_isInitialized(false) 124 , m_isInitialized(false)
105 , m_destinationNode(nullptr) 125 , m_destinationNode(nullptr)
106 , m_isResolvingResumePromises(false) 126 , m_isResolvingSuspendResumePromises(false)
107 , m_connectionCount(0) 127 , m_connectionCount(0)
108 , m_didInitializeContextGraphMutex(false) 128 , m_didInitializeContextGraphMutex(false)
109 , m_deferredTaskHandler(DeferredTaskHandler::create()) 129 , m_deferredTaskHandler(DeferredTaskHandler::create())
110 , m_isOfflineContext(false) 130 , m_isOfflineContext(false)
111 , m_contextState(Suspended) 131 , m_contextState(Suspended)
112 , m_cachedSampleFrame(0) 132 , m_cachedSampleFrame(0)
113 { 133 {
114 m_didInitializeContextGraphMutex = true; 134 m_didInitializeContextGraphMutex = true;
115 m_destinationNode = DefaultAudioDestinationNode::create(this); 135 m_destinationNode = DefaultAudioDestinationNode::create(this);
116 136
117 initialize(); 137 initialize();
118 } 138 }
119 139
120 // Constructor for offline (non-realtime) rendering. 140 // Constructor for offline (non-realtime) rendering.
121 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate) 141 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
122 : ActiveDOMObject(document) 142 : ActiveDOMObject(document)
123 , m_isStopScheduled(false) 143 , m_isStopScheduled(false)
124 , m_isCleared(false) 144 , m_isCleared(false)
125 , m_isInitialized(false) 145 , m_isInitialized(false)
126 , m_destinationNode(nullptr) 146 , m_destinationNode(nullptr)
127 , m_isResolvingResumePromises(false) 147 , m_isResolvingSuspendResumePromises(false)
128 , m_connectionCount(0) 148 , m_connectionCount(0)
129 , m_didInitializeContextGraphMutex(false) 149 , m_didInitializeContextGraphMutex(false)
130 , m_deferredTaskHandler(DeferredTaskHandler::create()) 150 , m_deferredTaskHandler(DeferredTaskHandler::create())
131 , m_isOfflineContext(true) 151 , m_isOfflineContext(true)
132 , m_contextState(Suspended) 152 , m_contextState(Suspended)
133 , m_cachedSampleFrame(0) 153 , m_cachedSampleFrame(0)
134 { 154 {
135 m_didInitializeContextGraphMutex = true; 155 m_didInitializeContextGraphMutex = true;
136 // Create a new destination for offline rendering. 156 // Create a new destination for offline rendering.
137 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl eRate); 157 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl eRate);
138 if (m_renderTarget.get()) 158 if (m_renderTarget.get())
139 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTa rget.get()); 159 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTa rget.get());
140 160
141 initialize(); 161 initialize();
142 } 162 }
143 163
144 AudioContext::~AudioContext() 164 AudioContext::~AudioContext()
145 { 165 {
146 #if DEBUG_AUDIONODE_REFERENCES 166 #if DEBUG_AUDIONODE_REFERENCES
147 fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId ); 167 fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId );
148 #endif 168 #endif
149 deferredTaskHandler().contextWillBeDestroyed(); 169 deferredTaskHandler().contextWillBeDestroyed();
150 // AudioNodes keep a reference to their context, so there should be no way t o be in the destructor if there are still AudioNodes around. 170 // AudioNodes keep a reference to their context, so there should be no way t o be in the destructor if there are still AudioNodes around.
151 ASSERT(!m_isInitialized); 171 ASSERT(!m_isInitialized);
152 ASSERT(!m_activeSourceNodes.size()); 172 ASSERT(!m_activeSourceNodes.size());
153 ASSERT(!m_finishedSourceHandlers.size()); 173 ASSERT(!m_finishedSourceHandlers.size());
154 ASSERT(!m_suspendResolvers.size()); 174 ASSERT(!m_suspendResumeResolvers.size());
155 ASSERT(!m_isResolvingResumePromises); 175 ASSERT(!m_isResolvingSuspendResumePromises);
156 ASSERT(!m_resumeResolvers.size());
157 } 176 }
158 177
159 void AudioContext::initialize() 178 void AudioContext::initialize()
160 { 179 {
161 if (isInitialized()) 180 if (isInitialized())
162 return; 181 return;
163 182
164 FFTFrame::initialize(); 183 FFTFrame::initialize();
165 m_listener = AudioListener::create(); 184 m_listener = AudioListener::create();
166 185
(...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after
753 DOMException::create( 772 DOMException::create(
754 InvalidAccessError, 773 InvalidAccessError,
755 "cannot suspend an OfflineAudioContext")); 774 "cannot suspend an OfflineAudioContext"));
756 } 775 }
757 776
758 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState); 777 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState);
759 ScriptPromise promise = resolver->promise(); 778 ScriptPromise promise = resolver->promise();
760 779
761 // Save the resolver. If the context is running, it will get resolved at th e end of a rendering 780 // Save the resolver. If the context is running, it will get resolved at th e end of a rendering
762 // quantum. Otherwise, resolve it now. 781 // quantum. Otherwise, resolve it now.
763 m_suspendResolvers.append(resolver); 782 m_suspendResumeResolvers.append(SuspendResumeResolver::createSuspendResolver (resolver));
764 783
765 if (m_contextState != Running) { 784 if (m_contextState != Running) {
766 // Context is not running so we can't wait for a rendering quantum to re solve the 785 // Context is not running so we can't wait for a rendering quantum to re solve the
767 // promise. Just resolve it now (along with any other pending suspend pr omises). 786 // promise. Just resolve it now (along with any other pending promises).
768 resolvePromisesForSuspendOnMainThread(); 787 resolvePromisesForSuspendResumeOnMainThread();
769 } 788 }
770 789
771 return promise; 790 return promise;
772 } 791 }
773 792
774 ScriptPromise AudioContext::resumeContext(ScriptState* scriptState) 793 ScriptPromise AudioContext::resumeContext(ScriptState* scriptState)
775 { 794 {
776 ASSERT(isMainThread()); 795 ASSERT(isMainThread());
777 AutoLocker locker(this); 796 AutoLocker locker(this);
778 797
(...skipping 15 matching lines...) Expand all
794 813
795 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState); 814 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState);
796 ScriptPromise promise = resolver->promise(); 815 ScriptPromise promise = resolver->promise();
797 816
798 // Restart the destination node to pull on the audio graph. 817 // Restart the destination node to pull on the audio graph.
799 if (m_destinationNode) 818 if (m_destinationNode)
800 startRendering(); 819 startRendering();
801 820
802 // Save the resolver which will get resolved when the destination node start s pulling on the 821 // Save the resolver which will get resolved when the destination node start s pulling on the
803 // graph again. 822 // graph again.
804 m_resumeResolvers.append(resolver); 823 m_suspendResumeResolvers.append(SuspendResumeResolver::createResumeResolver( resolver));
805 824
806 return promise; 825 return promise;
807 } 826 }
808 827
809 void AudioContext::notifySourceNodeFinishedProcessing(AudioHandler* handler) 828 void AudioContext::notifySourceNodeFinishedProcessing(AudioHandler* handler)
810 { 829 {
811 ASSERT(isAudioThread()); 830 ASSERT(isAudioThread());
812 m_finishedSourceHandlers.append(handler); 831 m_finishedSourceHandlers.append(handler);
813 } 832 }
814 833
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
862 881
863 void AudioContext::handlePreRenderTasks() 882 void AudioContext::handlePreRenderTasks()
864 { 883 {
865 ASSERT(isAudioThread()); 884 ASSERT(isAudioThread());
866 885
867 // At the beginning of every render quantum, try to update the internal rend ering graph state (from main thread changes). 886 // At the beginning of every render quantum, try to update the internal rend ering graph state (from main thread changes).
868 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u p the changes. 887 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u p the changes.
869 if (tryLock()) { 888 if (tryLock()) {
870 deferredTaskHandler().handleDeferredTasks(); 889 deferredTaskHandler().handleDeferredTasks();
871 890
872 resolvePromisesForResume();
haraken 2015/05/04 23:24:40 Why don't we need to call resolvePromisesForSuspen
873 891
874 // Check to see if source nodes can be stopped because the end time has passed. 892 // Check to see if source nodes can be stopped because the end time has passed.
875 handleStoppableSourceNodes(); 893 handleStoppableSourceNodes();
876 894
877 // Update the cached sample frame value. 895 // Update the cached sample frame value.
878 m_cachedSampleFrame = currentSampleFrame(); 896 m_cachedSampleFrame = currentSampleFrame();
879 897
880 unlock(); 898 unlock();
881 } 899 }
882 } 900 }
883 901
884 void AudioContext::handlePostRenderTasks() 902 void AudioContext::handlePostRenderTasks()
885 { 903 {
886 ASSERT(isAudioThread()); 904 ASSERT(isAudioThread());
887 905
888 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. 906 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently.
889 // The worst that can happen is that there will be some nodes which will tak e slightly longer than usual to be deleted or removed 907 // The worst that can happen is that there will be some nodes which will tak e slightly longer than usual to be deleted or removed
890 // from the render graph (in which case they'll render silence). 908 // from the render graph (in which case they'll render silence).
891 if (tryLock()) { 909 if (tryLock()) {
892 // Take care of AudioNode tasks where the tryLock() failed previously. 910 // Take care of AudioNode tasks where the tryLock() failed previously.
893 deferredTaskHandler().breakConnections(); 911 deferredTaskHandler().breakConnections();
894 912
895 // Dynamically clean up nodes which are no longer needed. 913 // Dynamically clean up nodes which are no longer needed.
896 releaseFinishedSourceNodes(); 914 releaseFinishedSourceNodes();
897 915
898 deferredTaskHandler().handleDeferredTasks(); 916 deferredTaskHandler().handleDeferredTasks();
899 deferredTaskHandler().requestToDeleteHandlersOnMainThread(); 917 deferredTaskHandler().requestToDeleteHandlersOnMainThread();
900 918
901 resolvePromisesForSuspend(); 919 resolvePromisesForSuspendResume();
902 920
903 unlock(); 921 unlock();
904 } 922 }
905 } 923 }
906 924
907 void AudioContext::resolvePromisesForResumeOnMainThread() 925 void AudioContext::resolvePromisesForSuspendResumeOnMainThread()
908 { 926 {
909 ASSERT(isMainThread()); 927 ASSERT(isMainThread());
910 AutoLocker locker(this); 928 AutoLocker locker(this);
911 929
912 for (auto& resolver : m_resumeResolvers) { 930 for (auto& handler : m_suspendResumeResolvers) {
931 bool isSuspend = handler->handlerType() == SuspendResumeResolver::Resolv erTypeSuspend;
932
933 // Stop or start rendering depending on the type of handler.
934 if (m_destinationNode) {
935 if (isSuspend) {
936 stopRendering();
937 } else {
938 // Normally we don't need this, but if suspend() and resume() ca lls have piled up,
939 // the context may have been suspended again, and we want to sta rt rendering for
940 // sure. See crbug.com/483269.
941 startRendering();
942 }
943 }
944
913 if (m_contextState == Closed) { 945 if (m_contextState == Closed) {
914 resolver->reject( 946 String message = isSuspend ? "suspend" : "resume";
915 DOMException::create(InvalidStateError, "Cannot resume a context that has been closed")); 947 handler->resolver()->reject(DOMException::create(InvalidStateError,
948 "cannot " + message + " a context that has been closed"));
916 } else { 949 } else {
917 resolver->resolve(); 950 handler->resolver()->resolve();
918 } 951 }
919 } 952 }
920 953
921 m_resumeResolvers.clear(); 954 m_suspendResumeResolvers.clear();
922 m_isResolvingResumePromises = false; 955 m_isResolvingSuspendResumePromises = false;
923 } 956 }
924 957
925 void AudioContext::resolvePromisesForResume() 958 void AudioContext::resolvePromisesForSuspendResume()
926 {
927 // This runs inside the AudioContext's lock when handling pre-render tasks.
928 ASSERT(isAudioThread());
929 ASSERT(isGraphOwner());
930
931 // Resolve any pending promises created by resume(). Only do this if we have n't already started
932 // resolving these promises. This gets called very often and it takes some t ime to resolve the
933 // promises in the main thread.
haraken 2015/05/04 23:24:40 Let's keep this comment.
934 if (!m_isResolvingResumePromises && m_resumeResolvers.size() > 0) {
935 m_isResolvingResumePromises = true;
936 Platform::current()->mainThread()->postTask(FROM_HERE, threadSafeBind(&A udioContext::resolvePromisesForResumeOnMainThread, this));
937 }
938 }
939
940 void AudioContext::resolvePromisesForSuspendOnMainThread()
941 {
942 ASSERT(isMainThread());
943 AutoLocker locker(this);
944
945 // We can stop rendering now.
946 if (m_destinationNode)
947 stopRendering();
948
949 for (auto& resolver : m_suspendResolvers) {
950 if (m_contextState == Closed) {
951 resolver->reject(
952 DOMException::create(InvalidStateError, "Cannot suspend a contex t that has been closed"));
953 } else {
954 resolver->resolve();
955 }
956 }
957
958 m_suspendResolvers.clear();
959 }
960
961 void AudioContext::resolvePromisesForSuspend()
962 { 959 {
963 // This runs inside the AudioContext's lock when handling pre-render tasks. 960 // This runs inside the AudioContext's lock when handling pre-render tasks.
964 ASSERT(isAudioThread()); 961 ASSERT(isAudioThread());
965 ASSERT(isGraphOwner()); 962 ASSERT(isGraphOwner());
966 963
967 // Resolve any pending promises created by suspend() 964 // Resolve any pending promises created by suspend()
haraken 2015/05/04 23:24:40 suspend() or resume().
968 if (m_suspendResolvers.size() > 0) 965 if (!m_isResolvingSuspendResumePromises && m_suspendResumeResolvers.size() > 0) {
969 Platform::current()->mainThread()->postTask(FROM_HERE, threadSafeBind(&A udioContext::resolvePromisesForSuspendOnMainThread, this)); 966 m_isResolvingSuspendResumePromises = true;
967 Platform::current()->mainThread()->postTask(FROM_HERE,
968 threadSafeBind(&AudioContext::resolvePromisesForSuspendResumeOnMainT hread, this));
969 }
970 } 970 }
971 971
972 void AudioContext::rejectPendingResolvers() 972 void AudioContext::rejectPendingResolvers()
973 { 973 {
974 ASSERT(isMainThread()); 974 ASSERT(isMainThread());
975 975
976 // Audio context is closing down so reject any suspend or resume promises th at are still 976 // Audio context is closing down so reject any suspend or resume promises th at are still
977 // pending. 977 // pending.
978 978
979 for (auto& resolver : m_suspendResolvers) { 979 for (auto& handler : m_suspendResumeResolvers)
980 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); 980 handler->resolver()->reject(DOMException::create(InvalidStateError, "Aud io context is going away"));
981 } 981 m_suspendResumeResolvers.clear();
haraken 2015/05/04 23:24:40 Don't we need to set m_isResolvingSuspendResumePro
982 m_suspendResolvers.clear();
983
984 for (auto& resolver : m_resumeResolvers) {
985 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away"));
986 }
987 m_resumeResolvers.clear();
988 m_isResolvingResumePromises = false;
989 } 982 }
990 983
991 const AtomicString& AudioContext::interfaceName() const 984 const AtomicString& AudioContext::interfaceName() const
992 { 985 {
993 return EventTargetNames::AudioContext; 986 return EventTargetNames::AudioContext;
994 } 987 }
995 988
996 ExecutionContext* AudioContext::executionContext() const 989 ExecutionContext* AudioContext::executionContext() const
997 { 990 {
998 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext(); 991 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext();
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1055 visitor->trace(m_destinationNode); 1048 visitor->trace(m_destinationNode);
1056 visitor->trace(m_listener); 1049 visitor->trace(m_listener);
1057 // trace() can be called in AudioContext constructor, and 1050 // trace() can be called in AudioContext constructor, and
1058 // m_contextGraphMutex might be unavailable. 1051 // m_contextGraphMutex might be unavailable.
1059 if (m_didInitializeContextGraphMutex) { 1052 if (m_didInitializeContextGraphMutex) {
1060 AutoLocker lock(this); 1053 AutoLocker lock(this);
1061 visitor->trace(m_activeSourceNodes); 1054 visitor->trace(m_activeSourceNodes);
1062 } else { 1055 } else {
1063 visitor->trace(m_activeSourceNodes); 1056 visitor->trace(m_activeSourceNodes);
1064 } 1057 }
1065 visitor->trace(m_resumeResolvers);
1066 visitor->trace(m_suspendResolvers);
1067 RefCountedGarbageCollectedEventTargetWithInlineData<AudioContext>::trace(vis itor); 1058 RefCountedGarbageCollectedEventTargetWithInlineData<AudioContext>::trace(vis itor);
1068 ActiveDOMObject::trace(visitor); 1059 ActiveDOMObject::trace(visitor);
1069 } 1060 }
1070 1061
1071 SecurityOrigin* AudioContext::securityOrigin() const 1062 SecurityOrigin* AudioContext::securityOrigin() const
1072 { 1063 {
1073 if (executionContext()) 1064 if (executionContext())
1074 return executionContext()->securityOrigin(); 1065 return executionContext()->securityOrigin();
1075 1066
1076 return nullptr; 1067 return nullptr;
(...skipping 24 matching lines...) Expand all
1101 // the destination node can GCed if JS has no references. stop() will also r esolve the Promise 1092 // the destination node can GCed if JS has no references. stop() will also r esolve the Promise
1102 // created here. 1093 // created here.
1103 stop(); 1094 stop();
1104 1095
1105 return promise; 1096 return promise;
1106 } 1097 }
1107 1098
1108 } // namespace blink 1099 } // namespace blink
1109 1100
1110 #endif // ENABLE(WEB_AUDIO) 1101 #endif // ENABLE(WEB_AUDIO)
OLDNEW
« Source/modules/webaudio/AudioContext.h ('K') | « Source/modules/webaudio/AudioContext.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698