GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/catalog_traversal_parallel.h
Date: 2026-10-04 02:40:33
Exec Total Coverage
Lines: 187 199 94.0%
Branches: 160 244 65.6%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_CATALOG_TRAVERSAL_PARALLEL_H_
6 #define CVMFS_CATALOG_TRAVERSAL_PARALLEL_H_
7
8 #include <stack>
9 #include <string>
10 #include <vector>
11
12 #include "catalog_traversal.h"
13 #include "util/atomic.h"
14 #include "util/exception.h"
15 #include "util/tube.h"
16
17 namespace swissknife {
18
19 /**
20 * This class implements the same functionality as CatalogTraversal, but in
21 * parallel. For common functionality, see the documentation of
22 * CatalogTraversal. Differences:
23 * - can choose number of threads
24 * - traversal types change meaning:
25 * - depth-first -> parallelized post-order traversal (parents are processed
26 * after all children are finished)
27 * - breadth-first -> same as original, but parallelized
28 */
29 template<class ObjectFetcherT>
30 class CatalogTraversalParallel : public CatalogTraversalBase<ObjectFetcherT> {
31 public:
32 typedef CatalogTraversalBase<ObjectFetcherT> Base;
33 typedef ObjectFetcherT ObjectFetcherTN;
34 typedef typename ObjectFetcherT::CatalogTN CatalogTN;
35 typedef typename ObjectFetcherT::HistoryTN HistoryTN;
36 typedef CatalogTraversalData<CatalogTN> CallbackDataTN;
37 typedef typename CatalogTN::NestedCatalogList NestedCatalogList;
38 typedef typename Base::Parameters Parameters;
39 typedef typename Base::TraversalType TraversalType;
40 typedef std::vector<shash::Any> HashList;
41
42 1190 explicit CatalogTraversalParallel(const Parameters &params)
43 : CatalogTraversalBase<ObjectFetcherT>(params)
44 1190 , num_threads_(params.num_threads)
45
4/8
✓ Branch 2 taken 1190 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1190 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1190 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1190 times.
✗ Branch 12 not taken.
1190 , serialize_callbacks_(params.serialize_callbacks) {
46 1190 atomic_init32(&num_errors_);
47
1/2
✓ Branch 1 taken 1190 times.
✗ Branch 2 not taken.
1190 shash::Any null_hash;
48 1190 null_hash.SetNull();
49
1/2
✓ Branch 1 taken 1190 times.
✗ Branch 2 not taken.
1190 catalogs_processing_.Init(1024, null_hash, hasher);
50
1/2
✓ Branch 1 taken 1190 times.
✗ Branch 2 not taken.
1190 catalogs_done_.Init(1024, null_hash, hasher);
51 1190 pthread_mutex_init(&catalog_callback_lock_, NULL);
52 1190 pthread_mutex_init(&catalogs_lock_, NULL);
53 1190 effective_history_depth_ = this->default_history_depth_;
54 1190 effective_timestamp_threshold_ = this->default_timestamp_threshold_;
55 1190 }
56
57 protected:
58 struct CatalogJob : public CatalogTraversal<ObjectFetcherT>::CatalogJob,
59 public Observable<int> {
60 4429580 explicit CatalogJob(const std::string &path,
61 const shash::Any &hash,
62 const unsigned tree_level,
63 const uint64_t history_depth,
64 CatalogTN *parent = NULL)
65 : CatalogTraversal<ObjectFetcherT>::CatalogJob(path, hash, tree_level,
66 4429580 history_depth, parent) {
67 4429580 atomic_init32(&children_unprocessed);
68 4429580 }
69
70
1/2
✓ Branch 1 taken 2205652 times.
✗ Branch 2 not taken.
2205940 void WakeParents() { this->NotifyListeners(0); }
71
72 atomic_int32 children_unprocessed;
73 };
74
75 public:
76 /**
77 * Starts the traversal process.
78 * After calling this methods CatalogTraversal will go through all catalogs
79 * and call the registered callback methods for each found catalog.
80 * If something goes wrong in the process, the traversal will be cancelled.
81 *
82 * @return true, when all catalogs were successfully processed. On
83 * failure the traversal is cancelled and false is returned.
84 */
85 963 bool Traverse(const TraversalType type = Base::kBreadthFirst) {
86
1/2
✓ Branch 1 taken 963 times.
✗ Branch 2 not taken.
963 const shash::Any root_catalog_hash = this->GetRepositoryRootCatalogHash();
87
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 963 times.
963 if (root_catalog_hash.IsNull()) {
88 ✗ return false;
89 }
90
1/2
✓ Branch 1 taken 963 times.
✗ Branch 2 not taken.
963 return Traverse(root_catalog_hash, type);
91 }
92
93 /**
94 * Starts the traversal process at the catalog pointed to by the given hash
95 *
96 * @param root_catalog_hash the entry point into the catalog traversal
97 * @return true when catalogs were successfully traversed
98 */
99 1143 bool Traverse(const shash::Any &root_catalog_hash,
100 const TraversalType type = Base::kBreadthFirst) {
101 // add the root catalog of the repository as the first element on the job
102 // stack
103 2286 if (this->no_repeat_history_
104
4/6
✓ Branch 0 taken 879 times.
✓ Branch 1 taken 264 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 879 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1143 times.
1143 && catalogs_done_.Contains(root_catalog_hash)) {
105 ✗ return true;
106 }
107 1143 effective_traversal_type_ = type;
108
3/6
✓ Branch 2 taken 1143 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1143 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1143 times.
✗ Branch 9 not taken.
1143 CatalogJob *root_job = new CatalogJob("", root_catalog_hash, 0, 0);
109 1143 PushJob(root_job);
110 1143 return DoTraverse();
111 }
112
113 /**
114 * Start the traversal process from a list of root catalogs. Same as
115 * TraverseRevision function, TraverseList does not traverse into predecessor
116 * catalog revisions and ignores TraversalParameter settings.
117 */
118 1554 bool TraverseList(const HashList &root_catalog_list,
119 const TraversalType type = Base::kBreadthFirst) {
120 // Push in reverse order for CatalogTraversal-like behavior
121 1554 HashList::const_reverse_iterator i = root_catalog_list.rbegin();
122 1554 const HashList::const_reverse_iterator iend = root_catalog_list.rend();
123 1554 bool has_pushed = false;
124 {
125 1554 MutexLockGuard const m(&catalogs_lock_);
126
3/4
✓ Branch 2 taken 4571 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3017 times.
✓ Branch 5 taken 1554 times.
4571 for (; i != iend; ++i) {
127
7/8
✓ Branch 0 taken 2909 times.
✓ Branch 1 taken 108 times.
✓ Branch 4 taken 2909 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1190 times.
✓ Branch 7 taken 1719 times.
✓ Branch 8 taken 1190 times.
✓ Branch 9 taken 1827 times.
3017 if (this->no_repeat_history_ && catalogs_done_.Contains(*i)) {
128 1190 continue;
129 }
130
131
3/6
✓ Branch 2 taken 1827 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1827 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1827 times.
✗ Branch 10 not taken.
1827 CatalogJob *root_job = new CatalogJob("", *i, 0, 0);
132
1/2
✓ Branch 1 taken 1827 times.
✗ Branch 2 not taken.
1827 PushJobUnlocked(root_job);
133 1827 has_pushed = true;
134 }
135 1554 }
136 // noop: no catalogs to traverse
137
2/2
✓ Branch 0 taken 455 times.
✓ Branch 1 taken 1099 times.
1554 if (!has_pushed) {
138 455 return true;
139 }
140 1099 effective_traversal_type_ = type;
141 1099 effective_history_depth_ = Parameters::kNoHistory;
142 1099 effective_timestamp_threshold_ = Parameters::kNoTimestampThreshold;
143
1/2
✓ Branch 1 taken 1099 times.
✗ Branch 2 not taken.
1099 bool const result = DoTraverse();
144 1099 effective_history_depth_ = this->default_history_depth_;
145 1099 effective_timestamp_threshold_ = this->default_timestamp_threshold_;
146 1099 return result;
147 }
148
149 /**
150 * Starts the traversal process at the catalog pointed to by the given hash
151 * but doesn't traverse into predecessor catalog revisions. This overrides the
152 * TraversalParameter settings provided at construction.
153 *
154 * @param root_catalog_hash the entry point into the catalog traversal
155 * @return true when catalogs were successfully traversed
156 */
157 24 bool TraverseRevision(const shash::Any &root_catalog_hash,
158 const TraversalType type = Base::kBreadthFirst) {
159 24 effective_history_depth_ = Parameters::kNoHistory;
160 24 effective_timestamp_threshold_ = Parameters::kNoTimestampThreshold;
161 24 bool const result = Traverse(root_catalog_hash, type);
162 24 effective_history_depth_ = this->default_history_depth_;
163 24 effective_timestamp_threshold_ = this->default_timestamp_threshold_;
164 24 return result;
165 }
166
167 protected:
168 20225350 static uint32_t hasher(const shash::Any &key) {
169 // Don't start with the first bytes, because == is using them as well
170 return static_cast<uint32_t>(
171 20225350 *(reinterpret_cast<const uint32_t *>(key.digest) + 1));
172 }
173
174 2242 bool DoTraverse() {
175 // Optimal number of threads is yet to be determined. The main event loop
176 // contains a spin-lock, so it should not be more than number of cores.
177 2242 threads_process_ = reinterpret_cast<pthread_t *>(
178 2242 smalloc(sizeof(pthread_t) * num_threads_));
179
2/2
✓ Branch 0 taken 2410 times.
✓ Branch 1 taken 2242 times.
4652 for (unsigned int i = 0; i < num_threads_; ++i) {
180 2410 int const retval = pthread_create(&threads_process_[i], NULL, MainProcessQueue,
181 this);
182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2410 times.
2410 if (retval != 0)
183 ✗ PANIC(kLogStderr, "failed to create thread");
184 }
185
186
2/2
✓ Branch 0 taken 2410 times.
✓ Branch 1 taken 2242 times.
4652 for (unsigned int i = 0; i < num_threads_; ++i) {
187 2410 int const retval = pthread_join(threads_process_[i], NULL);
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2410 times.
2410 assert(retval == 0);
189 }
190 2242 free(threads_process_);
191
192
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2230 times.
2242 if (atomic_read32(&num_errors_))
193 12 return false;
194
195
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2230 times.
2230 assert(catalogs_processing_.size() == 0);
196
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2230 times.
2230 assert(pre_job_queue_.IsEmpty());
197
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2230 times.
2230 assert(post_job_queue_.IsEmpty());
198 2230 return true;
199 }
200
201 2410 static void *MainProcessQueue(void *data) {
202 2410 CatalogTraversalParallel<ObjectFetcherT> *traversal = reinterpret_cast<
203 CatalogTraversalParallel<ObjectFetcherT> *>(data);
204 CatalogJob *current_job;
205 while (true) {
206 4836209 current_job = traversal->post_job_queue_.TryPopFront();
207
2/2
✓ Branch 0 taken 408393 times.
✓ Branch 1 taken 4429436 times.
4837829 if (current_job != NULL) {
208 408393 traversal->ProcessJobPost(current_job);
209 } else {
210 4429436 current_job = traversal->pre_job_queue_.PopFront();
211 // NULL means the master thread tells us to finish
212
2/2
✓ Branch 1 taken 2410 times.
✓ Branch 2 taken 4427062 times.
4429508 if (current_job->hash.IsNull()) {
213
1/2
✓ Branch 0 taken 2410 times.
✗ Branch 1 not taken.
2410 delete current_job;
214 2410 break;
215 }
216 4427062 traversal->ProcessJobPre(current_job);
217 }
218 }
219 2410 return NULL;
220 }
221
222 2242 void NotifyFinished() {
223
1/2
✓ Branch 1 taken 2242 times.
✗ Branch 2 not taken.
2242 shash::Any null_hash;
224 2242 null_hash.SetNull();
225
2/2
✓ Branch 0 taken 2410 times.
✓ Branch 1 taken 2242 times.
4652 for (unsigned i = 0; i < num_threads_; ++i) {
226
3/6
✓ Branch 2 taken 2410 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2410 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2410 times.
✗ Branch 9 not taken.
2410 CatalogJob *job = new CatalogJob("", null_hash, 0, 0);
227
1/2
✓ Branch 1 taken 2410 times.
✗ Branch 2 not taken.
2410 pre_job_queue_.EnqueueFront(job);
228 }
229 2242 }
230
231 1143 void PushJob(CatalogJob *job) {
232 1143 MutexLockGuard const m(&catalogs_lock_);
233
1/2
✓ Branch 1 taken 1143 times.
✗ Branch 2 not taken.
1143 PushJobUnlocked(job);
234 1143 }
235
236 4427170 void PushJobUnlocked(CatalogJob *job) {
237 4427170 catalogs_processing_.Insert(job->hash, job);
238 4427170 pre_job_queue_.EnqueueFront(job);
239 4427170 }
240
241 4426990 void ProcessJobPre(CatalogJob *job) {
242
4/6
✓ Branch 0 taken 4426990 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4425850 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 4425838 times.
4426990 if (!this->PrepareCatalog(job)) {
243 12 atomic_inc32(&num_errors_);
244
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 NotifyFinished();
245 4017361 return;
246 }
247
2/2
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 4425697 times.
4425838 if (job->ignore) {
248
1/2
✓ Branch 1 taken 141 times.
✗ Branch 2 not taken.
141 FinalizeJob(job);
249 141 return;
250 }
251
1/2
✓ Branch 1 taken 4416481 times.
✗ Branch 2 not taken.
4425697 NestedCatalogList const catalog_list = job->catalog->ListOwnNestedCatalogs();
252 unsigned int num_children;
253 // Ensure that pushed children won't call ProcessJobPost on this job
254 // before this function finishes
255 {
256 4416481 MutexLockGuard const m(&catalogs_lock_);
257
2/2
✓ Branch 0 taken 2221063 times.
✓ Branch 1 taken 2205942 times.
4427005 if (effective_traversal_type_ == Base::kBreadthFirst) {
258
1/2
✓ Branch 1 taken 2221063 times.
✗ Branch 2 not taken.
2221063 num_children = PushPreviousRevision(job)
259
1/2
✓ Branch 1 taken 2221063 times.
✗ Branch 2 not taken.
2221063 + PushNestedCatalogs(job, catalog_list);
260 } else {
261
1/2
✓ Branch 1 taken 2205942 times.
✗ Branch 2 not taken.
2205942 num_children = PushNestedCatalogs(job, catalog_list)
262
1/2
✓ Branch 1 taken 2205942 times.
✗ Branch 2 not taken.
2205942 + PushPreviousRevision(job);
263 2205942 atomic_write32(&job->children_unprocessed, num_children);
264 }
265
3/6
✓ Branch 0 taken 4427005 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4427005 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4427005 times.
4427005 if (!this->CloseCatalog(false, job)) {
266 ✗ atomic_inc32(&num_errors_);
267 ✗ NotifyFinished();
268 }
269 4427005 }
270
271 // breadth-first: can post-process immediately
272 // depth-first: no children -> can post-process immediately
273
4/4
✓ Branch 0 taken 2205906 times.
✓ Branch 1 taken 2220979 times.
✓ Branch 2 taken 1797501 times.
✓ Branch 3 taken 408405 times.
4426885 if (effective_traversal_type_ == Base::kBreadthFirst || num_children == 0) {
274
1/2
✓ Branch 1 taken 4017076 times.
✗ Branch 2 not taken.
4018480 ProcessJobPost(job);
275 4017076 return;
276 }
277
2/2
✓ Branch 1 taken 408405 times.
✓ Branch 2 taken 4017208 times.
4425481 }
278
279 4427005 unsigned int PushNestedCatalogs(CatalogJob *job,
280 const NestedCatalogList &catalog_list) {
281 4427005 typename NestedCatalogList::const_iterator i = catalog_list.begin();
282 4427005 typename NestedCatalogList::const_iterator const iend = catalog_list.end();
283 4427005 unsigned int num_children = 0;
284
2/2
✓ Branch 2 taken 4423854 times.
✓ Branch 3 taken 4427005 times.
8850859 for (; i != iend; ++i) {
285
7/8
✓ Branch 0 taken 12750 times.
✓ Branch 1 taken 4411104 times.
✓ Branch 4 taken 12750 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1080 times.
✓ Branch 7 taken 11670 times.
✓ Branch 8 taken 1080 times.
✓ Branch 9 taken 4422774 times.
4423854 if (this->no_repeat_history_ && catalogs_done_.Contains(i->hash)) {
286 1080 continue;
287 }
288
289 CatalogJob *child;
290 8845548 if (!this->no_repeat_history_
291
7/8
✓ Branch 0 taken 11670 times.
✓ Branch 1 taken 4411104 times.
✓ Branch 4 taken 11670 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11574 times.
✓ Branch 7 taken 96 times.
✓ Branch 8 taken 4422678 times.
✓ Branch 9 taken 96 times.
4422774 || !catalogs_processing_.Lookup(i->hash, &child)) {
292
2/2
✓ Branch 0 taken 4399644 times.
✓ Branch 1 taken 23034 times.
4422678 CatalogTN *parent = (this->no_close_) ? job->catalog : NULL;
293
1/2
✓ Branch 2 taken 4422678 times.
✗ Branch 3 not taken.
8845356 child = new CatalogJob(i->mountpoint.ToString(),
294
1/2
✓ Branch 2 taken 4422678 times.
✗ Branch 3 not taken.
4422678 i->hash,
295 4422678 job->tree_level + 1,
296
1/2
✓ Branch 1 taken 4422678 times.
✗ Branch 2 not taken.
4422678 job->history_depth,
297 parent);
298
1/2
✓ Branch 1 taken 4422678 times.
✗ Branch 2 not taken.
4422678 PushJobUnlocked(child);
299 }
300
301
2/2
✓ Branch 0 taken 2204886 times.
✓ Branch 1 taken 2217888 times.
4422774 if (effective_traversal_type_ == Base::kDepthFirst) {
302
1/2
✓ Branch 1 taken 2204886 times.
✗ Branch 2 not taken.
2204886 child->RegisterListener(&CatalogTraversalParallel::OnChildFinished,
303 this, job);
304 }
305 4422774 ++num_children;
306 }
307 4427005 return num_children;
308 }
309
310 /**
311 * Pushes the previous revision of a root catalog.
312 * @return the number of catalogs pushed on the processing stack
313 */
314 4427005 unsigned int PushPreviousRevision(CatalogJob *job) {
315 // only root catalogs are used for entering a previous revision (graph)
316
2/2
✓ Branch 1 taken 4422619 times.
✓ Branch 2 taken 4386 times.
4427005 if (!job->catalog->IsRoot()) {
317 4422619 return 0;
318 }
319
320
1/2
✓ Branch 1 taken 4386 times.
✗ Branch 2 not taken.
4386 const shash::Any previous_revision = job->catalog->GetPreviousRevision();
321
2/2
✓ Branch 1 taken 504 times.
✓ Branch 2 taken 3882 times.
4386 if (previous_revision.IsNull()) {
322 504 return 0;
323 }
324
325 // check if the next deeper history level is actually requested
326
3/4
✓ Branch 1 taken 3882 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2360 times.
✓ Branch 4 taken 1522 times.
3882 if (this->IsBelowPruningThresholds(*job, effective_history_depth_,
327 effective_timestamp_threshold_)) {
328 2360 return 0;
329 }
330
331 3044 if (this->no_repeat_history_
332
5/8
✓ Branch 0 taken 1186 times.
✓ Branch 1 taken 336 times.
✓ Branch 3 taken 1186 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1186 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1522 times.
1522 && catalogs_done_.Contains(previous_revision)) {
333 ✗ return 0;
334 }
335
336 CatalogJob *prev_job;
337 3044 if (!this->no_repeat_history_
338
5/8
✓ Branch 0 taken 1186 times.
✓ Branch 1 taken 336 times.
✓ Branch 3 taken 1186 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1186 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1522 times.
✗ Branch 8 not taken.
1522 || !catalogs_processing_.Lookup(previous_revision, &prev_job)) {
339
2/4
✓ Branch 2 taken 1522 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1522 times.
✗ Branch 6 not taken.
3044 prev_job = new CatalogJob("", previous_revision, 0,
340
1/2
✓ Branch 1 taken 1522 times.
✗ Branch 2 not taken.
1522 job->history_depth + 1);
341
1/2
✓ Branch 1 taken 1522 times.
✗ Branch 2 not taken.
1522 PushJobUnlocked(prev_job);
342 }
343
344
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 1342 times.
1522 if (effective_traversal_type_ == Base::kDepthFirst) {
345
1/2
✓ Branch 1 taken 180 times.
✗ Branch 2 not taken.
180 prev_job->RegisterListener(&CatalogTraversalParallel::OnChildFinished,
346 this, job);
347 }
348 1522 return 1;
349 }
350
351 4426417 void ProcessJobPost(CatalogJob *job) {
352 // Save time by keeping catalog open when suitable
353
1/2
✓ Branch 0 taken 4426441 times.
✗ Branch 1 not taken.
4426417 if (job->catalog == NULL) {
354
3/4
✓ Branch 0 taken 4426453 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 360 times.
✓ Branch 4 taken 4412221 times.
4426441 if (!this->ReopenCatalog(job)) {
355 360 atomic_inc32(&num_errors_);
356 ✗ NotifyFinished();
357 ✗ return;
358 }
359 }
360
1/2
✓ Branch 0 taken 4412197 times.
✗ Branch 1 not taken.
4412197 if (serialize_callbacks_) {
361 4412197 MutexLockGuard const m(&catalog_callback_lock_);
362
2/4
✓ Branch 1 taken 4427005 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4427005 times.
✗ Branch 5 not taken.
4427005 this->NotifyListeners(job->GetCallbackData());
363 4427005 } else {
364 ✗ this->NotifyListeners(job->GetCallbackData());
365 }
366
2/2
✓ Branch 0 taken 27301 times.
✓ Branch 1 taken 4399692 times.
4426993 if (!this->no_close_) {
367
2/4
✓ Branch 0 taken 27301 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 27301 times.
27301 if (!this->CloseCatalog(true, job)) {
368 ✗ atomic_inc32(&num_errors_);
369 ✗ NotifyFinished();
370 ✗ return;
371 }
372 }
373 4426993 FinalizeJob(job);
374 }
375
376 4427074 void FinalizeJob(CatalogJob *job) {
377 {
378 4427074 MutexLockGuard const m(&catalogs_lock_);
379
1/2
✓ Branch 1 taken 4427146 times.
✗ Branch 2 not taken.
4427146 catalogs_processing_.Erase(job->hash);
380
1/2
✓ Branch 1 taken 4427146 times.
✗ Branch 2 not taken.
4427146 catalogs_done_.Insert(job->hash, true);
381 // No more catalogs to process -> finish
382
1/2
✓ Branch 2 taken 2230 times.
✗ Branch 3 not taken.
4429376 if (catalogs_processing_.size() == 0 && pre_job_queue_.IsEmpty()
383
5/6
✓ Branch 0 taken 2230 times.
✓ Branch 1 taken 4424916 times.
✓ Branch 3 taken 2230 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2230 times.
✓ Branch 6 taken 4424916 times.
4429376 && post_job_queue_.IsEmpty()) {
384
1/2
✓ Branch 1 taken 2230 times.
✗ Branch 2 not taken.
2230 NotifyFinished();
385 }
386 4427146 }
387
2/2
✓ Branch 0 taken 2205952 times.
✓ Branch 1 taken 2221170 times.
4427122 if (effective_traversal_type_ == Base::kDepthFirst) {
388 2205952 job->WakeParents();
389 }
390
2/2
✓ Branch 0 taken 4426534 times.
✓ Branch 1 taken 132 times.
4426666 delete job;
391 4425958 }
392
393 2204610 void OnChildFinished(const int &a, CatalogJob *job) {
394 // atomic_xadd32 returns value before subtraction -> needs to equal 1
395
2/2
✓ Branch 1 taken 408405 times.
✓ Branch 2 taken 1796601 times.
2204610 if (atomic_xadd32(&job->children_unprocessed, -1) == 1) {
396 408405 post_job_queue_.EnqueueFront(job);
397 }
398 2205006 }
399
400 unsigned int num_threads_;
401 bool serialize_callbacks_;
402
403 uint64_t effective_history_depth_;
404 time_t effective_timestamp_threshold_;
405 TraversalType effective_traversal_type_;
406
407 pthread_t *threads_process_;
408 atomic_int32 num_errors_;
409
410 Tube<CatalogJob> pre_job_queue_;
411 Tube<CatalogJob> post_job_queue_;
412 SmallHashDynamic<shash::Any, CatalogJob *> catalogs_processing_;
413 SmallHashDynamic<shash::Any, bool> catalogs_done_;
414 pthread_mutex_t catalogs_lock_;
415
416 pthread_mutex_t catalog_callback_lock_;
417 };
418
419 } // namespace swissknife
420
421 #endif // CVMFS_CATALOG_TRAVERSAL_PARALLEL_H_
422