1/* -*- C++ -*-
2 This file implements the DependencyPolicy class.
3
4 SPDX-FileCopyrightText: 2004-2013 Mirko Boehm <mirko@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7
8 $Id: DebuggingAids.cpp 20 2005-08-08 21:02:51Z mirko $
9*/
10
11#include "dependencypolicy.h"
12
13#include <QCoreApplication>
14#include <QDebug>
15#include <QMutex>
16
17#include "debuggingaids.h"
18#include "job.h"
19#include "managedjobpointer.h"
20
21#include "dependency.h"
22
23using namespace ThreadWeaver;
24
25typedef QMultiMap<JobPointer, JobPointer> JobMultiMap;
26
27class Q_DECL_HIDDEN DependencyPolicy::Private
28{
29public:
30 /** A container to keep track of Job dependencies.
31 * For each dependency A->B, which means Job B depends on Job A and may only be executed after A has been
32 * finished, an entry will be added with key A and value B. When A is finished, the entry will be removed.
33 */
34 JobMultiMap &dependencies()
35 {
36 return depMap_;
37 }
38
39 QMutex *mutex()
40 {
41 return &mutex_;
42 }
43
44 JobMultiMap depMap_;
45 QMutex mutex_;
46};
47
48DependencyPolicy::DependencyPolicy()
49 : QueuePolicy()
50 , d(new Private())
51{
52}
53
54DependencyPolicy::~DependencyPolicy()
55{
56 delete d;
57}
58
59void DependencyPolicy::addDependency(JobPointer jobA, JobPointer jobB)
60{
61 // jobA depends on jobB
62 REQUIRE(jobA != nullptr && jobB != nullptr && jobA != jobB);
63
64 QMutexLocker a(jobA->mutex());
65 QMutexLocker b(jobB->mutex());
66 QMutexLocker l(d->mutex());
67 jobA->assignQueuePolicy(this);
68 jobB->assignQueuePolicy(this);
69 d->dependencies().insert(key: jobA, value: jobB);
70 TWDEBUG(2, "inserted dependency %p->%p.\n", jobA.data(), jobB.data());
71 ENSURE(d->dependencies().contains(jobA));
72}
73
74void DependencyPolicy::addDependency(const Dependency &dep)
75{
76 addDependency(jobA: dep.dependent(), jobB: dep.dependee());
77}
78
79bool DependencyPolicy::removeDependency(JobPointer jobA, JobPointer jobB)
80{
81 REQUIRE(jobA != nullptr && jobB != nullptr);
82 bool result = false;
83 QMutexLocker l(d->mutex());
84
85 // there may be only one (!) occurrence of [this, dep]:
86 QMutableMultiMapIterator<JobPointer, JobPointer> it(d->dependencies());
87 while (it.hasNext()) {
88 it.next();
89 if (it.key() == jobA && it.value() == jobB) {
90 it.remove();
91 TWDEBUG(2, "removed dependency %p->%p.\n", jobA.data(), jobB.data());
92 result = true;
93 break;
94 }
95 }
96 TWDEBUG(result == false, 2, "cannot remove dependency %p->%p, not found.\n", jobA.data(), jobB.data());
97 ENSURE(!d->dependencies().keys(jobB).contains(jobA));
98 return result;
99}
100
101bool DependencyPolicy::removeDependency(const Dependency &dep)
102{
103 return removeDependency(jobA: dep.dependent(), jobB: dep.dependee());
104}
105
106void DependencyPolicy::resolveDependencies(JobPointer job)
107{
108 if (job->success()) {
109 QMutexLocker l(d->mutex());
110 QMutableMultiMapIterator<JobPointer, JobPointer> it(d->dependencies());
111 // there has to be a better way to do this: (?)
112 while (it.hasNext()) { // we remove all entries where jobs depend on *this* :
113 it.next();
114 if (it.value() == job) {
115 TWDEBUG(2, "resolved dependencies for %p: %p->%p.\n", job.data(), it.key().data(), it.value().data());
116 it.remove();
117 }
118 }
119 }
120}
121
122// QList<JobPointer> DependencyPolicy::getDependencies(JobPointer job) const
123//{
124// REQUIRE (job != 0);
125// QList<JobInterface*> result;
126// JobMultiMap::const_iterator it;
127// QMutexLocker l( & d->mutex() );
128
129// for ( it = d->dependencies().constBegin(); it != d->dependencies().constEnd(); ++it )
130// {
131// if ( it.key() == job )
132// {
133// result.append( it.value() );
134// }
135// }
136// return result;
137//}
138
139bool DependencyPolicy::hasUnresolvedDependencies(JobPointer job) const
140{
141 REQUIRE(job != nullptr);
142 QMutexLocker l(d->mutex());
143 return d->dependencies().contains(key: job);
144}
145
146bool DependencyPolicy::isEmpty() const
147{
148 QMutexLocker l(d->mutex());
149 return d->dependencies().isEmpty();
150}
151
152DependencyPolicy &DependencyPolicy::instance()
153{
154 static DependencyPolicy policy;
155 return policy;
156}
157
158bool DependencyPolicy::canRun(JobPointer job)
159{
160 REQUIRE(job != nullptr);
161 return !hasUnresolvedDependencies(job);
162}
163
164void DependencyPolicy::free(JobPointer job)
165{
166 REQUIRE(job != nullptr);
167 REQUIRE(job->status() > Job::Status_Running);
168 if (job->success()) {
169 resolveDependencies(job);
170 TWDEBUG(3, "DependencyPolicy::free: dependencies resolved for job %p.\n", (void *)job.data());
171 } else {
172 TWDEBUG(3, "DependencyPolicy::free: not resolving dependencies for %p (execution not successful).\n", (void *)job.data());
173 }
174 ENSURE((!hasUnresolvedDependencies(job) && job->success()) || !job->success());
175}
176
177void DependencyPolicy::release(JobPointer job)
178{
179 REQUIRE(job != nullptr);
180 Q_UNUSED(job)
181}
182
183void DependencyPolicy::destructed(JobInterface *job)
184{
185 REQUIRE(job != nullptr);
186 resolveDependencies(job: ManagedJobPointer<JobInterface>(job));
187}
188
189// void DependencyPolicy::dumpJobDependencies()
190//{
191// QMutexLocker l( & d->mutex() );
192
193// debug ( 0, "Job Dependencies (left depends on right side):\n" );
194// for ( JobMultiMap::const_iterator it = d->dependencies().constBegin(); it != d->dependencies().constEnd(); ++it )
195// {
196// debug( 0, " : %p <-- %p\n", (void*)it.key(), (void*)it.value());
197// }
198// debug ( 0, "-----------------\n" );
199//}
200

source code of threadweaver/src/dependencypolicy.cpp