GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 36.6% 48 / 0 / 131
Functions: 60.0% 6 / 0 / 10
Branches: 19.6% 38 / 0 / 194

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