Skip to content

Commit b6953ee

Browse files
committed
v12 API: add global lock and unlock
1 parent 65c447b commit b6953ee

7 files changed

Lines changed: 183 additions & 8 deletions

File tree

avs_core/core/CompatEnvironment.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@ class IScriptEnvironment_AvsPreV11C {
295295
virtual int __stdcall propGetDataTypeHint(const AVSMap* map, const char* key, int index, int* error) = 0; // returns AVSPropDataTypeHint
296296
virtual int __stdcall propSetDataH(AVSMap* map, const char* key, const char* d, int length, int type, int append) = 0;
297297

298+
// V12
299+
// New Global Lock API for cross-plugin synchronization.
300+
// Plugins must ensure these calls are balanced (acquire followed by release),
301+
virtual bool __stdcall AcquireGlobalLock(const char* name) = 0;
302+
virtual void __stdcall ReleaseGlobalLock(const char* name) = 0;
303+
298304
}; // end class IScriptEnvironment_AvsPreV11C. Order is important.
299305

300306
#endif // _AVS_COMPATENVIRONMENT_H_INCLUDED

avs_core/core/InternalEnvironment.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ class InternalEnvironment :
202202
virtual int __stdcall propGetDataTypeHint(const AVSMap* map, const char* key, int index, int* error) = 0; /* returns AVSPropDataTypeHint */
203203
virtual int __stdcall propSetDataH(AVSMap* map, const char* key, const char* d, int length, int type, int append) = 0;
204204

205+
// V12
206+
virtual bool __stdcall AcquireGlobalLock(const char* name) = 0;
207+
virtual void __stdcall ReleaseGlobalLock(const char* name) = 0;
208+
205209
// IScriptEnvironment2
206210
virtual bool __stdcall LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result) = 0;
207211
virtual void __stdcall AddAutoloadDir(const char* dirPath, bool toFront) = 0;

avs_core/core/avisynth.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
#include <clocale>
8989
#include <cmath>
9090
#include <limits>
91+
#include <set>
9192

