core/MTGuard.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Avisynth v2.6. Copyright 2002-2009 Ben Rudiak-Gould et al. | ||
| 2 | // http://avisynth.nl | ||
| 3 | |||
| 4 | // This program is free software; you can redistribute it and/or modify | ||
| 5 | // it under the terms of the GNU General Public License as published by | ||
| 6 | // the Free Software Foundation; either version 2 of the License, or | ||
| 7 | // (at your option) any later version. | ||
| 8 | // | ||
| 9 | // This program is distributed in the hope that it will be useful, | ||
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | // GNU General Public License for more details. | ||
| 13 | // | ||
| 14 | // You should have received a copy of the GNU General Public License | ||
| 15 | // along with this program; if not, write to the Free Software | ||
| 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit | ||
| 17 | // http://www.gnu.org/copyleft/gpl.html . | ||
| 18 | // | ||
| 19 | // Linking Avisynth statically or dynamically with other modules is making a | ||
| 20 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU | ||
| 21 | // General Public License cover the whole combination. | ||
| 22 | // | ||
| 23 | // As a special exception, the copyright holders of Avisynth give you | ||
| 24 | // permission to link Avisynth with independent modules that communicate with | ||
| 25 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license | ||
| 26 | // terms of these independent modules, and to copy and distribute the | ||
| 27 | // resulting combined work under terms of your choice, provided that | ||
| 28 | // every copy of the combined work is accompanied by a complete copy of | ||
| 29 | // the source code of Avisynth (the version of Avisynth used to produce the | ||
| 30 | // combined work), being distributed under the terms of the GNU General | ||
| 31 | // Public License plus this exception. An independent module is a module | ||
| 32 | // which is not derived from or based on Avisynth, such as 3rd-party filters, | ||
| 33 | // import and export plugins, or graphical user interfaces. | ||
| 34 | |||
| 35 | #include "MTGuard.h" | ||
| 36 | #include "cache.h" | ||
| 37 | #include "internal.h" | ||
| 38 | #include "FilterConstructor.h" | ||
| 39 | #include "InternalEnvironment.h" | ||
| 40 | #include <cassert> | ||
| 41 | #include <mutex> | ||
| 42 | |||
| 43 | #ifdef X86_32 | ||
| 44 | #include <mmintrin.h> | ||
| 45 | #endif | ||
| 46 | |||
| 47 | struct MTGuardChildFilter { | ||
| 48 | PClip filter; | ||
| 49 | std::mutex mutex; | ||
| 50 | }; | ||
| 51 | |||
| 52 | #ifdef AVS_WINDOWS | ||
| 53 | MTGuard::MTGuard(PClip firstChild, MtMode mtmode, std::unique_ptr<const FilterConstructor>&& funcCtor, const wchar_t* current_directory, InternalEnvironment* env) : | ||
| 54 | #else | ||
| 55 | 6 | MTGuard::MTGuard(PClip firstChild, MtMode mtmode, std::unique_ptr<const FilterConstructor>&& funcCtor, const char* current_directory, InternalEnvironment* env) : | |
| 56 | #endif | ||
| 57 | 6 | Env(env), | |
| 58 | 6 | nThreads(1), | |
| 59 | 6 | mt_enabled(false), | |
| 60 | 12 | FilterCtor(std::move(funcCtor)), | |
| 61 | #ifdef AVS_WINDOWS | ||
| 62 | CurrentDirectory(current_directory ? current_directory : L""), | ||
| 63 | #else | ||
| 64 |
2/4✓ Branch 9 → 10 taken 6 times.
✗ Branch 9 → 11 not taken.
✓ Branch 12 → 13 taken 6 times.
✗ Branch 12 → 35 not taken.
|
12 | CurrentDirectory(current_directory ? current_directory : ""), |
| 65 | #endif | ||
| 66 | 6 | MTMode(mtmode) | |
| 67 | { | ||
| 68 |
2/4✓ Branch 14 → 15 taken 6 times.
✗ Branch 14 → 17 not taken.
✓ Branch 15 → 16 taken 6 times.
✗ Branch 15 → 17 not taken.
|
6 | assert( ((int)mtmode > (int)MT_INVALID) && ((int)mtmode < (int)MT_MODE_COUNT) ); |
| 69 | |||
| 70 |
5/14✓ Branch 18 → 19 taken 6 times.
✗ Branch 18 → 46 not taken.
✓ Branch 20 → 21 taken 6 times.
✗ Branch 20 → 38 not taken.
✓ Branch 22 → 20 taken 6 times.
✓ Branch 22 → 23 taken 6 times.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 6 times.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 42 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
|
12 | ChildFilters = std::unique_ptr<MTGuardChildFilter[]>(new MTGuardChildFilter[1]); |
| 71 |
1/2✓ Branch 29 → 30 taken 6 times.
✗ Branch 29 → 47 not taken.
|
6 | ChildFilters[0].filter = firstChild; |
| 72 |
1/2✓ Branch 32 → 33 taken 6 times.
✗ Branch 32 → 47 not taken.
|
6 | vi = ChildFilters[0].filter->GetVideoInfo(); |
| 73 | |||
| 74 |
1/2✓ Branch 33 → 34 taken 6 times.
✗ Branch 33 → 47 not taken.
|
6 | Env->ManageCache(MC_RegisterMTGuard, reinterpret_cast<void*>(this)); |
| 75 | 6 | } | |
| 76 | |||
| 77 | 12 | MTGuard::~MTGuard() | |
| 78 | { | ||
| 79 | 6 | Env->ManageCache(MC_UnRegisterMTGuard, reinterpret_cast<void*>(this)); | |
| 80 | 12 | } | |
| 81 | |||
| 82 | ✗ | void MTGuard::EnableMT(size_t nThreads) | |
| 83 | { | ||
| 84 | // called for each filter in the chain, starting from the top-level filter | ||
| 85 | // even if their mt_enabled were set by an earlier Prefetch. | ||
| 86 | |||
| 87 | ✗ | assert(nThreads >= 1); | |
| 88 | |||
| 89 | ✗ | if (nThreads > 1) | |
| 90 | { | ||
| 91 | ✗ | switch (MTMode) | |
| 92 | { | ||
| 93 | ✗ | case MT_NICE_FILTER: | |
| 94 | { | ||
| 95 | // already created single instance, just set the thread count | ||
| 96 | ✗ | if (!this->mt_enabled) | |
| 97 | ✗ | ChildFilters[0].filter->SetCacheHints(CACHE_INFORM_NUM_THREADS, (int)nThreads); | |
| 98 | ✗ | break; | |
| 99 | } | ||
| 100 | ✗ | case MT_MULTI_INSTANCE: | |
| 101 | { | ||
| 102 | // creates the extra filter instances needed for the actual thread count | ||
| 103 | // set only when unset | ||
| 104 | ✗ | if (!this->mt_enabled) { | |
| 105 | ✗ | auto newchilds = std::unique_ptr<MTGuardChildFilter[]>(new MTGuardChildFilter[nThreads]); | |
| 106 | // copy existing | ||
| 107 | ✗ | for (size_t i = 0; i < this->nThreads; ++i) { | |
| 108 | ✗ | newchilds[i].filter = ChildFilters[i].filter; | |
| 109 | } | ||
| 110 | ✗ | if (CurrentDirectory.empty()) { | |
| 111 | // create the rest | ||
| 112 | ✗ | for (size_t i = this->nThreads; i < nThreads; ++i) { | |
| 113 | ✗ | newchilds[i].filter = FilterCtor->InstantiateFilter().AsClip(); | |
| 114 | } | ||
| 115 | } | ||
| 116 | else { | ||
| 117 | // switch to the original CurrentDirectory to instantiate the threaded filters | ||
| 118 | // in order to their constructor to be able to see the original current directory | ||
| 119 | ✗ | CWDChanger change_cwd(CurrentDirectory.c_str()); // wstring on Windows, string on Linux | |
| 120 | |||
| 121 | // create the rest | ||
| 122 | ✗ | for (size_t i = this->nThreads; i < nThreads; ++i) { | |
| 123 | ✗ | newchilds[i].filter = FilterCtor->InstantiateFilter().AsClip(); | |
| 124 | } | ||
| 125 | ✗ | } | |
| 126 | // inform all filter instances about the threading | ||
| 127 | ✗ | for (size_t i = 0; i < nThreads; ++i) | |
| 128 | ✗ | newchilds[i].filter->SetCacheHints(CACHE_INFORM_NUM_THREADS, (int)nThreads); | |
| 129 | ✗ | ChildFilters = std::move(newchilds); | |
| 130 | ✗ | } | |
| 131 | ✗ | break; | |
| 132 | } | ||
| 133 | ✗ | case MT_SERIALIZED: | |
| 134 | { | ||
| 135 | // already created single instance, just set the thread count | ||
| 136 | ✗ | if (!this->mt_enabled) | |
| 137 | ✗ | ChildFilters[0].filter->SetCacheHints(CACHE_INFORM_NUM_THREADS, 1); | |
| 138 | ✗ | break; | |
| 139 | } | ||
| 140 | ✗ | default: | |
| 141 | { | ||
| 142 | ✗ | assert(0); | |
| 143 | break; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | ✗ | else if (nThreads == 1) { | |
| 148 | ✗ | if (!this->mt_enabled) | |
| 149 | ✗ | ChildFilters[0].filter->SetCacheHints(CACHE_INFORM_NUM_THREADS, 1); | |
| 150 | } | ||
| 151 | |||
| 152 | ✗ | if (!this->mt_enabled) { | |
| 153 | ✗ | this->nThreads = std::max(this->nThreads, nThreads); | |
| 154 | ✗ | this->mt_enabled = true; | |
| 155 | } | ||
| 156 | |||
| 157 | // We don't need the stored parameters any more, | ||
| 158 | // free their memory. | ||
| 159 | //FilterCtor.reset(); | ||
| 160 | ✗ | } | |
| 161 | |||
| 162 | 5 | PVideoFrame __stdcall MTGuard::GetFrame(int n, IScriptEnvironment* env_) | |
| 163 | { | ||
| 164 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5 times.
|
5 | assert(nThreads > 0); |
| 165 | /* | ||
| 166 | // We can't call child filter without mutex guards even when nThreads is 1, | ||
| 167 | // because a lately invoked filter may call GetFrame again, see | ||
| 168 | // MT_SERIALIZED considerations below. | ||
| 169 | // Code left here intentionally but commented out. | ||
| 170 | if (nThreads == 1) | ||
| 171 | return ChildFilters[0].filter->GetFrame(n, env); | ||
| 172 | */ | ||
| 173 | 5 | InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_); | |
| 174 | 5 | IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv); | |
| 175 | |||
| 176 | 5 | PVideoFrame frame = NULL; | |
| 177 | |||
| 178 |
1/4✗ Branch 6 → 7 not taken.
✓ Branch 6 → 13 taken 5 times.
✗ Branch 6 → 23 not taken.
✗ Branch 6 → 33 not taken.
|
5 | switch (MTMode) |
| 179 | { | ||
| 180 | ✗ | case MT_NICE_FILTER: | |
| 181 | { | ||
| 182 | ✗ | frame = ChildFilters[0].filter->GetFrame(n, env); | |
| 183 | ✗ | break; | |
| 184 | } | ||
| 185 | 5 | case MT_MULTI_INSTANCE: | |
| 186 | { | ||
| 187 | // When called from thread pool then thread IDs are one-to-one mapped to ChildFilters array. | ||
| 188 | // 'modulo' method ensures that no over-addressing can happen. | ||
| 189 | // The number of filter instances are created in EnableMT as such. | ||
| 190 | // Thread pool thread IDs are consecutive numbers, starting with 1. | ||
| 191 | // Prefetch threads are consecutive numbers, starting with 0. | ||
| 192 | // Here we cannot differentiate from where was the filter called. | ||
| 193 | // Note: | ||
| 194 | // when called from Prefetcher (which has 'num_of_logical_processors' threads instead of nThreads) | ||
| 195 | // the mapping is not ideal. It can be less or more than the actual instance count. | ||
| 196 | // E.g. when mapping a thread on a CPU with 8 logical cores | ||
| 197 | // to a instance count of 3 (nThread=3) the mapping is uneven (modulo example): [0,3,6]->[0] [1,4,7]->[1] [2,5]->[2] | ||
| 198 |
1/2✓ Branch 13 → 14 taken 5 times.
✗ Branch 13 → 44 not taken.
|
5 | size_t clipIndex = IEnv->GetThreadId() % nThreads; |
| 199 | 5 | auto& child = ChildFilters[clipIndex]; | |
| 200 |
1/2✓ Branch 15 → 16 taken 5 times.
✗ Branch 15 → 44 not taken.
|
5 | std::lock_guard<std::mutex> lock(child.mutex); |
| 201 |
3/4✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 41 taken 1 time.
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 39 not taken.
|
5 | frame = child.filter->GetFrame(n, env); |
| 202 | 4 | break; | |
| 203 | 5 | } | |
| 204 | ✗ | case MT_SERIALIZED: | |
| 205 | { | ||
| 206 | ✗ | std::lock_guard<std::mutex> lock(ChildFilters[0].mutex); | |
| 207 | // Deadlock situation when GetFrame(0) was called from an Invoke | ||
| 208 | // solved in 3.7.2 by separating memory_mutex from invoke_mutex. | ||
| 209 | ✗ | frame = ChildFilters[0].filter->GetFrame(n, env); | |
| 210 | ✗ | break; | |
| 211 | ✗ | } | |
| 212 | ✗ | default: | |
| 213 | { | ||
| 214 | ✗ | assert(0); | |
| 215 | env->ThrowError("Invalid Avisynth logic."); | ||
| 216 | break; | ||
| 217 | } | ||
| 218 | } // switch | ||
| 219 | |||
| 220 | #ifdef X86_32 | ||
| 221 | _mm_empty(); | ||
| 222 | #endif | ||
| 223 | |||
| 224 | 4 | return frame; | |
| 225 | 1 | } | |
| 226 | |||
| 227 | ✗ | void __stdcall MTGuard::GetAudio(void* buf, int64_t start, int64_t count, IScriptEnvironment* env_) | |
| 228 | { | ||
| 229 | ✗ | assert(nThreads > 0); | |
| 230 | |||
| 231 | /* commented out, see MTGuard::GetFrame comments for MT_SERIALIZED | ||
| 232 | if (nThreads == 1) | ||
| 233 | { | ||
| 234 | ChildFilters[0].filter->GetAudio(buf, start, count, env); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | */ | ||
| 238 | ✗ | InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_); | |
| 239 | ✗ | IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv); | |
| 240 | |||
| 241 | |||
| 242 | ✗ | switch (MTMode) | |
| 243 | { | ||
| 244 | ✗ | case MT_NICE_FILTER: | |
| 245 | { | ||
| 246 | ✗ | ChildFilters[0].filter->GetAudio(buf, start, count, env); | |
| 247 | ✗ | break; | |
| 248 | } | ||
| 249 | ✗ | case MT_MULTI_INSTANCE: | |
| 250 | { | ||
| 251 | ✗ | size_t clipIndex = IEnv->GetThreadId() % nThreads; | |
| 252 | ✗ | auto& child = ChildFilters[clipIndex]; | |
| 253 | ✗ | std::lock_guard<std::mutex> lock(child.mutex); | |
| 254 | ✗ | child.filter->GetAudio(buf, start, count, env); | |
| 255 | ✗ | break; | |
| 256 | ✗ | } | |
| 257 | ✗ | case MT_SERIALIZED: | |
| 258 | { | ||
| 259 | ✗ | std::lock_guard<std::mutex> lock(ChildFilters[0].mutex); | |
| 260 | ✗ | ChildFilters[0].filter->GetAudio(buf, start, count, env); | |
| 261 | ✗ | break; | |
| 262 | ✗ | } | |
| 263 | ✗ | default: | |
| 264 | { | ||
| 265 | ✗ | assert(0); | |
| 266 | env->ThrowError("Invalid Avisynth logic."); | ||
| 267 | break; | ||
| 268 | } | ||
| 269 | } // switch | ||
| 270 | |||
| 271 | #ifdef X86_32 | ||
| 272 | _mm_empty(); | ||
| 273 | #endif | ||
| 274 | ✗ | } | |
| 275 | |||
| 276 | 11 | const VideoInfo& __stdcall MTGuard::GetVideoInfo() | |
| 277 | { | ||
| 278 | 11 | return vi; | |
| 279 | } | ||
| 280 | |||
| 281 | ✗ | bool __stdcall MTGuard::GetParity(int n) | |
| 282 | { | ||
| 283 | ✗ | return ChildFilters[0].filter->GetParity(n); | |
| 284 | } | ||
| 285 | |||
| 286 | 31 | int __stdcall MTGuard::SetCacheHints(int cachehints, int frame_range) | |
| 287 | { | ||
| 288 | AVS_UNUSED(frame_range); | ||
| 289 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 31 times.
|
31 | if (CACHE_GET_MTMODE == cachehints) { |
| 290 | ✗ | return MT_NICE_FILTER; | |
| 291 | } | ||
| 292 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 31 times.
|
31 | if (CACHE_IS_MTGUARD_REQ == cachehints) { |
| 293 | ✗ | return CACHE_IS_MTGUARD_ANS; | |
| 294 | } | ||
| 295 | // Filter-MTGuard-CacheGuard(Cache or nothing) | ||
| 296 | // MTGuard is just a helper over real filters, we pass these requests upstream | ||
| 297 |
4/6✓ Branch 6 → 7 taken 31 times.
✗ Branch 6 → 10 not taken.
✓ Branch 7 → 8 taken 31 times.
✗ Branch 7 → 10 not taken.
✓ Branch 8 → 9 taken 26 times.
✓ Branch 8 → 10 taken 5 times.
|
31 | if (CACHE_GET_AUDIO_POLICY == cachehints || CACHE_GET_AUDIO_SIZE == cachehints || |
| 298 |
2/2✓ Branch 9 → 10 taken 5 times.
✓ Branch 9 → 20 taken 21 times.
|
26 | CACHE_GETCHILD_AUDIO_MODE == cachehints || CACHE_GETCHILD_AUDIO_SIZE == cachehints) |
| 299 |
1/2✓ Branch 13 → 14 taken 10 times.
✗ Branch 13 → 18 not taken.
|
10 | return (ChildFilters[0].filter->GetVersion() >= 5) ? ChildFilters[0].filter->SetCacheHints(cachehints, 0) : 0; |
| 300 |
4/4✓ Branch 20 → 21 taken 13 times.
✓ Branch 20 → 22 taken 8 times.
✓ Branch 21 → 22 taken 7 times.
✓ Branch 21 → 32 taken 6 times.
|
21 | if (CACHE_GET_DEV_TYPE == cachehints || CACHE_GET_CHILD_DEV_TYPE == cachehints) { |
| 301 |
1/2✓ Branch 25 → 26 taken 15 times.
✗ Branch 25 → 30 not taken.
|
15 | return (ChildFilters[0].filter->GetVersion() >= 5) ? ChildFilters[0].filter->SetCacheHints(cachehints, 0) : 0; |
| 302 | } | ||
| 303 | |||
| 304 | 6 | return 0; | |
| 305 | } | ||
| 306 | |||
| 307 | ✗ | bool __stdcall MTGuard::IsMTGuard(const PClip& p) | |
| 308 | { | ||
| 309 | ✗ | return ((p->GetVersion() >= 5) && (p->SetCacheHints(CACHE_IS_MTGUARD_REQ, 0) == CACHE_IS_MTGUARD_ANS)); | |
| 310 | } | ||
| 311 | |||
| 312 | #ifdef AVS_WINDOWS | ||
| 313 | PClip MTGuard::Create(MtMode mode, PClip filterInstance, std::unique_ptr<const FilterConstructor> funcCtor, const wchar_t* current_directory, InternalEnvironment* env) | ||
| 314 | #else | ||
| 315 | 6 | PClip MTGuard::Create(MtMode mode, PClip filterInstance, std::unique_ptr<const FilterConstructor> funcCtor, const char* current_directory, InternalEnvironment* env) | |
| 316 | #endif | ||
| 317 | { | ||
| 318 |
1/4✗ Branch 2 → 3 not taken.
✓ Branch 2 → 13 taken 6 times.
✗ Branch 2 → 23 not taken.
✗ Branch 2 → 33 not taken.
|
6 | switch (mode) |
| 319 | { | ||
| 320 | ✗ | case MT_NICE_FILTER: | |
| 321 | { | ||
| 322 | // Put a guard even around MT_NICE_FILTER mode filters, in order EnableMT to | ||
| 323 | // be able to inform the filter (CACHE_SET_NUM_OF_THREAD) about the actual (Prefetch) thread count. | ||
| 324 | ✗ | return new MTGuard(filterInstance, mode, nullptr, nullptr, env); | |
| 325 | } | ||
| 326 | 6 | case MT_MULTI_INSTANCE: // Fall-through intentional | |
| 327 | { | ||
| 328 |
4/10✓ Branch 16 → 17 taken 6 times.
✗ Branch 16 → 46 not taken.
✓ Branch 17 → 18 taken 6 times.
✗ Branch 17 → 44 not taken.
✓ Branch 18 → 19 taken 6 times.
✗ Branch 18 → 44 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 6 times.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
|
12 | return new MTGuard(filterInstance, mode, std::move(funcCtor), current_directory, env); |
| 329 | // args2 and args3 are not valid after this point anymore | ||
| 330 | } | ||
| 331 | ✗ | case MT_SERIALIZED: | |
| 332 | { | ||
| 333 | // Put a guard even around MT_SERIALIZED mode filters, in order EnableMT to | ||
| 334 | // be able to inform the filter (CACHE_SET_NUM_OF_THREAD) about the actual | ||
| 335 | // (1) thread count. | ||
| 336 | ✗ | return new MTGuard(filterInstance, mode, nullptr, nullptr, env); | |
| 337 | // args2 and args3 are not valid after this point anymore | ||
| 338 | } | ||
| 339 | ✗ | default: | |
| 340 | // There are broken plugins out there in the wild that have (GetVersion() >= 5), but still | ||
| 341 | // return garbage for SetCacheHints(). However, this case should be recognized and | ||
| 342 | // handled earlier, so we can never get to this default-branch. If we do, assume the worst. | ||
| 343 | ✗ | assert(0); | |
| 344 | return new MTGuard(filterInstance, MT_SERIALIZED, nullptr, nullptr, env); | ||
| 345 | } | ||
| 346 | } | ||
| 347 |