From 167efab28c81af11ad53a4178648c36959ea7956 Mon Sep 17 00:00:00 2001 From: Richard Osborne Date: Wed, 20 May 2026 17:12:51 -0500 Subject: [PATCH 1/2] addref to obs_source_t* before getProperties * add concurrency test * addref to obs_source_t* before calling getProperties to handle the rare race where the browser_source was destroyed right after the null check on the url but before we could get the path. Typically, obs adds a ref but externally we cannot invoke obs_source_addref() directly. So instead we will use OBSSource * update skipSource() so these tests can be run locally on Darwin --- obs-studio-server/source/osn-source.cpp | 4 +++- tests/osn-tests/src/test_osn_source.ts | 31 +++++++++++++++++++++++++ tests/osn-tests/util/obs_handler.ts | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/obs-studio-server/source/osn-source.cpp b/obs-studio-server/source/osn-source.cpp index 48ce2a20c..b00cc1141 100644 --- a/obs-studio-server/source/osn-source.cpp +++ b/obs-studio-server/source/osn-source.cpp @@ -250,7 +250,9 @@ void osn::Source::IsConfigurable(void *data, const int64_t id, const std::vector void osn::Source::GetProperties(void *data, const int64_t id, const std::vector &args, std::vector &rval) { - // Attempt to find the source asked to load. + // Attempt to find the source asked to load. Get a strong reference to help + // guarantee the source is not destroyed while we are attempting to retrieve + // properties. obs_source_t *src = osn::Source::Manager::GetInstance().find(args[0].value_union.ui64); if (src == nullptr) { PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Source reference is not valid."); diff --git a/tests/osn-tests/src/test_osn_source.ts b/tests/osn-tests/src/test_osn_source.ts index fbbac72c0..a5d0a8421 100644 --- a/tests/osn-tests/src/test_osn_source.ts +++ b/tests/osn-tests/src/test_osn_source.ts @@ -591,6 +591,37 @@ describe(testName, () => { }); }); + it('Get properties of browser source while releasing concurrently does not crash', async function() { + if (obs.skipSource(EOBSInputTypes.BrowserSource)) { this.skip(); } // Skip if browser source is not supported + const iterations = 20; + const promises: Promise[] = []; + + for (let i = 0; i < iterations; i++) { + const input = osn.InputFactory.create(EOBSInputTypes.BrowserSource, 'browser_concurrent_' + i); + expect(input).to.not.equal(undefined, GetErrorMessage(ETestErrorMsg.CreateInput, EOBSInputTypes.BrowserSource)); + + // Race getProperties against release on separate async ticks + const getProps = new Promise(resolve => { + setImmediate(() => { + try { input.properties; } catch (_) {} + resolve(); + }); + }); + + const releaseSource = new Promise(resolve => { + setImmediate(() => { + try { input.release(); } catch (_) {} + resolve(); + }); + }); + + promises.push(getProps, releaseSource); + } + + // If the race condition is present, one of these will crash the OBS server process + await Promise.all(promises); + }); + it('Set enabled and get it for all filter types', () => { obs.filterTypes.forEach(function(filterType) { logInfo(testName, 'Testing filter type: ' + filterType); diff --git a/tests/osn-tests/util/obs_handler.ts b/tests/osn-tests/util/obs_handler.ts index a3b8de811..078a5e373 100644 --- a/tests/osn-tests/util/obs_handler.ts +++ b/tests/osn-tests/util/obs_handler.ts @@ -527,7 +527,7 @@ export class OBSHandler { } skipSource(inputType: string) { - if (process.platform === 'darwin') { + if (process.platform === 'darwin' && this.isCI()) { if (inputType === 'browser_source' || inputType === 'window_capture' || inputType === 'monitor_capture' || From 586a06486bc97aab7a4691689da5f0b22fef438d Mon Sep 17 00:00:00 2001 From: Richard Osborne Date: Wed, 20 May 2026 17:47:11 -0500 Subject: [PATCH 2/2] use OBSSource to addref --- obs-studio-server/source/osn-source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obs-studio-server/source/osn-source.cpp b/obs-studio-server/source/osn-source.cpp index b00cc1141..f4dda8525 100644 --- a/obs-studio-server/source/osn-source.cpp +++ b/obs-studio-server/source/osn-source.cpp @@ -253,7 +253,7 @@ void osn::Source::GetProperties(void *data, const int64_t id, const std::vector< // Attempt to find the source asked to load. Get a strong reference to help // guarantee the source is not destroyed while we are attempting to retrieve // properties. - obs_source_t *src = osn::Source::Manager::GetInstance().find(args[0].value_union.ui64); + OBSSource src = osn::Source::Manager::GetInstance().find(args[0].value_union.ui64); if (src == nullptr) { PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Source reference is not valid."); }