9293
#include "FilterGraph.h"
9394
#include "DeviceManager.h"
@@ -97,6 +98,83 @@
9798
#define YieldProcessor() __nop(void)
9899
#endif
99100

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+
100178
extern const AVSFunction Audio_filters[],
101179
Combine_filters[],
102180
Convert_filters[],
@@ -779,6 +857,8 @@ class ScriptEnvironment {
779857
PVideoFrame Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height);
780858
int SetMemoryMax(int mem);
781859
int SetWorkingDir(const char* newdir);
860+
bool AcquireGlobalLock(const char* name);
861+
void ReleaseGlobalLock(const char* name);
782862
AVSC_CC ~ScriptEnvironment();
783863
void* ManageCache(int key, void* data);
784864
bool PlanarChromaAlignment(IScriptEnvironment::PlanarChromaAlignmentMode key);
@@ -1911,6 +1991,14 @@ class ThreadScriptEnvironment : public InternalEnvironment
19111991
return core->SetWorkingDir(newdir);
19121992
}
19131993

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+
19142002
void* __stdcall ManageCache(int key, void* data)
19152003
{
19162004
if (
@@ -2602,6 +2690,8 @@ ScriptEnvironment::~ScriptEnvironment() {
26022690
// give every one their last wish.
26032691
at_exit.Execute(threadEnv.get());
26042692

2693+
GlobalLockManager::on_environment_exit(this); // V12
2694+
26052695
delete thread_pool;
26062696

26072697
tls->var_table.Clear();
@@ -5567,6 +5657,25 @@ int ScriptEnvironment::SetMemoryMax(AvsDeviceType type, int index, int mem)
55675657
return Devices->GetDevice(type, index)->SetMemoryMax(mem);
55685658
}
55695659

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+
55705679
PVideoFrame ScriptEnvironment::GetOnDeviceFrame(const PVideoFrame& src, Device* device)
55715680
{
55725681
typedef int diff_t;

avs_core/core/avisynth.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,5 @@ EXPORTS
165165
avs_val_is_array = _avs_val_is_array@8
166166
avs_val_is_error = _avs_val_is_error@8
167167

168+
avs_acquire_global_lock = _avs_acquire_global_lock@8
169+
avs_release_global_lock = _avs_release_global_lock@8

avs_core/core/avisynth_c.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,33 @@ int AVSC_CC avs_set_working_dir(AVS_ScriptEnvironment * p, const char* newdir)
15131513
}
15141514
}
15151515

1516+
// V12
1517+
extern "C"
1518+
int AVSC_CC avs_acquire_global_lock(AVS_ScriptEnvironment* p, const char* name)
1519+
{
1520+
p->error = 0;
1521+
try {
1522+
return p->env->AcquireGlobalLock(name) ? 1 : 0;
1523+
}
1524+
catch (const AvisynthError& err) {
1525+
p->error = err.msg;
1526+
return 0;
1527+
}
1528+
}
1529+
1530+
// V12
1531+
extern "C"
1532+
void AVSC_CC avs_release_global_lock(AVS_ScriptEnvironment* p, const char* name)
1533+
{
1534+
p->error = 0;
1535+
try {
1536+
p->env->ReleaseGlobalLock(name);
1537+
}
1538+
catch (const AvisynthError& err) {
1539+
p->error = err.msg;
1540+
}
1541+
}
1542+
15161543
// Interface V8. See AVS_AEP_xxx enums
15171544
extern "C"
15181545
size_t AVSC_CC avs_get_env_property(AVS_ScriptEnvironment * p, int avs_aep_prop)

avs_core/include/avisynth.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
// - New propSetDataH, like propSetData but with optional data type hint (byte/string)
5555
// (VSAPI4: mapSetData, our propSetData became VSAPI4: mapSetData3)
5656
// 20250415 V11.1 Fix AVS_Value 64 bit data member declaration for 64-bit non Intel (other than X86_X64) systems.
57+
// 20250601 V12 Global lock aquire and release: AcquireGlobalLock, ReleaseGlobalLock
5758

5859
// http://avisynth.nl
5960

@@ -90,8 +91,8 @@
9091
// graphical user interfaces.
9192

9293

93-
#ifndef __AVISYNTH_11_H__
94-
#define __AVISYNTH_11_H__
94+
#ifndef __AVISYNTH_12_H__
95+
#define __AVISYNTH_12_H__
9596

9697
#include "avs/config.h"
9798
#include "avs/capi.h"
@@ -124,8 +125,8 @@ enum AvsVersion {
124125
AVISYNTH_CLASSIC_INTERFACE_VERSION_25 = 3,
125126
AVISYNTH_CLASSIC_INTERFACE_VERSION_26BETA = 5,
126127
AVISYNTH_CLASSIC_INTERFACE_VERSION = 6,
127-
AVISYNTH_INTERFACE_VERSION = 11,
128-
AVISYNTHPLUS_INTERFACE_BUGFIX_VERSION = 1 // reset to zero whenever the normal interface version bumps
128+
AVISYNTH_INTERFACE_VERSION = 12,
129+
AVISYNTHPLUS_INTERFACE_BUGFIX_VERSION = 0 // reset to zero whenever the normal interface version bumps
129130
};
130131

131132
/* Compiler-specific crap */
@@ -1714,6 +1715,12 @@ class IScriptEnvironment {
17141715
virtual int __stdcall propGetDataTypeHint(const AVSMap* map, const char* key, int index, int* error) = 0; // returns AVSPropDataTypeHint
17151716
virtual int __stdcall propSetDataH(AVSMap* map, const char* key, const char* d, int length, int type, int append) = 0;
17161717

1718+
// V12
1719+
// New Global Lock API for cross-plugin synchronization.
1720+
// Plugins must ensure these calls are balanced (acquire followed by release),
1721+
virtual bool __stdcall AcquireGlobalLock(const char* name) = 0;
1722+
virtual void __stdcall ReleaseGlobalLock(const char* name) = 0;
1723+
17171724
}; // end class IScriptEnvironment. Order is important. Avoid overloads with the same name.
17181725

17191726

@@ -1939,6 +1946,9 @@ class INeoEnv {
19391946
virtual void* __stdcall Allocate(size_t nBytes, size_t alignment, AvsAllocType type) = 0;
19401947
virtual void __stdcall Free(void* ptr) = 0;
19411948

1949+
virtual bool __stdcall AcquireGlobalLock(const char* name) = 0;
1950+
virtual void __stdcall ReleaseGlobalLock(const char* name) = 0;
1951+
19421952
virtual char* __stdcall SaveString(const char* s, int length = -1) = 0;
19431953
virtual char* __stdcall SaveString(const char* s, int length, bool escape) = 0;
19441954
virtual char* Sprintf(const char* fmt, ...) = 0;
@@ -2031,4 +2041,4 @@ AVSC_API(IScriptEnvironment2*, CreateScriptEnvironment2)(int version = AVISYNTH_
20312041

20322042
#pragma pack(pop)
20332043

2034-
#endif //__AVISYNTH_11_H__
2044+
#endif //__AVISYNTH_12_H__

avs_core/include/avisynth_c.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
// New avs_add_func_r: alternative avs_add_func which returns the result in a byref parameter
131131
// New AVS_ApplyFuncR type
132132
// 20250415 V11.1 Fix AVS_Value 64 bit data member declaration for 64-bit non Intel (other than X86_X64) systems.
133+
// 20250601 V12 Global lock acquire and release: avs_acquire_global_lock, avs_release_global_lock
133134

134135
// Notes.
135136
// Choose either method:
@@ -160,11 +161,11 @@
160161
// Constants
161162
//
162163

163-
#ifndef __AVISYNTH_11_H__
164+
#ifndef __AVISYNTH_12_H__
164165
enum {
165166
AVISYNTH_INTERFACE_CLASSIC_VERSION = 6,
166-
AVISYNTH_INTERFACE_VERSION = 11,
167-
AVISYNTHPLUS_INTERFACE_BUGFIX_VERSION = 1 // reset to zero whenever the normal interface version bumps
167+
AVISYNTH_INTERFACE_VERSION = 12,
168+
AVISYNTHPLUS_INTERFACE_BUGFIX_VERSION = 0 // reset to zero whenever the normal interface version bumps
168169
};
169170
#endif
170171

@@ -1329,6 +1330,16 @@ AVSC_API(int, avs_set_memory_max)(AVS_ScriptEnvironment *, int mem);
13291330

13301331
AVSC_API(int, avs_set_working_dir)(AVS_ScriptEnvironment *, const char * newdir);
13311332

1333+
// V12
1334+
// Acquire a global named lock.
1335+
// 'env' is the environment handle, 'name' is the lock identifier (e.g., "fftw").
1336+
// Returns 1 on success, 0 on failure.
1337+
AVSC_API(int, avs_acquire_global_lock)(AVS_ScriptEnvironment *, const char* name);
1338+
// V12
1339+
// Release a global named lock.
1340+
// 'env' is the environment handle, 'name' is the lock identifier.
1341+
AVSC_API(void, avs_release_global_lock)(AVS_ScriptEnvironment *, const char* name);
1342+
13321343
// avisynth.dll exports this; it's a way to use it as a library, without
13331344
// writing an AVS script or without going through AVIFile.
13341345
AVSC_API(AVS_ScriptEnvironment *, avs_create_script_environment)(int version);
@@ -1651,6 +1662,9 @@ struct AVS_Library {
16511662
AVSC_DECLARE_FUNC(avs_val_is_string);
16521663
AVSC_DECLARE_FUNC(avs_val_is_array);
16531664
AVSC_DECLARE_FUNC(avs_val_is_error);
1665+
// V12
1666+
AVSC_DECLARE_FUNC(avs_acquire_global_lock);
1667+
AVSC_DECLARE_FUNC(avs_release_global_lock);
16541668
};
16551669

16561670
#undef AVSC_DECLARE_FUNC
@@ -1940,6 +1954,9 @@ avs_bits_per_component constant 8 (8 bits/component)
19401954
AVSC_LOAD_FUNC_OPT(avs_val_is_string);
19411955
AVSC_LOAD_FUNC_OPT(avs_val_is_array);
19421956
AVSC_LOAD_FUNC_OPT(avs_val_is_error);
1957+
// V12
1958+
AVSC_LOAD_FUNC_OPT(avs_acquire_global_lock);
1959+
AVSC_LOAD_FUNC_OPT(avs_release_global_lock);
19431960

19441961
#undef __AVSC_STRINGIFY
19451962
#undef AVSC_STRINGIFY

0 commit comments

Comments
 (0)