|
88 | 88 | #include <clocale> |
89 | 89 | #include <cmath> |
90 | 90 | #include <limits> |
| 91 | +#include <set> |
91 | 92 |
|
92 | 93 | #include "FilterGraph.h" |
93 | 94 | #include "DeviceManager.h" |
|
97 | 98 | #define YieldProcessor() __nop(void) |
98 | 99 | #endif |
99 | 100 |
|
| 101 | +/* Global Lock Manager */ |
| 102 | +class GlobalLockManager |
| 103 | +{ |
| 104 | +public: |
| 105 | + // Get a named mutex. If it doesn't exist, it's created. |
| 106 | + static std::mutex& get_mutex(const std::string& name); |
| 107 | + |
| 108 | + // Track which environments hold which locks for cleanup. |
| 109 | + static void acquire_lock_for_env(const std::string& name, ScriptEnvironment* env); |
| 110 | + static void release_lock_for_env(const std::string& name, ScriptEnvironment* env); |
| 111 | + |
| 112 | + // Called when an IScriptEnvironment is destroyed. |
| 113 | + static void on_environment_exit(ScriptEnvironment* env); |
| 114 | + |
| 115 | +private: |
| 116 | + GlobalLockManager() = delete; |
| 117 | + ~GlobalLockManager() = delete; |
| 118 | + |
| 119 | + static std::map<std::string, std::unique_ptr<std::mutex>> s_namedMutexes; |
| 120 | + static std::mutex s_mutexMapLock; // Protects s_namedMutexes |
| 121 | + |
| 122 | + static std::map<ScriptEnvironment*, std::set<std::string>> s_envToHeldLocks; |
| 123 | + static std::mutex s_envLocksMapLock; // Protects s_envToHeldLocks |
| 124 | +}; |
| 125 | + |
| 126 | +std::map<std::string, std::unique_ptr<std::mutex>> GlobalLockManager::s_namedMutexes; |
| 127 | +std::mutex GlobalLockManager::s_mutexMapLock; |
| 128 | +std::map<ScriptEnvironment*, std::set<std::string>> GlobalLockManager::s_envToHeldLocks; |
| 129 | +std::mutex GlobalLockManager::s_envLocksMapLock; |
| 130 | + |
| 131 | +std::mutex& GlobalLockManager::get_mutex(const std::string& name) |
| 132 | +{ |
| 133 | + std::lock_guard<std::mutex> lock(s_mutexMapLock); |
| 134 | + if (s_namedMutexes.find(name) == s_namedMutexes.end()) |
| 135 | + { |
| 136 | + s_namedMutexes[name] = std::make_unique<std::mutex>(); |
| 137 | + } |
| 138 | + return *s_namedMutexes[name]; |
| 139 | +} |
| 140 | + |
| 141 | +void GlobalLockManager::acquire_lock_for_env(const std::string& name, ScriptEnvironment* env) |
| 142 | +{ |
| 143 | + std::lock_guard<std::mutex> envLock(s_envLocksMapLock); |
| 144 | + s_envToHeldLocks[env].insert(name); |
| 145 | +} |
| 146 | + |
| 147 | +void GlobalLockManager::release_lock_for_env(const std::string& name, ScriptEnvironment* env) |
| 148 | +{ |
| 149 | + std::lock_guard<std::mutex> envLock(s_envLocksMapLock); |
| 150 | + if (s_envToHeldLocks.count(env)) |
| 151 | + { |
| 152 | + s_envToHeldLocks[env].erase(name); |
| 153 | + if (s_envToHeldLocks[env].empty()) |
| 154 | + { |
| 155 | + s_envToHeldLocks.erase(env); |
| 156 | + } |
| 157 | + } |
| 158 | +} |
| 159 | + |
| 160 | +void GlobalLockManager::on_environment_exit(ScriptEnvironment* env) |
| 161 | +{ |
| 162 | + std::lock_guard<std::mutex> envLock(s_envLocksMapLock); |
| 163 | + auto it = s_envToHeldLocks.find(env); |
| 164 | + if (it != s_envToHeldLocks.end()) |
| 165 | + { |
| 166 | + // Note: The actual mutexes are not unlocked here, only the tracking is cleared. |
| 167 | + // A truly stuck mutex requires more complex solutions (e.g., timed_mutex with recovery, |
| 168 | + // or ensuring threads exit gracefully). For Avisynth's use case, clearing the tracking |
| 169 | + // is important to prevent memory leaks in the map. |
| 170 | + |
| 171 | + // If it tried to call unlock() on a mutex held by another (potentially crashed) thread, |
| 172 | + // that would lead to undefined behavior.By simply removing the tracking entry, |
| 173 | + // we avoid this dangerous operation. |
| 174 | + s_envToHeldLocks.erase(it); |
| 175 | + } |
| 176 | +} |
| 177 | + |
100 | 178 | extern const AVSFunction Audio_filters[], |
101 | 179 | Combine_filters[], |
102 | 180 | Convert_filters[], |
@@ -779,6 +857,8 @@ class ScriptEnvironment { |
779 | 857 | PVideoFrame Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height); |
780 | 858 | int SetMemoryMax(int mem); |
781 | 859 | int SetWorkingDir(const char* newdir); |
| 860 | + bool AcquireGlobalLock(const char* name); |
| 861 | + void ReleaseGlobalLock(const char* name); |
782 | 862 | AVSC_CC ~ScriptEnvironment(); |
783 | 863 | void* ManageCache(int key, void* data); |
784 | 864 | bool PlanarChromaAlignment(IScriptEnvironment::PlanarChromaAlignmentMode key); |
@@ -1911,6 +1991,14 @@ class ThreadScriptEnvironment : public InternalEnvironment |
1911 | 1991 | return core->SetWorkingDir(newdir); |
1912 | 1992 | } |
1913 | 1993 |
|
| 1994 | + bool __stdcall AcquireGlobalLock(const char* name) { |
| 1995 | + return core->AcquireGlobalLock(name); |
| 1996 | + } |
| 1997 | + |
| 1998 | + void __stdcall ReleaseGlobalLock(const char* name) { |
| 1999 | + core->ReleaseGlobalLock(name); |
| 2000 | + } |
| 2001 | + |
1914 | 2002 | void* __stdcall ManageCache(int key, void* data) |
1915 | 2003 | { |
1916 | 2004 | if ( |
@@ -2602,6 +2690,8 @@ ScriptEnvironment::~ScriptEnvironment() { |
2602 | 2690 | // give every one their last wish. |
2603 | 2691 | at_exit.Execute(threadEnv.get()); |
2604 | 2692 |
|
| 2693 | + GlobalLockManager::on_environment_exit(this); // V12 |
| 2694 | + |
2605 | 2695 | delete thread_pool; |
2606 | 2696 |
|
2607 | 2697 | tls->var_table.Clear(); |
@@ -5567,6 +5657,25 @@ int ScriptEnvironment::SetMemoryMax(AvsDeviceType type, int index, int mem) |
5567 | 5657 | return Devices->GetDevice(type, index)->SetMemoryMax(mem); |
5568 | 5658 | } |
5569 | 5659 |
|
| 5660 | +bool ScriptEnvironment::AcquireGlobalLock(const char* name) |
| 5661 | +{ |
| 5662 | + if (!name) return false; |
| 5663 | + std::string lock_name(name); |
| 5664 | + std::mutex& mtx = GlobalLockManager::get_mutex(lock_name); |
| 5665 | + mtx.lock(); // Blocks until lock is acquired. |
| 5666 | + GlobalLockManager::acquire_lock_for_env(lock_name, this); // Track for cleanup. |
| 5667 | + return true; |
| 5668 | +} |
| 5669 | + |
| 5670 | +void ScriptEnvironment::ReleaseGlobalLock(const char* name) |
| 5671 | +{ |
| 5672 | + if (!name) return; |
| 5673 | + std::string lock_name(name); |
| 5674 | + std::mutex& mtx = GlobalLockManager::get_mutex(lock_name); |
| 5675 | + GlobalLockManager::release_lock_for_env(lock_name, this); // Untrack. |
| 5676 | + mtx.unlock(); |
| 5677 | +} |
| 5678 | + |
5570 | 5679 | PVideoFrame ScriptEnvironment::GetOnDeviceFrame(const PVideoFrame& src, Device* device) |
5571 | 5680 | { |
5572 | 5681 | typedef int diff_t; |
|
0 commit comments