• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdevelop API Reference
  • KDE Home
  • Contact Us
 

kdevelop/kdevplatform/serialization

  • extragear
  • kdevelop
  • kdevelop
  • kdevplatform
  • serialization
itemrepositoryregistry.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2008 David Nolden <[email protected]>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17  */
18 
19 #include "itemrepositoryregistry.h"
20 
21 #include <QDir>
22 #include <QProcessEnvironment>
23 #include <QCoreApplication>
24 #include <QDataStream>
25 #include <QStandardPaths>
26 
27 #include <KLocalizedString>
28 
29 #include <util/shellutils.h>
30 
31 #include "abstractitemrepository.h"
32 #include "debug.h"
33 
34 using namespace KDevelop;
35 
36 namespace {
37 //If KDevelop crashed this many times consecutively, clean up the repository
38 const int crashesBeforeCleanup = 1;
39 
40 void setCrashCounter(QFile& crashesFile, int count)
41 {
42  crashesFile.close();
43  crashesFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
44  QDataStream writeStream(&crashesFile);
45  writeStream << count;
46 }
47 
48 bool shouldClear(const QString& path)
49 {
50  QDir dir(path);
51 
52  if (!dir.exists()) {
53  return false;
54  }
55 
56  if (getenv("CLEAR_DUCHAIN_DIR")) {
57  qCDebug(SERIALIZATION) << "clearing duchain directory because CLEAR_DUCHAIN_DIR is set";
58  return true;
59  }
60 
61  if (dir.exists(QStringLiteral("is_writing"))) {
62  qCWarning(SERIALIZATION) << "repository" << path << "was write-locked, it probably is inconsistent";
63  return true;
64  }
65 
66  if (!dir.exists(QStringLiteral("version_%1").arg(staticItemRepositoryVersion()))) {
67  qCWarning(SERIALIZATION) << "version mismatch or no version hint; expected version:" <<
68  staticItemRepositoryVersion();
69  return true;
70  }
71 
72  QFile crashesFile(dir.filePath(QStringLiteral("crash_counter")));
73  if (crashesFile.open(QIODevice::ReadOnly)) {
74  int count;
75  QDataStream stream(&crashesFile);
76  stream >> count;
77 
78  qCDebug(SERIALIZATION) << "current count of crashes: " << count;
79 
80  if (count >= crashesBeforeCleanup && !getenv("DONT_CLEAR_DUCHAIN_DIR")) {
81  bool userAnswer = askUser(i18np("The previous session crashed.", "Session crashed %1 times in a row.",
82  count),
83  i18nc("@action", "Clear cache"),
84  i18nc("@title", "Session crashed"),
85  i18n("The crash may be caused by a corruption of cached data.\n\n"
86  "Press Clear if you want KDevelop to clear the cache, otherwise press Continue if you are sure the crash has another origin."),
87  i18nc("@action", "Clear Cache"),
88  i18n("Continue"));
89  if (userAnswer) {
90  qCDebug(SERIALIZATION) << "User chose to clean repository";
91  return true;
92  } else {
93  setCrashCounter(crashesFile, 1);
94  qCDebug(SERIALIZATION) << "User chose to reset crash counter";
95  }
96  } else {
98  setCrashCounter(crashesFile, ++count);
99  }
100  } else {
101  setCrashCounter(crashesFile, 1);
102  }
103 
104  return false;
105 }
106 }
107 
108 namespace KDevelop {
109 class ItemRepositoryRegistryPrivate
110 {
111 public:
112  ItemRepositoryRegistry* m_owner;
113  bool m_shallDelete;
114  QString m_path;
115  QMap<AbstractItemRepository*, AbstractRepositoryManager*> m_repositories;
116  QMap<QString, QAtomicInt*> m_customCounters;
117  mutable QMutex m_mutex;
118 
119  explicit ItemRepositoryRegistryPrivate(ItemRepositoryRegistry* owner)
120  : m_owner(owner)
121  , m_shallDelete(false)
122  , m_mutex(QMutex::Recursive)
123  {
124  }
125 
126  void lockForWriting();
127  void unlockForWriting();
128  void deleteDataDirectory(const QString& path, bool recreate = true);
129 
135  bool open(const QString& path);
136 
139  void close();
140 };
141 
142 //The global item-repository registry
143 ItemRepositoryRegistry* ItemRepositoryRegistry::m_self = nullptr;
144 
145 ItemRepositoryRegistry::ItemRepositoryRegistry(const QString& repositoryPath)
146  : d_ptr(new ItemRepositoryRegistryPrivate(this))
147 {
148  Q_D(ItemRepositoryRegistry);
149 
150  Q_ASSERT(!repositoryPath.isEmpty());
151  d->open(repositoryPath);
152 }
153 
154 void ItemRepositoryRegistry::initialize(const QString& repositoryPath)
155 {
156  if (!m_self) {
159  m_self = new ItemRepositoryRegistry(repositoryPath);
160  }
161 }
162 
163 ItemRepositoryRegistry* ItemRepositoryRegistry::self()
164 {
165  Q_ASSERT(m_self);
166  return m_self;
167 }
168 
169 void ItemRepositoryRegistry::deleteRepositoryFromDisk(const QString& repositoryPath)
170 {
171  // Now, as we have only the global item-repository registry, assume that if and only if
172  // the given session is ours, its cache path is used by the said global item-repository registry.
173  if (m_self && m_self->d_func()->m_path == repositoryPath) {
174  // remove later
175  m_self->d_func()->m_shallDelete = true;
176  } else {
177  // Otherwise, given session is not ours.
178  // remove its item-repository directory directly.
179  QDir(repositoryPath).removeRecursively();
180  }
181 }
182 
183 QMutex& ItemRepositoryRegistry::mutex()
184 {
185  Q_D(ItemRepositoryRegistry);
186 
187  return d->m_mutex;
188 }
189 
190 QAtomicInt& ItemRepositoryRegistry::customCounter(const QString& identity, int initialValue)
191 {
192  Q_D(ItemRepositoryRegistry);
193 
194  auto customCounterIt = d->m_customCounters.find(identity);
195  if (customCounterIt == d->m_customCounters.end()) {
196  customCounterIt = d->m_customCounters.insert(identity, new QAtomicInt(initialValue));
197  }
198  return **customCounterIt;
199 }
200 
202 ItemRepositoryRegistry& globalItemRepositoryRegistry()
203 {
204  return *ItemRepositoryRegistry::self();
205 }
206 
207 void ItemRepositoryRegistry::registerRepository(AbstractItemRepository* repository, AbstractRepositoryManager* manager)
208 {
209  Q_D(ItemRepositoryRegistry);
210 
211  QMutexLocker lock(&d->m_mutex);
212  d->m_repositories.insert(repository, manager);
213  if (!d->m_path.isEmpty()) {
214  if (!repository->open(d->m_path)) {
215  d->deleteDataDirectory(d->m_path);
216  qCritical() << "failed to open a repository";
217  abort();
218  }
219  }
220 }
221 
222 QString ItemRepositoryRegistry::path() const
223 {
224  Q_D(const ItemRepositoryRegistry);
225 
226  //We cannot lock the mutex here, since this may be called with one of the repositories locked,
227  //and that may lead to a deadlock when at the same time a storing is requested
228  return d->m_path;
229 }
230 
231 void ItemRepositoryRegistryPrivate::lockForWriting()
232 {
233  QMutexLocker lock(&m_mutex);
234  //Create is_writing
235  QFile f(m_path + QLatin1String("/is_writing"));
236  f.open(QIODevice::WriteOnly);
237  f.close();
238 }
239 
240 void ItemRepositoryRegistry::lockForWriting()
241 {
242  Q_D(ItemRepositoryRegistry);
243 
244  d->lockForWriting();
245 }
246 
247 void ItemRepositoryRegistryPrivate::unlockForWriting()
248 {
249  QMutexLocker lock(&m_mutex);
250  //Delete is_writing
251  QFile::remove(m_path + QLatin1String("/is_writing"));
252 }
253 
254 void ItemRepositoryRegistry::unlockForWriting()
255 {
256  Q_D(ItemRepositoryRegistry);
257 
258  d->unlockForWriting();
259 }
260 
261 void ItemRepositoryRegistry::unRegisterRepository(AbstractItemRepository* repository)
262 {
263  Q_D(ItemRepositoryRegistry);
264 
265  QMutexLocker lock(&d->m_mutex);
266  Q_ASSERT(d->m_repositories.contains(repository));
267  repository->close();
268  d->m_repositories.remove(repository);
269 }
270 
271 //After calling this, the data-directory may be a new one
272 void ItemRepositoryRegistryPrivate::deleteDataDirectory(const QString& path, bool recreate)
273 {
274  QMutexLocker lock(&m_mutex);
275 
276  //lockForWriting creates a file, that prevents any other KDevelop instance from using the directory as it is.
277  //Instead, the other instance will try to delete the directory as well.
278  lockForWriting();
279 
280  bool result = QDir(path).removeRecursively();
281  Q_ASSERT(result);
282  Q_UNUSED(result);
283  // Just recreate the directory then; leave old path (as it is dependent on appname and session only).
284  if (recreate) {
285  QDir().mkpath(path);
286  }
287 }
288 
289 bool ItemRepositoryRegistryPrivate::open(const QString& path)
290 {
291  QMutexLocker mlock(&m_mutex);
292  if (m_path == path) {
293  return true;
294  }
295 
296  // Check if the repository shall be cleared
297  if (shouldClear(path)) {
298  qCWarning(SERIALIZATION) << QStringLiteral("The data-repository at %1 has to be cleared.").arg(path);
299  deleteDataDirectory(path);
300  }
301 
302  QDir().mkpath(path);
303 
304  for (auto it = m_repositories.constBegin(), end = m_repositories.constEnd(); it != end; ++it) {
305  if (!it.key()->open(path)) {
306  deleteDataDirectory(path);
307  qCritical() << "failed to open a repository";
308  abort();
309  }
310  }
311 
312  QFile f(path + QLatin1String("/Counters"));
313  if (f.open(QIODevice::ReadOnly)) {
314  QDataStream stream(&f);
315 
316  while (!stream.atEnd()) {
317  //Read in all custom counter values
318  QString counterName;
319  stream >> counterName;
320  int counterValue;
321  stream >> counterValue;
322  m_owner->customCounter(counterName, 0) = counterValue;
323  }
324  }
325 
326  m_path = path;
327 
328  return true;
329 }
330 
331 void ItemRepositoryRegistry::store()
332 {
333  Q_D(ItemRepositoryRegistry);
334 
335  QMutexLocker lock(&d->m_mutex);
336  for (auto it = d->m_repositories.constBegin(), end = d->m_repositories.constEnd(); it != end; ++it) {
337  it.key()->store();
338  }
339 
340  QFile versionFile(d->m_path + QStringLiteral("/version_%1").arg(staticItemRepositoryVersion()));
341  if (versionFile.open(QIODevice::WriteOnly)) {
342  versionFile.close();
343  } else {
344  qCWarning(SERIALIZATION) << "Could not open version file for writing";
345  }
346 
347  //Store all custom counter values
348  QFile f(d->m_path + QLatin1String("/Counters"));
349  if (f.open(QIODevice::WriteOnly)) {
350  f.resize(0);
351  QDataStream stream(&f);
352  for (QMap<QString, QAtomicInt*>::const_iterator it = d->m_customCounters.constBegin();
353  it != d->m_customCounters.constEnd();
354  ++it) {
355  stream << it.key();
356  stream << it.value()->fetchAndAddRelaxed(0);
357  }
358  } else {
359  qCWarning(SERIALIZATION) << "Could not open counter file for writing";
360  }
361 }
362 
363 void ItemRepositoryRegistry::printAllStatistics() const
364 {
365  Q_D(const ItemRepositoryRegistry);
366 
367  QMutexLocker lock(&d->m_mutex);
368  for (auto it = d->m_repositories.constBegin(), end = d->m_repositories.constEnd(); it != end; ++it) {
369  AbstractItemRepository* repository = it.key();
370  qCDebug(SERIALIZATION) << "statistics in" << repository->repositoryName() << ":";
371  qCDebug(SERIALIZATION) << repository->printStatistics();
372  }
373 }
374 
375 int ItemRepositoryRegistry::finalCleanup()
376 {
377  Q_D(ItemRepositoryRegistry);
378 
379  QMutexLocker lock(&d->m_mutex);
380  int changed = false;
381  for (auto it = d->m_repositories.constBegin(), end = d->m_repositories.constEnd(); it != end; ++it) {
382  AbstractItemRepository* repository = it.key();
383  int added = repository->finalCleanup();
384  changed += added;
385  qCDebug(SERIALIZATION) << "cleaned in" << repository->repositoryName() << ":" << added;
386  }
387 
388  return changed;
389 }
390 
391 void ItemRepositoryRegistryPrivate::close()
392 {
393  QMutexLocker lock(&m_mutex);
394 
395  for (auto it = m_repositories.constBegin(), end = m_repositories.constEnd(); it != end; ++it) {
396  it.key()->close();
397  }
398 
399  m_path.clear();
400 }
401 
402 ItemRepositoryRegistry::~ItemRepositoryRegistry()
403 {
404  Q_D(ItemRepositoryRegistry);
405 
406  QMutexLocker lock(&d->m_mutex);
407  d->close();
408  for (QAtomicInt* counter : qAsConst(d->m_customCounters)) {
409  delete counter;
410  }
411 }
412 
413 void ItemRepositoryRegistry::shutdown()
414 {
415  Q_D(ItemRepositoryRegistry);
416 
417  QMutexLocker lock(&d->m_mutex);
418  QString path = d->m_path;
419 
420  // FIXME: we don't close since this can trigger crashes at shutdown
421  // since some items are still referenced, e.g. in static variables
422 // d->close();
423 
424  if (d->m_shallDelete) {
425  d->deleteDataDirectory(path, false);
426  } else {
427  QFile::remove(path + QLatin1String("/crash_counter"));
428  }
429 }
430 }
KDevelop::ItemRepositoryRegistry::lockForWriting
void lockForWriting()
Marks the directory as inconsistent, so it will be discarded on next startup if the application crash...
Definition: itemrepositoryregistry.cpp:240
QMutex
KDevelop::AbstractItemRepository
The interface class for an item-repository object.
Definition: abstractitemrepository.h:33
KDevelop::AbstractItemRepository::open
virtual bool open(const QString &path)=0
QFile::resize
bool resize(qint64 sz)
KDevelop::ItemRepositoryRegistry::customCounter
QAtomicInt & customCounter(const QString &identity, int initialValue)
Returns a custom counter persistently stored as part of item-repositories in the same directory...
Definition: itemrepositoryregistry.cpp:190
QFile::remove
bool remove()
QDataStream
KDevelop::AbstractItemRepository::finalCleanup
virtual int finalCleanup()=0
Does a big cleanup, removing all non-persistent items in the repositories.
KDevelop::AbstractItemRepository::printStatistics
virtual QString printStatistics() const =0
QMap::constBegin
const_iterator constBegin() const
QMap
KDevelop::ItemRepositoryRegistry::printAllStatistics
void printAllStatistics() const
Prints the statistics of all registered item-repositories to the command line using qDebug()...
Definition: itemrepositoryregistry.cpp:363
KDevelop::ItemRepositoryRegistry::shutdown
void shutdown()
Indicates that the application has been closed gracefully.
Definition: itemrepositoryregistry.cpp:413
KDevelop::ItemRepositoryRegistry::deleteRepositoryFromDisk
static void deleteRepositoryFromDisk(const QString &repositoryPath)
Deletes the item-repository of a specified session; or, if it is currently used, marks it for deletio...
Definition: itemrepositoryregistry.cpp:169
QFile
KDevelop::ItemRepositoryRegistry::~ItemRepositoryRegistry
~ItemRepositoryRegistry()
Definition: itemrepositoryregistry.cpp:402
itemrepositoryregistry.h
KDevelop::ItemRepositoryRegistry::unRegisterRepository
void unRegisterRepository(AbstractItemRepository *repository)
Remove a repository.
Definition: itemrepositoryregistry.cpp:261
KDevelop::ItemRepositoryRegistry::registerRepository
void registerRepository(AbstractItemRepository *repository, AbstractRepositoryManager *manager)
Add a new repository.
Definition: itemrepositoryregistry.cpp:207
QString::isEmpty
bool isEmpty() const
KDevelop::globalItemRepositoryRegistry
ItemRepositoryRegistry & globalItemRepositoryRegistry()
The global item-repository registry that is used by default.
Definition: itemrepositoryregistry.cpp:202
QAtomicInt
QString
KDevelop::ItemRepositoryRegistry::finalCleanup
int finalCleanup()
Does a big cleanup, removing all non-persistent items in the repositories.
Definition: itemrepositoryregistry.cpp:375
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
KDevelop::ItemRepositoryRegistry::path
QString path() const
Definition: itemrepositoryregistry.cpp:222
KDevelop::ItemRepositoryRegistry::initialize
static void initialize(const QString &repositoryPath)
Initialize the global item-repository registry for the given session.
Definition: itemrepositoryregistry.cpp:154
KDevelop::ItemRepositoryRegistry
Manages a set of item-repositories and allows loading/storing them all at once from/to disk...
Definition: itemrepositoryregistry.h:41
KDevelop::AbstractRepositoryManager
Internal helper class that wraps around a repository object and manages its lifetime.
Definition: abstractitemrepository.h:51
QFile::close
virtual void close()
KDevelop::ItemRepositoryRegistry::store
void store()
Stores all repositories to disk, eventually unloading unused data to save memory. ...
Definition: itemrepositoryregistry.cpp:331
QDir
KDevelop::AbstractItemRepository::close
virtual void close(bool doStore=false)=0
KDevelop::staticItemRepositoryVersion
uint staticItemRepositoryVersion()
Returns a version-number that is used to reset the item-repository after incompatible layout changes...
Definition: abstractitemrepository.cpp:24
QLatin1String
QMutexLocker
KDevelop::ItemRepositoryRegistry::unlockForWriting
void unlockForWriting()
Removes the inconsistency mark set by lockForWriting().
Definition: itemrepositoryregistry.cpp:254
KDevelop::ItemRepositoryRegistry::mutex
QMutex & mutex()
Definition: itemrepositoryregistry.cpp:183
abstractitemrepository.h
QDir::mkpath
bool mkpath(const QString &dirPath) const
KDevelop::AbstractItemRepository::repositoryName
virtual QString repositoryName() const =0
KDevelop::ItemRepositoryRegistry::self
static ItemRepositoryRegistry * self()
Definition: itemrepositoryregistry.cpp:163
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Fri Dec 6 2019 04:51:35 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kdevelop/kdevplatform/serialization

Skip menu "kdevelop/kdevplatform/serialization"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdevelop API Reference

Skip menu "kdevelop API Reference"
  •   kdevplatform
  •     debugger
  •     documentation
  •     interfaces
  •     language
  •       assistant
  •       backgroundparser
  •       checks
  •       classmodel
  •       codecompletion
  •       codegen
  •       duchain
  •       editor
  •       highlighting
  •       interfaces
  •       util
  •     outputview
  •     project
  •     serialization
  •     shell
  •     sublime
  •     tests
  •     util
  •     vcs

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal