Changeset 278284 in webkit
- Timestamp:
- May 31, 2021, 5:03:03 PM (5 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 5 edited
-
ChangeLog (modified) (1 diff)
-
Modules/webaudio/PannerNode.cpp (modified) (18 diffs)
-
Modules/webaudio/PannerNode.h (modified) (2 diffs)
-
Modules/webaudio/PannerNode.idl (modified) (2 diffs)
-
platform/audio/Distance.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r278283 r278284 1 2021-05-31 Chris Dumez <cdumez@apple.com> 2 3 Fix thread safety issues in PannerNode 4 https://bugs.webkit.org/show_bug.cgi?id=226455 5 6 Reviewed by Darin Adler. 7 8 Adopt thread safety annotations in PannerNode and fix bugs found by clang. 9 In particular, the following issues were fixed: 10 - tailTime() / latencyTime() were accessing m_panner on the audio thread without locking 11 even though m_panner gets modified on the main thread. 12 - process() was accessing panningModel and m_panner before locking, on the audio thread, 13 even though those get modified on the main thread. 14 - processOnlyAudioParams() was failing to grab the process lock. 15 - requiresTailProcessing() may get called on the audio thread and was failing to grab 16 the processLock before accessing m_panner, which gets modified on the main thread. 17 18 * Modules/webaudio/PannerNode.cpp: 19 (WebCore::PannerNode::create): 20 (WebCore::PannerNode::PannerNode): 21 (WebCore::PannerNode::process): 22 (WebCore::PannerNode::processOnlyAudioParams): 23 (WebCore::PannerNode::setPanningModelForBindings): 24 (WebCore::PannerNode::setDistanceModelForBindings): 25 (WebCore::PannerNode::setRefDistanceForBindings): 26 (WebCore::PannerNode::setMaxDistanceForBindings): 27 (WebCore::PannerNode::setRolloffFactorForBindings): 28 (WebCore::PannerNode::setConeOuterGainForBindings): 29 (WebCore::PannerNode::setConeOuterAngleForBindings): 30 (WebCore::PannerNode::setConeInnerAngleForBindings): 31 (WebCore::PannerNode::requiresTailProcessing const): 32 (WebCore::PannerNode::tailTime const): 33 (WebCore::PannerNode::latencyTime const): 34 * Modules/webaudio/PannerNode.h: 35 * Modules/webaudio/PannerNode.idl: 36 * platform/audio/Distance.h: 37 1 38 2021-05-31 Adrian Perez de Castro <aperez@igalia.com> 2 39 -
trunk/Source/WebCore/Modules/webaudio/PannerNode.cpp
r277932 r278284 59 59 return result.releaseException(); 60 60 61 result = panner->setMaxDistance (options.maxDistance);62 if (result.hasException()) 63 return result.releaseException(); 64 65 result = panner->setRefDistance (options.refDistance);66 if (result.hasException()) 67 return result.releaseException(); 68 69 result = panner->setRolloffFactor (options.rolloffFactor);70 if (result.hasException()) 71 return result.releaseException(); 72 73 result = panner->setConeOuterGain (options.coneOuterGain);61 result = panner->setMaxDistanceForBindings(options.maxDistance); 62 if (result.hasException()) 63 return result.releaseException(); 64 65 result = panner->setRefDistanceForBindings(options.refDistance); 66 if (result.hasException()) 67 return result.releaseException(); 68 69 result = panner->setRolloffFactorForBindings(options.rolloffFactor); 70 if (result.hasException()) 71 return result.releaseException(); 72 73 result = panner->setConeOuterGainForBindings(options.coneOuterGain); 74 74 if (result.hasException()) 75 75 return result.releaseException(); … … 80 80 PannerNode::PannerNode(BaseAudioContext& context, const PannerOptions& options) 81 81 : AudioNode(context, NodeTypePanner) 82 // Load the HRTF database asynchronously so we don't block the Javascript thread while creating the HRTF database. 83 , m_hrtfDatabaseLoader(HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(context.sampleRate())) 82 84 , m_panningModel(options.panningModel) 85 , m_panner(Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader.ptr())) 83 86 , m_positionX(AudioParam::create(context, "positionX"_s, options.positionX, -FLT_MAX, FLT_MAX, AutomationRate::ARate)) 84 87 , m_positionY(AudioParam::create(context, "positionY"_s, options.positionY, -FLT_MAX, FLT_MAX, AutomationRate::ARate)) … … 87 90 , m_orientationY(AudioParam::create(context, "orientationY"_s, options.orientationY, -FLT_MAX, FLT_MAX, AutomationRate::ARate)) 88 91 , m_orientationZ(AudioParam::create(context, "orientationZ"_s, options.orientationZ, -FLT_MAX, FLT_MAX, AutomationRate::ARate)) 89 // Load the HRTF database asynchronously so we don't block the Javascript thread while creating the HRTF database. 90 , m_hrtfDatabaseLoader(HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(context.sampleRate())) 91 { 92 setDistanceModel(options.distanceModel); 93 setConeInnerAngle(options.coneInnerAngle); 94 setConeOuterAngle(options.coneOuterAngle); 92 { 93 setDistanceModelForBindings(options.distanceModel); 94 setConeInnerAngleForBindings(options.coneInnerAngle); 95 setConeOuterAngleForBindings(options.coneOuterAngle); 95 96 96 97 addInput(); … … 109 110 AudioBus* destination = output(0)->bus(); 110 111 111 if (!isInitialized() || !input(0)->isConnected() || !m_panner.get()) {112 if (!isInitialized() || !input(0)->isConnected()) { 112 113 destination->zero(); 113 114 return; … … 120 121 } 121 122 122 // HRTFDatabase should be loaded before proceeding for offline audio context when panningModel() is "HRTF". 123 if (panningModel() == PanningModelType::HRTF && !m_hrtfDatabaseLoader->isLoaded()) { 123 // The audio thread can't block on this lock, so we use tryLock() instead. 124 if (!m_processLock.tryLock()) { 125 // Too bad - tryLock() failed. We must be in the middle of changing the panner. 126 destination->zero(); 127 return; 128 } 129 Locker locker { AdoptLock, m_processLock }; 130 131 if (!m_panner) { 132 destination->zero(); 133 return; 134 } 135 136 // HRTFDatabase should be loaded before proceeding for offline audio context when m_panningModel is "HRTF". 137 if (m_panningModel == PanningModelType::HRTF && !m_hrtfDatabaseLoader->isLoaded()) { 124 138 if (context().isOfflineContext()) 125 139 m_hrtfDatabaseLoader->waitForLoaderThreadCompletion(); … … 130 144 } 131 145 132 // The audio thread can't block on this lock, so we use tryLock() instead.133 if (!m_processLock.tryLock()) {134 // Too bad - tryLock() failed. We must be in the middle of changing the panner.135 destination->zero();136 return;137 }138 Locker locker { AdoptLock, m_processLock };139 140 146 if ((hasSampleAccurateValues() || listener().hasSampleAccurateValues()) && (shouldUseARate() || listener().shouldUseARate())) { 141 147 processSampleAccurateValues(destination, source, framesToProcess); … … 158 164 void PannerNode::processOnlyAudioParams(size_t framesToProcess) 159 165 { 166 ASSERT(context().isAudioThread()); 167 if (!m_processLock.tryLock()) 168 return; 169 170 Locker locker { AdoptLock, m_processLock }; 160 171 float values[AudioUtilities::renderQuantumSize]; 161 172 ASSERT(framesToProcess <= AudioUtilities::renderQuantumSize); … … 246 257 } 247 258 248 void PannerNode::initialize()249 {250 if (isInitialized())251 return;252 253 m_panner = Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader.get());254 255 AudioNode::initialize();256 }257 258 void PannerNode::uninitialize()259 {260 if (!isInitialized())261 return;262 263 m_panner = nullptr;264 AudioNode::uninitialize();265 }266 267 259 AudioListener& PannerNode::listener() 268 260 { … … 270 262 } 271 263 272 void PannerNode::setPanningModel(PanningModelType model) 273 { 274 ASSERT(isMainThread()); 275 276 if (!m_panner.get() || model != m_panningModel) { 277 // This synchronizes with process(). 278 Locker locker { m_processLock }; 279 280 m_panner = Panner::create(model, sampleRate(), m_hrtfDatabaseLoader.get()); 264 void PannerNode::setPanningModelForBindings(PanningModelType model) 265 { 266 ASSERT(isMainThread()); 267 268 // This synchronizes with process(). 269 Locker locker { m_processLock }; 270 if (!m_panner || model != m_panningModel) { 271 m_panner = Panner::create(model, sampleRate(), m_hrtfDatabaseLoader.ptr()); 281 272 m_panningModel = model; 282 273 } … … 337 328 } 338 329 339 DistanceModelType PannerNode::distanceModel() const 340 { 341 return const_cast<PannerNode*>(this)->m_distanceEffect.model(); 342 } 343 344 void PannerNode::setDistanceModel(DistanceModelType model) 330 DistanceModelType PannerNode::distanceModelForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS 331 { 332 ASSERT(isMainThread()); 333 return m_distanceEffect.model(); 334 } 335 336 void PannerNode::setDistanceModelForBindings(DistanceModelType model) 345 337 { 346 338 ASSERT(isMainThread()); … … 352 344 } 353 345 354 ExceptionOr<void> PannerNode::setRefDistance (double refDistance)346 ExceptionOr<void> PannerNode::setRefDistanceForBindings(double refDistance) 355 347 { 356 348 ASSERT(isMainThread()); … … 366 358 } 367 359 368 ExceptionOr<void> PannerNode::setMaxDistance (double maxDistance)360 ExceptionOr<void> PannerNode::setMaxDistanceForBindings(double maxDistance) 369 361 { 370 362 ASSERT(isMainThread()); … … 380 372 } 381 373 382 ExceptionOr<void> PannerNode::setRolloffFactor (double rolloffFactor)374 ExceptionOr<void> PannerNode::setRolloffFactorForBindings(double rolloffFactor) 383 375 { 384 376 ASSERT(isMainThread()); … … 394 386 } 395 387 396 ExceptionOr<void> PannerNode::setConeOuterGain (double gain)388 ExceptionOr<void> PannerNode::setConeOuterGainForBindings(double gain) 397 389 { 398 390 ASSERT(isMainThread()); … … 408 400 } 409 401 410 void PannerNode::setConeOuterAngle (double angle)402 void PannerNode::setConeOuterAngleForBindings(double angle) 411 403 { 412 404 ASSERT(isMainThread()); … … 418 410 } 419 411 420 void PannerNode::setConeInnerAngle (double angle)412 void PannerNode::setConeInnerAngleForBindings(double angle) 421 413 { 422 414 ASSERT(isMainThread()); … … 516 508 bool PannerNode::requiresTailProcessing() const 517 509 { 510 if (!m_processLock.tryLock()) 511 return true; 512 Locker locker { AdoptLock, m_processLock }; 518 513 // If there's no internal panner method set up yet, assume we require tail 519 514 // processing in case the HRTF panner is set later, which does require tail … … 540 535 } 541 536 537 double PannerNode::tailTime() const 538 { 539 if (!m_processLock.tryLock()) 540 return std::numeric_limits<double>::infinity(); 541 Locker locker { AdoptLock, m_processLock }; 542 return m_panner ? m_panner->tailTime() : 0; 543 } 544 545 double PannerNode::latencyTime() const 546 { 547 if (!m_processLock.tryLock()) 548 return std::numeric_limits<double>::infinity(); 549 Locker locker { AdoptLock, m_processLock }; 550 return m_panner ? m_panner->latencyTime() : 0; 551 } 552 542 553 } // namespace WebCore 543 554 -
trunk/Source/WebCore/Modules/webaudio/PannerNode.h
r274650 r278284 62 62 void process(size_t framesToProcess) override; 63 63 void processOnlyAudioParams(size_t framesToProcess) final; 64 void initialize() override;65 void uninitialize() override;66 64 67 65 // Listener … … 69 67 70 68 // Panning model 71 PanningModelType panningModel () const {return m_panningModel; }72 void setPanningModel (PanningModelType);69 PanningModelType panningModelForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_panningModel; } 70 void setPanningModelForBindings(PanningModelType); 73 71 74 72 // Position 75 FloatPoint3D position() const;76 73 ExceptionOr<void> setPosition(float x, float y, float z); 77 AudioParam& positionX() {return m_positionX.get(); }78 AudioParam& positionY() {return m_positionY.get(); }79 AudioParam& positionZ() {return m_positionZ.get(); }74 AudioParam& positionX() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_positionX.get(); } 75 AudioParam& positionY() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_positionY.get(); } 76 AudioParam& positionZ() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_positionZ.get(); } 80 77 81 78 // Orientation 82 FloatPoint3D orientation() const;83 79 ExceptionOr<void> setOrientation(float x, float y, float z); 84 AudioParam& orientationX() {return m_orientationX.get(); }85 AudioParam& orientationY() {return m_orientationY.get(); }86 AudioParam& orientationZ() {return m_orientationZ.get(); }80 AudioParam& orientationX() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_orientationX.get(); } 81 AudioParam& orientationY() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_orientationY.get(); } 82 AudioParam& orientationZ() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_orientationZ.get(); } 87 83 88 84 // Distance parameters 89 DistanceModelType distanceModel () const;90 void setDistanceModel (DistanceModelType);85 DistanceModelType distanceModelForBindings() const; 86 void setDistanceModelForBindings(DistanceModelType); 91 87 92 double refDistance () const {return m_distanceEffect.refDistance(); }93 ExceptionOr<void> setRefDistance (double);88 double refDistanceForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_distanceEffect.refDistance(); } 89 ExceptionOr<void> setRefDistanceForBindings(double); 94 90 95 double maxDistance () const {return m_distanceEffect.maxDistance(); }96 ExceptionOr<void> setMaxDistance (double);91 double maxDistanceForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_distanceEffect.maxDistance(); } 92 ExceptionOr<void> setMaxDistanceForBindings(double); 97 93 98 double rolloffFactor () const {return m_distanceEffect.rolloffFactor(); }99 ExceptionOr<void> setRolloffFactor (double);94 double rolloffFactorForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_distanceEffect.rolloffFactor(); } 95 ExceptionOr<void> setRolloffFactorForBindings(double); 100 96 101 97 // Sound cones - angles in degrees 102 double coneInnerAngle () const {return m_coneEffect.innerAngle(); }103 void setConeInnerAngle (double);98 double coneInnerAngleForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_coneEffect.innerAngle(); } 99 void setConeInnerAngleForBindings(double); 104 100 105 double coneOuterAngle () const {return m_coneEffect.outerAngle(); }106 void setConeOuterAngle (double);101 double coneOuterAngleForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_coneEffect.outerAngle(); } 102 void setConeOuterAngleForBindings(double); 107 103 108 double coneOuterGain () const {return m_coneEffect.outerGain(); }109 ExceptionOr<void> setConeOuterGain (double);104 double coneOuterGainForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_coneEffect.outerGain(); } 105 ExceptionOr<void> setConeOuterGainForBindings(double); 110 106 111 107 ExceptionOr<void> setChannelCount(unsigned) final; 112 108 ExceptionOr<void> setChannelCountMode(ChannelCountMode) final; 113 109 114 void azimuthElevation(double* outAzimuth, double* outElevation); 115 116 double tailTime() const override { return m_panner ? m_panner->tailTime() : 0; } 117 double latencyTime() const override { return m_panner ? m_panner->latencyTime() : 0; } 110 double tailTime() const final; 111 double latencyTime() const final; 118 112 119 113 private: 120 114 PannerNode(BaseAudioContext&, const PannerOptions&); 121 115 122 void calculateAzimuthElevation(double* outAzimuth, double* outElevation, const FloatPoint3D& position, const FloatPoint3D& listenerPosition, const FloatPoint3D& listenerForward, const FloatPoint3D& listenerUp) ;123 float calculateDistanceConeGain(const FloatPoint3D& position, const FloatPoint3D& orientation, const FloatPoint3D& listenerPosition) ;116 void calculateAzimuthElevation(double* outAzimuth, double* outElevation, const FloatPoint3D& position, const FloatPoint3D& listenerPosition, const FloatPoint3D& listenerForward, const FloatPoint3D& listenerUp) WTF_REQUIRES_LOCK(m_processLock); 117 float calculateDistanceConeGain(const FloatPoint3D& position, const FloatPoint3D& orientation, const FloatPoint3D& listenerPosition) WTF_REQUIRES_LOCK(m_processLock); 124 118 125 119 // Returns the combined distance and cone gain attenuation. 126 float distanceConeGain() ;120 float distanceConeGain() WTF_REQUIRES_LOCK(m_processLock); 127 121 128 122 bool requiresTailProcessing() const final; 129 123 130 void processSampleAccurateValues(AudioBus* destination, const AudioBus* source, size_t framesToProcess); 131 bool hasSampleAccurateValues() const; 132 bool shouldUseARate() const; 124 void azimuthElevation(double* outAzimuth, double* outElevation) WTF_REQUIRES_LOCK(m_processLock); 125 void processSampleAccurateValues(AudioBus* destination, const AudioBus* source, size_t framesToProcess) WTF_REQUIRES_LOCK(m_processLock); 126 bool hasSampleAccurateValues() const WTF_REQUIRES_LOCK(m_processLock); 127 bool shouldUseARate() const WTF_REQUIRES_LOCK(m_processLock); 133 128 134 std::unique_ptr<Panner> m_panner; 135 PanningModelType m_panningModel; 129 FloatPoint3D position() const WTF_REQUIRES_LOCK(m_processLock); 130 FloatPoint3D orientation() const WTF_REQUIRES_LOCK(m_processLock); 131 132 Ref<HRTFDatabaseLoader> m_hrtfDatabaseLoader; 133 PanningModelType m_panningModel WTF_GUARDED_BY_LOCK(m_processLock); 134 std::unique_ptr<Panner> m_panner WTF_GUARDED_BY_LOCK(m_processLock); 136 135 137 136 // Gain 138 DistanceEffect m_distanceEffect ;139 ConeEffect m_coneEffect ;137 DistanceEffect m_distanceEffect WTF_GUARDED_BY_LOCK(m_processLock); 138 ConeEffect m_coneEffect WTF_GUARDED_BY_LOCK(m_processLock); 140 139 141 Ref<AudioParam> m_positionX ;142 Ref<AudioParam> m_positionY ;143 Ref<AudioParam> m_positionZ ;140 Ref<AudioParam> m_positionX WTF_GUARDED_BY_LOCK(m_processLock); 141 Ref<AudioParam> m_positionY WTF_GUARDED_BY_LOCK(m_processLock); 142 Ref<AudioParam> m_positionZ WTF_GUARDED_BY_LOCK(m_processLock); 144 143 145 Ref<AudioParam> m_orientationX; 146 Ref<AudioParam> m_orientationY; 147 Ref<AudioParam> m_orientationZ; 148 149 // HRTF Database loader 150 RefPtr<HRTFDatabaseLoader> m_hrtfDatabaseLoader; 144 Ref<AudioParam> m_orientationX WTF_GUARDED_BY_LOCK(m_processLock); 145 Ref<AudioParam> m_orientationY WTF_GUARDED_BY_LOCK(m_processLock); 146 Ref<AudioParam> m_orientationZ WTF_GUARDED_BY_LOCK(m_processLock); 151 147 152 148 // Synchronize process() with setting of the panning model, source's location -
trunk/Source/WebCore/Modules/webaudio/PannerNode.idl
r276715 r278284 32 32 33 33 // Default model for stereo is equalpower 34 attribute PanningModelType panningModel;34 [ImplementedAs=panningModelForBindings] attribute PanningModelType panningModel; 35 35 36 36 // Uses a 3D cartesian coordinate system … … 39 39 40 40 // Default distance model is inverse 41 attribute DistanceModelType distanceModel;41 [ImplementedAs=distanceModelForBindings] attribute DistanceModelType distanceModel; 42 42 43 attribute double refDistance;44 attribute double maxDistance;45 attribute double rolloffFactor;43 [ImplementedAs=refDistanceForBindings] attribute double refDistance; 44 [ImplementedAs=maxDistanceForBindings] attribute double maxDistance; 45 [ImplementedAs=rolloffFactorForBindings] attribute double rolloffFactor; 46 46 47 47 // Directional sound cone 48 attribute double coneInnerAngle;49 attribute double coneOuterAngle;50 attribute double coneOuterGain;48 [ImplementedAs=coneInnerAngleForBindings] attribute double coneInnerAngle; 49 [ImplementedAs=coneOuterAngleForBindings] attribute double coneOuterAngle; 50 [ImplementedAs=coneOuterGainForBindings] attribute double coneOuterGain; 51 51 52 52 // Position of audio source in 3D Cartesian system -
trunk/Source/WebCore/platform/audio/Distance.h
r267544 r278284 49 49 double gain(double distance); 50 50 51 DistanceModelType model() { return m_model; }51 DistanceModelType model() const { return m_model; } 52 52 53 53 void setModel(DistanceModelType model, bool clamped)
Note:
See TracChangeset
for help on using the changeset viewer.