Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions obs-studio-server/source/osn-scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,16 @@ void osn::Scene::CreatePrivate(void *data, const int64_t id, const std::vector<i

obs_source_t *source = obs_scene_get_source(scene);
if (!source) {
obs_scene_release(scene);
PRETTY_ERROR_RETURN(ErrorCode::Error, "Failed to get source from scene.");
}

uint64_t uid = osn::Source::Manager::GetInstance().allocate(source);
if (uid == UINT64_MAX) {
obs_scene_release(scene);
PRETTY_ERROR_RETURN(ErrorCode::CriticalError, "Index list is full.");
}
osn::Source::attach_source_signals(source);

rval.push_back(ipc::value((uint64_t)ErrorCode::Ok));
rval.push_back(ipc::value(uid));
Expand Down
4 changes: 2 additions & 2 deletions obs-studio-server/source/osn-source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ 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<ipc::value> &args, std::vector<ipc::value> &rval)
{
// Atomically find and acquire a strong reference under the manager lock,
// preventing the source from being destroyed between find() and obs_source_get_ref().
// Promote the manager's retained weak reference while its registration is
// locked, so the source remains alive for this call or is rejected as expired.
OBSSourceAutoRelease src = osn::Source::Manager::GetInstance().findAndRef(args[0].value_union.ui64);
if (!src) {
PRETTY_ERROR_RETURN(ErrorCode::InvalidReference, "Source reference is not valid.");
Expand Down
75 changes: 68 additions & 7 deletions obs-studio-server/source/osn-source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <ipc-server.hpp>
#include <obs.h>
#include <obs.hpp>
#include <utility>
#include "utility.hpp"
#undef strtoll
#include "nlohmann/json.hpp"
Expand All @@ -32,7 +33,10 @@ class Source {

protected:
Manager() {}
~Manager() {}
~Manager() { clear(); }

private:
std::map<utility::unique_id::id_t, OBSWeakSourceAutoRelease> weak_sources;

public:
Manager(Manager const &) = delete;
Expand All @@ -41,16 +45,73 @@ class Source {
public:
static Manager &GetInstance();

// Atomically finds the source and acquires a strong reference under the
// manager lock, preventing destruction between find() and obs_source_get_ref().
// Returns null (as OBSSourceAutoRelease) if not found or already destroyed.
utility::unique_id::id_t allocate(obs_source_t *source)
{
std::lock_guard<std::recursive_mutex> lock(internal_mutex);
OBSWeakSourceAutoRelease weakSource(obs_source_get_weak_source(source));

// A destroyed source's address can be reused while a stale manager
// entry still retains its expired weak control block. Only deduplicate
// when both the source address and weak control block identify the same
// live source; otherwise discard the stale registration.
for (auto iter = object_map.begin(); iter != object_map.end();) {
if (iter->second != source) {
++iter;
continue;
}

const auto weakIter = weak_sources.find(iter->first);
if (weakIter != weak_sources.end() && weakIter->second.Get() == weakSource.Get())
return iter->first;

weak_sources.erase(iter->first);
iter = object_map.erase(iter);
}

const auto uid = utility::unique_object_manager<obs_source_t>::allocate(source);
if (uid != std::numeric_limits<utility::unique_id::id_t>::max()) {
try {
weak_sources.emplace(uid, std::move(weakSource));
Comment thread
aleksandr-voitenko marked this conversation as resolved.
} catch (...) {
utility::unique_object_manager<obs_source_t>::free(uid);
throw;
}
}
return uid;
}

utility::unique_id::id_t free(obs_source_t *source)
{
std::lock_guard<std::recursive_mutex> lock(internal_mutex);
const auto uid = utility::unique_object_manager<obs_source_t>::free(source);
weak_sources.erase(uid);
return uid;
}

obs_source_t *free(utility::unique_id::id_t uid)
{
std::lock_guard<std::recursive_mutex> lock(internal_mutex);
obs_source_t *source = utility::unique_object_manager<obs_source_t>::free(uid);
weak_sources.erase(uid);
return source;
}

void clear()
{
std::lock_guard<std::recursive_mutex> lock(internal_mutex);
object_map.clear();
weak_sources.clear();
}

// Promote the retained weak reference instead of deriving a reference from
// the raw source pointer, whose control block may already be destroyed.
OBSSourceAutoRelease findAndRef(utility::unique_id::id_t id)
{
std::lock_guard<std::recursive_mutex> lock(internal_mutex);
auto iter = object_map.find(id);
if (iter == object_map.end())
auto iter = weak_sources.find(id);
if (iter == weak_sources.end())
return nullptr;
return obs_source_get_ref(iter->second);
return obs_weak_source_get_source(iter->second.Get());
}
};

Expand Down
Loading
Loading