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

kdevplatform/serialization

  • sources
  • kfour-appscomplete
  • 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("tty 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:button", "Clear Cache"),
88  i18nc("@action:button", "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  bool m_wasShutdown;
115  QString m_path;
116  QMap<AbstractItemRepository*, AbstractRepositoryManager*> m_repositories;
117  QMap<QString, QAtomicInt*> m_customCounters;
118  mutable QMutex m_mutex;
119 
120  explicit ItemRepositoryRegistryPrivate(ItemRepositoryRegistry* owner)
121  : m_owner(owner)
122  , m_shallDelete(false)
123  , m_wasShutdown(false)
124  , m_mutex(QMutex::Recursive)
125  {
126  }
127 
128  void lockForWriting();
129  void unlockForWriting();
130  void deleteDataDirectory(const QString& path, bool recreate = true);
131 
137  bool open(const QString& path);
138 
141  void close();
142 };
143 
144 //The global item-repository registry
145 ItemRepositoryRegistry* ItemRepositoryRegistry::m_self = nullptr;
146 
147 ItemRepositoryRegistry::ItemRepositoryRegistry(const QString& repositoryPath)
148  : d_ptr(new ItemRepositoryRegistryPrivate(this))
149 {
150  Q_D(ItemRepositoryRegistry);
151 
152  Q_ASSERT(!repositoryPath.isEmpty());
153  d->open(repositoryPath);
154 }
155 
156 void ItemRepositoryRegistry::initialize(const QString& repositoryPath)
157 {
158  if (!m_self) {
161  m_self = new ItemRepositoryRegistry(repositoryPath);
162  }
163  m_self->d_func()->m_wasShutdown = false;
164  m_self->d_func()->m_shallDelete = false;
165 }
166 
167 ItemRepositoryRegistry* ItemRepositoryRegistry::self()
168 {
169  Q_ASSERT(m_self);
170  return m_self;
171 }
172 
173 void ItemRepositoryRegistry::deleteRepositoryFromDisk(const QString& repositoryPath)
174 {
175  // Now, as we have only the global item-repository registry, assume that if and only if
176  // the given session is ours, its cache path is used by the said global item-repository registry.
177  if (m_self && !m_self->d_func()->m_wasShutdown && m_self->d_func()->m_path == repositoryPath) {
178  // remove later
179  m_self->d_func()->m_shallDelete = true;
180  } else {
181  // Otherwise, given session is not ours.
182  // remove its item-repository directory directly.
183  QDir(repositoryPath).removeRecursively();
184  }
185 }
186 
187 QMutex& ItemRepositoryRegistry::mutex()
188 {
189  Q_D(ItemRepositoryRegistry);
190 
191  return d->m_mutex;
192 }
193 
194 QAtomicInt& ItemRepositoryRegistry::customCounter(const QString& identity, int initialValue)
195 {
196  Q_D(ItemRepositoryRegistry);
197 
198  auto customCounterIt = d->m_customCounters.find(identity);
199  if (customCounterIt == d->m_customCounters.end()) {
200  customCounterIt = d->m_customCounters.insert(identity, new QAtomicInt(initialValue));
201  }
202  return **customCounterIt;
203 }
204 
206 ItemRepositoryRegistry& globalItemRepositoryRegistry()
207 {
208  return *ItemRepositoryRegistry::self();
209 }
210 
211 void ItemRepositoryRegistry::registerRepository(AbstractItemRepository* repository, AbstractRepositoryManager* manager)
212 {
213  Q_D(ItemRepositoryRegistry);
214 
215  QMutexLocker lock(&d->m_mutex);
216  d->m_repositories.insert(repository, manager);
217  if (!d->m_path.isEmpty()) {
218  if (!repository->open(d->m_path)) {
219  d->deleteDataDirectory(d->m_path);
220  qCritical() << "failed to open a repository";
221  abort();
222  }
223  }
224 }
225 
226 QString ItemRepositoryRegistry::path() const
227 {
228  Q_D(const ItemRepositoryRegistry);
229 
230  //We cannot lock the mutex here, since this may be called with one of the repositories locked,
231  //and that may lead to a deadlock when at the same time a storing is requested
232  return d->m_path;
233 }
234 
235 void ItemRepositoryRegistryPrivate::lockForWriting()
236 {
237  QMutexLocker lock(&m_mutex);
238  //Create is_writing
239  QFile f(m_path + QLatin1String("/is_writing"));
240  f.open(QIODevice::WriteOnly);
241  f.close();
242 }
243 
244 void ItemRepositoryRegistry::lockForWriting()
245 {
246  Q_D(ItemRepositoryRegistry);
247 
248  d->lockForWriting();
249 }
250 
251 void ItemRepositoryRegistryPrivate::unlockForWriting()
252 {
253  QMutexLocker lock(&m_mutex);
254  //Delete is_writing
255  QFile::remove(m_path + QLatin1String("/is_writing"));
256 }
257 
258 void ItemRepositoryRegistry::unlockForWriting()
259 {
260  Q_D(ItemRepositoryRegistry);
261 
262  d->unlockForWriting();
263 }
264 
265 void ItemRepositoryRegistry::unRegisterRepository(AbstractItemRepository* repository)
266 {
267  Q_D(ItemRepositoryRegistry);
268 
269  QMutexLocker lock(&d->m_mutex);
270  Q_ASSERT(d->m_repositories.contains(repository));
271  repository->close();
272  d->m_repositories.remove(repository);
273 }
274 
275 //After calling this, the data-directory may be a new one
276 void ItemRepositoryRegistryPrivate::deleteDataDirectory(const QString& path, bool recreate)
277 {
278  QMutexLocker lock(&m_mutex);
279 
280  //lockForWriting creates a file, that prevents any other KDevelop instance from using the directory as it is.
281  //Instead, the other instance will try to delete the directory as well.
282  lockForWriting();
283 
284  bool result = QDir(path).removeRecursively();
285  Q_ASSERT(result);
286  Q_UNUSED(result);
287  // Just recreate the directory then; leave old path (as it is dependent on appname and session only).
288  if (recreate) {
289  QDir().mkpath(path);
290  }
291 }
292 
293 bool ItemRepositoryRegistryPrivate::open(const QString& path)
294 {
295  QMutexLocker mlock(&m_mutex);
296  if (m_path == path) {
297  return true;
298  }
299 
300  // Check if the repository shall be cleared
301  if (shouldClear(path)) {
302  qCWarning(SERIALIZATION) << QStringLiteral("The data-repository at %1 has to be cleared.").arg(path);
303  deleteDataDirectory(path);
304  }
305 
306  QDir().mkpath(path);
307 
308  for (auto it = m_repositories.constBegin(), end = m_repositories.constEnd(); it != end; ++it) {
309  if (!it.key()->open(path)) {
310  deleteDataDirectory(path);
311  qCritical() << "failed to open a repository";
312  abort();
313  }
314  }
315 
316  QFile f(path + QLatin1String("/Counters"));
317  if (f.open(QIODevice::ReadOnly)) {
318  QDataStream stream(&f);
319 
320  while (!stream.atEnd()) {
321  //Read in all custom counter values
322  QString counterName;
323  stream >> counterName;
324  int counterValue;
325  stream >> counterValue;
326  m_owner->customCounter(counterName, 0) = counterValue;
327  }
328  }
329 
330  m_path = path;
331 
332  return true;
333 }
334 
335 void ItemRepositoryRegistry::store()
336 {
337  Q_D(ItemRepositoryRegistry);
338 
339  QMutexLocker lock(&d->m_mutex);
340  for (auto it = d->m_repositories.constBegin(), end = d->m_repositories.constEnd(); it != end; ++it) {
341  it.key()->store();
342  }
343 
344  QFile versionFile(d->m_path + QStringLiteral("/version_%1").arg(staticItemRepositoryVersion()));
345  if (versionFile.open(QIODevice::WriteOnly)) {
346  versionFile.close();
347  } else {
348  qCWarning(SERIALIZATION) << "Could not open version file for writing";
349  }
350 
351  //Store all custom counter values
352  QFile f(d->m_path + QLatin1String("/Counters"));
353  if (f.open(QIODevice::WriteOnly)) {
354  f.resize(0);
355  QDataStream stream(&f);
356  for (QMap<QString, QAtomicInt*>::const_iterator it = d->m_customCounters.constBegin();
357  it != d->m_customCounters.constEnd();
358  ++it) {
359  stream << it.key();
360  stream << it.value()->fetchAndAddRelaxed(0);
361  }
362  } else {
363  qCWarning(SERIALIZATION) << "Could not open counter file for writing";
364  }
365 }
366 
367 void ItemRepositoryRegistry::printAllStatistics() const
368 {
369  Q_D(const ItemRepositoryRegistry);
370 
371  QMutexLocker lock(&d->m_mutex);
372  for (auto it = d->m_repositories.constBegin(), end = d->m_repositories.constEnd(); it != end; ++it) {
373  AbstractItemRepository* repository = it.key();
374  qCDebug(SERIALIZATION) << "statistics in" << repository->repositoryName() << ":";
375  qCDebug(SERIALIZATION) << repository->printStatistics();
376  }
377 }
378 
379 int ItemRepositoryRegistry::finalCleanup()
380 {
381  Q_D(ItemRepositoryRegistry);
382 
383  QMutexLocker lock(&d->m_mutex);
384  int changed = false;
385  for (auto it = d->m_repositories.constBegin(), end = d->m_repositories.constEnd(); it != end; ++it) {
386  AbstractItemRepository* repository = it.key();
387  int added = repository->finalCleanup();
388  changed += added;
389  qCDebug(SERIALIZATION) << "cleaned in" << repository->repositoryName() << ":" << added;
390  }
391 
392  return changed;
393 }
394 
395 void ItemRepositoryRegistryPrivate::close()
396 {
397  QMutexLocker lock(&m_mutex);
398 
399  for (auto it = m_repositories.constBegin(), end = m_repositories.constEnd(); it != end; ++it) {
400  it.key()->close();
401  }
402 
403  m_path.clear();
404 }
405 
406 ItemRepositoryRegistry::~ItemRepositoryRegistry()
407 {
408  Q_D(ItemRepositoryRegistry);
409 
410  QMutexLocker lock(&d->m_mutex);
411  d->close();
412  for (QAtomicInt* counter : qAsConst(d->m_customCounters)) {
413  delete counter;
414  }
415 }
416 
417 void ItemRepositoryRegistry::shutdown()
418 {
419  Q_D(ItemRepositoryRegistry);
420 
421  QMutexLocker lock(&d->m_mutex);
422  QString path = d->m_path;
423 
424  // FIXME: we don't close since this can trigger crashes at shutdown
425  // since some items are still referenced, e.g. in static variables
426 // d->close();
427 
428  if (d->m_shallDelete) {
429  d->deleteDataDirectory(path, false);
430  } else {
431  QFile::remove(path + QLatin1String("/crash_counter"));
432  }
433 
434  d->m_wasShutdown = true;
435 }
436 }
QMap::constBegin
const_iterator constBegin() const
QMutex
QFile::close
virtual void close()
QFile::resize
bool resize(qint64 sz)
QFile::remove
bool remove()
KDevelop::ItemRepositoryRegistry::mutex
QMutex & mutex()
Definition: itemrepositoryregistry.cpp:187
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QDir
QDataStream
KDevelop::ItemRepositoryRegistry::finalCleanup
int finalCleanup()
Does a big cleanup, removing all non-persistent items in the repositories.
Definition: itemrepositoryregistry.cpp:379
KDevelop::ItemRepositoryRegistry::printAllStatistics
void printAllStatistics() const
Prints the statistics of all registered item-repositories to the command line using qDebug().
Definition: itemrepositoryregistry.cpp:367
itemrepositoryregistry.h
KDevelop::ItemRepositoryRegistry::store
void store()
Stores all repositories to disk, eventually unloading unused data to save memory.
Definition: itemrepositoryregistry.cpp:335
KDevelop::AbstractItemRepository::close
virtual void close(bool doStore=false)=0
KDevelop::AbstractItemRepository::open
virtual bool open(const QString &path)=0
KDevelop::ItemRepositoryRegistry::self
static ItemRepositoryRegistry * self()
Definition: itemrepositoryregistry.cpp:167
KDevelop::staticItemRepositoryVersion
uint staticItemRepositoryVersion()
Returns a version-number that is used to reset the item-repository after incompatible layout changes.
Definition: abstractitemrepository.cpp:24
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:194
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:244
KDevelop::ItemRepositoryRegistry::unlockForWriting
void unlockForWriting()
Removes the inconsistency mark set by lockForWriting().
Definition: itemrepositoryregistry.cpp:258
QString
QString::isEmpty
bool isEmpty() const
QDir::mkpath
bool mkpath(const QString &dirPath) const
KDevelop::AbstractItemRepository::printStatistics
virtual QString printStatistics() const =0
KDevelop::globalItemRepositoryRegistry
ItemRepositoryRegistry & globalItemRepositoryRegistry()
The global item-repository registry that is used by default.
Definition: itemrepositoryregistry.cpp:206
QLatin1String
KDevelop::ItemRepositoryRegistry::unRegisterRepository
void unRegisterRepository(AbstractItemRepository *repository)
Remove a repository.
Definition: itemrepositoryregistry.cpp:265
KDevelop::AbstractRepositoryManager
Internal helper class that wraps around a repository object and manages its lifetime.
Definition: abstractitemrepository.h:51
QAtomicInt
KDevelop::ItemRepositoryRegistry::~ItemRepositoryRegistry
~ItemRepositoryRegistry()
Definition: itemrepositoryregistry.cpp:406
QMap
KDevelop::AbstractItemRepository::repositoryName
virtual QString repositoryName() const =0
KDevelop::ItemRepositoryRegistry::registerRepository
void registerRepository(AbstractItemRepository *repository, AbstractRepositoryManager *manager)
Add a new repository.
Definition: itemrepositoryregistry.cpp:211
KDevelop::AbstractItemRepository
The interface class for an item-repository object.
Definition: abstractitemrepository.h:33
KDevelop
Definition: abstractitemrepository.cpp:23
KDevelop::AbstractItemRepository::finalCleanup
virtual int finalCleanup()=0
Does a big cleanup, removing all non-persistent items in the repositories.
QMutexLocker
KDevelop::ItemRepositoryRegistry::path
QString path() const
Definition: itemrepositoryregistry.cpp:226
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:173
abstractitemrepository.h
QFile
KDevelop::ItemRepositoryRegistry::initialize
static void initialize(const QString &repositoryPath)
Initialize the global item-repository registry for the given session.
Definition: itemrepositoryregistry.cpp:156
KDevelop::ItemRepositoryRegistry
Manages a set of item-repositories and allows loading/storing them all at once from/to disk.
Definition: itemrepositoryregistry.h:41
KDevelop::ItemRepositoryRegistry::shutdown
void shutdown()
Indicates that the application has been closed gracefully.
Definition: itemrepositoryregistry.cpp:417
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Apr 10 2021 23:30:57 by doxygen 1.8.16 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kdevplatform/serialization

Skip menu "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