annotate base/TempDirectory.cpp @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents 1d656dcda8ef
children 93fb1ebff76b
rev   line source
Chris@81 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@81 2
Chris@81 3 /*
Chris@81 4 Sonic Visualiser
Chris@81 5 An audio file viewer and annotation editor.
Chris@81 6 Centre for Digital Music, Queen Mary, University of London.
Chris@81 7 This file copyright 2006 Chris Cannam.
Chris@81 8
Chris@81 9 This program is free software; you can redistribute it and/or
Chris@81 10 modify it under the terms of the GNU General Public License as
Chris@81 11 published by the Free Software Foundation; either version 2 of the
Chris@81 12 License, or (at your option) any later version. See the file
Chris@81 13 COPYING included with this distribution for more information.
Chris@81 14 */
Chris@81 15
Chris@81 16 #include "TempDirectory.h"
Chris@150 17 #include "system/System.h"
Chris@130 18 #include "Exceptions.h"
Chris@81 19
Chris@81 20 #include <QDir>
Chris@81 21 #include <QFile>
Chris@81 22 #include <QMutexLocker>
Chris@170 23 #include <QSettings>
Chris@81 24
Chris@81 25 #include <iostream>
Chris@81 26 #include <cassert>
Chris@81 27
Chris@81 28 TempDirectory *
Chris@81 29 TempDirectory::m_instance = new TempDirectory;
Chris@81 30
Chris@81 31 TempDirectory *
Chris@145 32 TempDirectory::getInstance()
Chris@81 33 {
Chris@81 34 return m_instance;
Chris@81 35 }
Chris@81 36
Chris@81 37 TempDirectory::TempDirectory() :
Chris@81 38 m_tmpdir("")
Chris@81 39 {
Chris@81 40 }
Chris@81 41
Chris@81 42 TempDirectory::~TempDirectory()
Chris@81 43 {
Chris@81 44 std::cerr << "TempDirectory::~TempDirectory" << std::endl;
Chris@81 45
Chris@81 46 cleanup();
Chris@81 47 }
Chris@81 48
Chris@81 49 void
Chris@81 50 TempDirectory::cleanup()
Chris@81 51 {
Chris@81 52 cleanupDirectory("");
Chris@81 53 }
Chris@81 54
Chris@81 55 QString
Chris@81 56 TempDirectory::getPath()
Chris@81 57 {
Chris@81 58 QMutexLocker locker(&m_mutex);
Chris@81 59
Chris@81 60 if (m_tmpdir != "") return m_tmpdir;
Chris@81 61
Chris@170 62 QSettings settings;
Chris@170 63 settings.beginGroup("TempDirectory");
Chris@170 64 QString svDirParent = settings.value("create-in", "$HOME").toString();
Chris@170 65 settings.endGroup();
Chris@170 66
Chris@170 67 svDirParent.replace("$HOME", QDir::home().absolutePath());
Chris@170 68
Chris@126 69 QString svDirBase = ".sv1";
Chris@170 70 QString svDir = QDir(svDirParent).filePath(svDirBase);
Chris@98 71 if (!QFileInfo(svDir).exists()) {
Chris@170 72 if (!QDir(svDirParent).mkdir(svDirBase)) {
Chris@170 73 throw DirectoryCreationFailed(QString("%1 directory in %2")
Chris@170 74 .arg(svDirBase).arg(svDirParent));
Chris@98 75 }
Chris@98 76 } else if (!QFileInfo(svDir).isDir()) {
Chris@170 77 throw DirectoryCreationFailed(QString("%1/%2 is not a directory")
Chris@170 78 .arg(svDirParent).arg(svDirBase));
Chris@98 79 }
Chris@98 80
Chris@98 81 cleanupAbandonedDirectories(svDir);
Chris@98 82
Chris@98 83 return createTempDirectoryIn(svDir);
Chris@98 84 }
Chris@98 85
Chris@98 86 QString
Chris@98 87 TempDirectory::createTempDirectoryIn(QString dir)
Chris@98 88 {
Chris@98 89 // Entered with mutex held.
Chris@98 90
Chris@98 91 QDir tempDirBase(dir);
Chris@81 92
Chris@81 93 // Generate a temporary directory. Qt4.1 doesn't seem to be able
Chris@81 94 // to do this for us, and mkdtemp is not standard. This method is
Chris@81 95 // based on the way glibc does mkdtemp.
Chris@81 96
Chris@81 97 static QString chars =
Chris@81 98 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Chris@81 99
Chris@81 100 QString suffix;
Chris@81 101 int padlen = 6, attempts = 100;
Chris@81 102 unsigned int r = time(0) ^ getpid();
Chris@81 103
Chris@81 104 for (int i = 0; i < padlen; ++i) {
Chris@81 105 suffix += "X";
Chris@81 106 }
Chris@81 107
Chris@81 108 for (int j = 0; j < attempts; ++j) {
Chris@81 109
Chris@81 110 unsigned int v = r;
Chris@81 111
Chris@81 112 for (int i = 0; i < padlen; ++i) {
Chris@81 113 suffix[i] = chars[v % 62];
Chris@81 114 v /= 62;
Chris@81 115 }
Chris@81 116
Chris@81 117 QString candidate = QString("sv_%1").arg(suffix);
Chris@81 118
Chris@86 119 if (tempDirBase.mkpath(candidate)) {
Chris@86 120 m_tmpdir = tempDirBase.filePath(candidate);
Chris@81 121 break;
Chris@81 122 }
Chris@81 123
Chris@81 124 r = r + 7777;
Chris@81 125 }
Chris@81 126
Chris@81 127 if (m_tmpdir == "") {
Chris@81 128 throw DirectoryCreationFailed(QString("temporary subdirectory in %1")
Chris@86 129 .arg(tempDirBase.canonicalPath()));
Chris@81 130 }
Chris@81 131
Chris@98 132 QString pidpath = QDir(m_tmpdir).filePath(QString("%1.pid").arg(getpid()));
Chris@98 133 QFile pidfile(pidpath);
Chris@98 134
Chris@98 135 if (!pidfile.open(QIODevice::WriteOnly)) {
Chris@98 136 throw DirectoryCreationFailed(QString("pid file creation in %1")
Chris@98 137 .arg(m_tmpdir));
Chris@98 138 } else {
Chris@98 139 pidfile.close();
Chris@98 140 }
Chris@98 141
Chris@81 142 return m_tmpdir;
Chris@81 143 }
Chris@81 144
Chris@81 145 QString
Chris@81 146 TempDirectory::getSubDirectoryPath(QString subdir)
Chris@81 147 {
Chris@81 148 QString tmpdirpath = getPath();
Chris@81 149
Chris@81 150 QMutexLocker locker(&m_mutex);
Chris@81 151
Chris@81 152 QDir tmpdir(tmpdirpath);
Chris@81 153 QFileInfo fi(tmpdir.filePath(subdir));
Chris@81 154
Chris@81 155 if (!fi.exists()) {
Chris@81 156 if (!tmpdir.mkdir(subdir)) {
Chris@81 157 throw DirectoryCreationFailed(fi.filePath());
Chris@81 158 } else {
Chris@81 159 return fi.filePath();
Chris@81 160 }
Chris@81 161 } else if (fi.isDir()) {
Chris@81 162 return fi.filePath();
Chris@81 163 } else {
Chris@81 164 throw DirectoryCreationFailed(fi.filePath());
Chris@81 165 }
Chris@81 166 }
Chris@81 167
Chris@81 168 void
Chris@81 169 TempDirectory::cleanupDirectory(QString tmpdir)
Chris@81 170 {
Chris@81 171 bool isRoot = false;
Chris@81 172
Chris@81 173 if (tmpdir == "") {
Chris@81 174
Chris@81 175 m_mutex.lock();
Chris@81 176
Chris@81 177 isRoot = true;
Chris@81 178 tmpdir = m_tmpdir;
Chris@81 179
Chris@81 180 if (tmpdir == "") {
Chris@81 181 m_mutex.unlock();
Chris@81 182 return;
Chris@81 183 }
Chris@81 184 }
Chris@81 185
Chris@81 186 QDir dir(tmpdir);
Chris@83 187 dir.setFilter(QDir::Dirs | QDir::Files);
Chris@81 188
Chris@81 189 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@81 190
Chris@83 191 if (dir[i] == "." || dir[i] == "..") continue;
Chris@81 192 QFileInfo fi(dir.filePath(dir[i]));
Chris@81 193
Chris@81 194 if (fi.isDir()) {
Chris@81 195 cleanupDirectory(fi.absoluteFilePath());
Chris@81 196 } else {
Chris@81 197 if (!QFile(fi.absoluteFilePath()).remove()) {
Chris@81 198 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 199 << "Failed to unlink file \""
Chris@81 200 << fi.absoluteFilePath().toStdString() << "\""
Chris@81 201 << std::endl;
Chris@81 202 }
Chris@81 203 }
Chris@81 204 }
Chris@81 205
Chris@81 206 QString dirname = dir.dirName();
Chris@81 207 if (dirname != "") {
Chris@81 208 if (!dir.cdUp()) {
Chris@81 209 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 210 << "Failed to cd to parent directory of "
Chris@81 211 << tmpdir.toStdString() << std::endl;
Chris@81 212 return;
Chris@81 213 }
Chris@81 214 if (!dir.rmdir(dirname)) {
Chris@81 215 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 216 << "Failed to remove directory "
Chris@81 217 << dirname.toStdString() << std::endl;
Chris@81 218 }
Chris@81 219 }
Chris@81 220
Chris@81 221 if (isRoot) {
Chris@81 222 m_tmpdir = "";
Chris@81 223 m_mutex.unlock();
Chris@81 224 }
Chris@81 225 }
Chris@98 226
Chris@98 227 void
Chris@98 228 TempDirectory::cleanupAbandonedDirectories(QString svDir)
Chris@98 229 {
Chris@98 230 QDir dir(svDir, "sv_*", QDir::Name, QDir::Dirs);
Chris@98 231
Chris@98 232 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@98 233
Chris@98 234 QDir subdir(dir.filePath(dir[i]), "*.pid", QDir::Name, QDir::Files);
Chris@98 235
Chris@98 236 for (unsigned int j = 0; j < subdir.count(); ++j) {
Chris@98 237
Chris@98 238 bool ok = false;
Chris@98 239 int pid = QFileInfo(subdir[j]).baseName().toInt(&ok);
Chris@98 240 if (!ok) continue;
Chris@98 241
Chris@98 242 if (GetProcessStatus(pid) == ProcessNotRunning) {
Chris@98 243 std::cerr << "INFO: Found abandoned temporary directory from "
Chris@327 244 << "a previous, defunct process\n(pid=" << pid
Chris@98 245 << ", directory=\""
Chris@98 246 << dir.filePath(dir[i]).toStdString()
Chris@98 247 << "\"). Removing it..." << std::endl;
Chris@98 248 cleanupDirectory(dir.filePath(dir[i]));
Chris@98 249 std::cerr << "...done." << std::endl;
Chris@98 250 break;
Chris@98 251 }
Chris@98 252 }
Chris@98 253 }
Chris@98 254 }
Chris@98 255
Chris@98 256
Chris@98 257