@@ -425,7 +425,8 @@ SaveString, Sprintf, VSprintf, Invoke, BitBlt, AtExit, AddFunction,
425425MakeWritable, FunctionExists, GetVar, GetVarDef, SetVar, SetGlobalVar,
426426PushContext, PopContext, NewVideoFrame, CheckVersion, Subframe,
427427SubframePlanar, SetMemoryMax, SetWorkingDir, DeleteScriptEnvironment
428- and ApplyMessage. They are described in the following subsections.
428+ and ApplyMessage and many others They are described in the following
429+ subsections.
429430
430431
431432.. _cplusplus_throwerror :
@@ -1710,7 +1711,8 @@ CPP interface (through avisynth.h).
17101711 has_at_least_v8 = avisynth_if_ver >= 8; // frame properties, NewVideoFrameP, other V8 environment functions
17111712 has_at_least_v8_1 = avisynth_if_ver > 8 || (avisynth_if_ver == 8 && avisynth_bugfix_ver >= 1);
17121713 // 8.1: C interface frameprop access fixed, IsPropertyWritable/MakePropertyWritable support, extended GetEnvProperty queries
1713- has_at_least_v9 = avisynth_if_ver >= 9; // future
1714+ has_at_least_v9 = avisynth_if_ver >= 9;
1715+ has_at_least_v12 = avisynth_if_ver >= 12; // global locks
17141716
17151717C interface (through avisynth_c.h)
17161718
@@ -1734,7 +1736,8 @@ C interface (through avisynth_c.h)
17341736 has_at_least_v8 = avisynth_if_ver >= 8; // frame properties, NewVideoFrameP, other V8 environment functions
17351737 has_at_least_v8_1 = avisynth_if_ver > 8 || (avisynth_if_ver == 8 && avisynth_bugfix_ver >= 1);
17361738 // 8.1: C interface frameprop access fixed, IsPropertyWritable/MakePropertyWritable support, extended GetEnvProperty queries
1737- has_at_least_v9 = avisynth_if_ver >= 9; // future
1739+ has_at_least_v9 = avisynth_if_ver >= 9;
1740+ has_at_least_v12 = avisynth_if_ver >= 12; // global locks
17381741
17391742
17401743AEP_INTERFACE_BUGFIX (c++) AVS_AEP_INTERFACE_BUGFIX (c)
@@ -1768,6 +1771,24 @@ Allocate, v8
17681771
17691772buffer pool allocate.
17701773
1774+ Primary goal of ``AVS_NORMAL_ALLOC `` was to have the Avisynth-reserved memory
1775+ counter up-to-date and to take into account this memory area as well.
1776+ Thus when reaching the MemoryMax limit the core would free up memory from
1777+ this resource as well (along with frame registry and cache entries).
1778+ Works like a normal _aligned_alloc.
1779+
1780+ But ``AVS_POOLED_ALLOC `` has (or had) a lot more important benefit. Pooled allocations
1781+ are meant for filters that don't need the buffers between frames and can free
1782+ them between calls to ``GetFrame() ``. Then multiple filters can use the same buffers
1783+ instead of each filter unnecessarily clinging onto them even while a different filter
1784+ is executing. This resulted in tons of memory savings, which was really useful, as most
1785+ of the complex scripts on HD material used to be memory-bound in that era (around 2015).
1786+ The main reason ``env->Allocate `` and ``env->Free `` were created at all was to support these
1787+ pooled allocations and memory re-use between filters. Adding ``AVS_NORMAL_ALLOC `` was just a bonus.
1788+
1789+ The memory savings might not be that important today (as of 2025) since PCs now have a lot
1790+ more RAM, but the savings are still there.
1791+
17711792::
17721793
17731794 // IScriptEnvironment Allocate
@@ -1787,7 +1808,7 @@ Free, v8
17871808
17881809 virtual void __stdcall Free(void* ptr) = 0;
17891810
1790- buffer pool free.
1811+ buffer pool free. Pair of `` Allocate ``.
17911812
17921813
17931814.. _cplusplus_getvartry :
@@ -1883,6 +1904,258 @@ See also
18831904- :ref: `getFramePropsRW <cplusplus_getframepropsrw >`.
18841905
18851906
1907+ .. _cplusplus_acquiregloballock :
1908+ .. _cplusplus_releasegloballock :
1909+
1910+ AcquireGlobalLock, v12
1911+ ^^^^^^^^^^^^^^^^^^^^^^
1912+ ReleaseGlobalLock, v12
1913+ ^^^^^^^^^^^^^^^^^^^^^^
1914+
1915+ ::
1916+
1917+ // C++ interface (IScriptEnvironment virtual methods)
1918+ virtual bool __stdcall AcquireGlobalLock(const char* name) = 0;
1919+ virtual void __stdcall ReleaseGlobalLock(const char* name) = 0;
1920+
1921+ // C interface (global avs_ functions)
1922+ AVS_EXPORT int __stdcall avs_acquire_global_lock(AVS_ScriptEnvironment* env, const char* name);
1923+ AVS_EXPORT void __stdcall avs_release_global_lock(AVS_ScriptEnvironment* env, const char* name);
1924+
1925+ These functions provide a global, named mutex mechanism to synchronize access to shared
1926+ resources across different plugins within the same Avisynth process. This is essential for
1927+ libraries like fftw3 that have non-thread-safe global state (e.g., their planner functions).
1928+
1929+ When AcquireGlobalLock (or avs_acquire_global_lock) is called, the calling thread will block
1930+ until the named lock is available. The lock is then exclusively held by that thread until
1931+ ReleaseGlobalLock (or avs_release_global_lock) is called.
1932+
1933+ The name parameter allows for different independent global locks.
1934+ For FFTW, the recommended name is "fftw".
1935+
1936+ For safe use, set and check ``has_at_least_v12 ``, see interface version check methods above.
1937+
1938+ **Example#1 RAII for C++ interface plugins **
1939+
1940+ C++ plugins, which operate with the IScriptEnvironment* interface, should use a
1941+ Resource Acquisition Is Initialization (RAII) wrapper. This GlobalLockGuard class example
1942+ ensures the lock is automatically released when the guarding object goes out of scope,
1943+ even if exceptions occur.
1944+
1945+ The constructor of this GlobalLockGuard expects an IScriptEnvironment* directly,
1946+ as C++ plugins will have access to this pointer.
1947+ ::
1948+
1949+ // FFTW is not thread-safe, need to guard around its functions (except fftw_execute).
1950+ // http://www.fftw.org/fftw3_doc/Thread-safety.html#Thread-safety
1951+ // Pre V12, not 100%, does not guard locks from multiple plugins using FFTW at the same time.
1952+ static std::mutex fftw_legacy_mutex; // defined as static
1953+
1954+ // Since Avisynth IF v12 use global lock which handles fftw locks for different plugins which use the same FFTW library.
1955+ class GlobalLockGuard
1956+ {
1957+ public:
1958+ // env_ptr should be the IScriptEnvironment* received by the plugin's function.
1959+ // lock_name is the name of the lock (e.g., "fftw").
1960+ // use_v12_if_available: If true, tries V12. If false or V12 not available, falls back to legacy mutex.
1961+ GlobalLockGuard(IScriptEnvironment* env_ptr, const char* lock_name, bool use_v12_global_lock)
1962+ : m_env_ptr(env_ptr), m_lockName(lock_name), m_acquired(false), m_is_legacy_lock(false)
1963+ {
1964+ if (!m_env_ptr || !m_lockName) {
1965+ // Invalid parameters, cannot acquire lock.
1966+ return;
1967+ }
1968+
1969+ // Attempt to acquire V12 global lock if requested and available.
1970+ // This assumes IScriptEnvironment provides a way to check its version.
1971+ if (use_v12_global_lock)
1972+ {
1973+ // We must use a try-catch block if AcquireGlobalLock can throw,
1974+ // or just assume it blocks and returns success/failure.
1975+ // Assuming it blocks and returns true/false for success.
1976+ m_acquired = m_env_ptr->AcquireGlobalLock(m_lockName);
1977+ if (m_acquired) {
1978+ m_is_legacy_lock = false; // Successfully acquired V12 lock
1979+ return; // Lock acquired, exit constructor
1980+ }
1981+ }
1982+
1983+ // If we reach here, V12 lock wasn't used/acquired, fall back to legacy mutex.
1984+ if (strcmp(m_lockName, "fftw") == 0) {
1985+ fftw_legacy_mutex.lock(); // Acquire the legacy mutex
1986+ m_acquired = true;
1987+ m_is_legacy_lock = true; // Acquired legacy lock
1988+ }
1989+ // else { // Handle unrecognized lock_name for legacy fallback if needed }
1990+ }
1991+
1992+ // Destructor releases the lock.
1993+ ~GlobalLockGuard()
1994+ {
1995+ if (m_acquired) // Only attempt to release if successfully acquired
1996+ {
1997+ if (m_is_legacy_lock) {
1998+ fftw_legacy_mutex.unlock(); // Release legacy mutex
1999+ }
2000+ else {
2001+ // Release V12 global lock
2002+ if (m_env_ptr) { // Safety check
2003+ m_env_ptr->ReleaseGlobalLock(m_lockName);
2004+ }
2005+ }
2006+ }
2007+ }
2008+
2009+ bool is_acquired() const { return m_acquired; }
2010+
2011+ // Disallow copying and assignment to prevent common errors with mutexes.
2012+ GlobalLockGuard(const GlobalLockGuard&) = delete;
2013+ GlobalLockGuard& operator=(const GlobalLockGuard&) = delete;
2014+
2015+ private:
2016+ IScriptEnvironment* m_env_ptr; // Store the C++ interface pointer directly
2017+ const char* m_lockName;
2018+ bool m_acquired;
2019+ bool m_is_legacy_lock; // true if legacy mutex was used, false if V12 global lock was used
2020+ };
2021+
2022+ **Example for lock, C++ plugin using RAII **
2023+
2024+ ::
2025+
2026+ // In a C++ plugin's source file (e.g., fft3dfilter, dfttest.cpp)
2027+ #include "AvsLockGuard.h" // Assuming GlobalLockGuard is in this header
2028+
2029+ // ... plugin setup ...
2030+
2031+ // Use a scope to define the critical section for FFTW planning.
2032+ {
2033+ // Acquire the global "fftw" lock. It's automatically released when this scope exits.
2034+ GlobalLockGuard fftw_lock(env, "fftw", has_at_least_v12);
2035+
2036+ // --- CRITICAL SECTION START ---
2037+ // Code here is protected by the global "fftw" lock.
2038+ // Only one thread across all dynamically linked FFTW plugins
2039+ // within this process can execute this section concurrently.
2040+ fftwf_plan my_plan = fftwf_plan_dft_r2c_3d(...); // Perform FFTW planning
2041+ // --- CRITICAL SECTION END ---
2042+
2043+ } // `fftw_lock` goes out of scope here, automatically releasing the lock.
2044+
2045+
2046+ Note that when the lock is used in plan destroying, in a class destructror, we don't have
2047+ ``env `` as a parameter, so we must use an an ``env_saved `` pointer stored earlier.
2048+
2049+ **Example#2 RAII for C++ plugins using C-Compatible interface **
2050+
2051+ C-compatible plugins, which receive an ``AVS_ScriptEnvironment* ``, should also use an
2052+ RAII wrapper for safe lock management. This ``GlobalLockGuardC `` will internally call
2053+ the ``avs_ `` C functions for acquiring and releasing the lock.
2054+
2055+ ::
2056+
2057+ // Note: no pre-V12 fallback is shown here
2058+
2059+ // It operates on the AVS_ScriptEnvironment* handle and calls the C-interface functions.
2060+ class GlobalLockGuardC
2061+ {
2062+ public:
2063+ // env_handle should be the AVS_ScriptEnvironment* received by the plugin's function.
2064+ GlobalLockGuardC(AVS_ScriptEnvironment* env_handle, const char* lock_name)
2065+ : m_env_handle(env_handle), m_lockName(lock_name), m_acquired(false)
2066+ {
2067+ if (m_env_handle && m_lockName)
2068+ {
2069+ m_acquired = (avs_acquire_global_lock(m_env_handle, m_lockName) == 1);
2070+ }
2071+ }
2072+
2073+ // Destructor releases the lock using the C-interface functions.
2074+ ~GlobalLockGuardC()
2075+ {
2076+ if (m_acquired && m_env_handle && m_lockName)
2077+ {
2078+ avs_release_global_lock(m_env_handle, m_lockName);
2079+ }
2080+ }
2081+
2082+ bool is_acquired() const { return m_acquired; }
2083+
2084+ // Disallow copying and assignment to prevent common errors with mutexes.
2085+ GlobalLockGuardC(const GlobalLockGuardC&) = delete;
2086+ GlobalLockGuardC& operator=(const GlobalLockGuardC&) = delete;
2087+
2088+ private:
2089+ AVS_ScriptEnvironment* m_env_handle; // Store the C-compatible handle
2090+ const char* m_lockName;
2091+ bool m_acquired;
2092+ };
2093+
2094+
2095+
2096+ **Example for lock, C++ interface, with above RAII **
2097+ ::
2098+
2099+ // In a C-compatible plugin's source file (e.g., myfilter.cpp that uses C++ features)
2100+ // You'd typically also include the C-compatible RAII wrapper
2101+
2102+ // Plugin function signature for C-compatible plugins (receives AVS_ScriptEnvironment*)
2103+ static AVS_Value AVSC_CC Create_xxxx(AVS_ScriptEnvironment* env, AVS_Value args, void* param)
2104+ {
2105+ // ... plugin setup ...
2106+
2107+ // Use a scope to define the critical section for FFTW planning.
2108+ {
2109+ // Acquire the global "fftw" lock using the C-compatible RAII wrapper.
2110+ GlobalLockGuardC fftw_lock(env, "fftw");
2111+
2112+ // You might want to check if the lock was acquired, though avs_acquire_global_lock
2113+ // will typically block until successful.
2114+ // If you need to handle acquisition failure, you would check fftw_lock.is_acquired().
2115+ // In a pure C plugin, you would return an error AVS_Value.
2116+
2117+ // --- CRITICAL SECTION START ---
2118+ // Code here is protected by the global "fftw" lock.
2119+ fftwf_plan my_plan = fftwf_plan_dft_r2c_3d(...); // Perform FFTW planning
2120+ // --- CRITICAL SECTION END ---
2121+
2122+ } // `fftw_lock` goes out of scope here, automatically releasing the lock.
2123+
2124+ // ... rest of the plugin logic ...
2125+ }
2126+
2127+
2128+ **Example for use from pure C-compatible **
2129+
2130+ Pure C plugins do not have access to C++ RAII. They must manually call ``avs_acquire_global_lock ``
2131+ and ``avs_release_global_lock ``, ensuring that every acquisition has a corresponding release.
2132+
2133+ ::
2134+
2135+ fftwf_plan my_plan = NULL;
2136+ int lock_acquired = 0; // Flag to track if lock was acquired
2137+
2138+ // Acquire the lock
2139+ lock_acquired = avs_acquire_global_lock(env, "fftw");
2140+ if (!lock_acquired) {
2141+ // Handle error: Return an error AVS_Value.
2142+ return avs_new_error(clip, "MyCFilter: Failed to acquire global FFTW planner lock!");
2143+ }
2144+
2145+ // --- CRITICAL SECTION START ---
2146+ // Code here is protected by the global "fftw" lock.
2147+ my_plan = fftwf_plan_dft_r2c_3d(...); // Perform FFTW planning
2148+ // --- CRITICAL SECTION END ---
2149+
2150+ // Release the lock manually
2151+ avs_release_global_lock(env, "fftw");
2152+
2153+
2154+
2155+ See also https://github.com/AviSynth/AviSynthPlus/issues/444
2156+ and https://www.fftw.org/doc/Thread-safety.html
2157+
2158+
18862159.. _cplusplus_pvideoframe :
18872160
18882161PVideoFrame
@@ -2446,4 +2719,4 @@ ____
24462719
24472720Back to :doc: `FilterSDK `
24482721
2449- $Date: 2025/02/24 13:53 :00 $
2722+ $Date: 2025/06/03 08:36 :00 $
0 commit comments