annotate base/TempDirectory.cpp @ 537:3cc4b7cd2aa5

* Merge from one-fftdataserver-per-fftmodel branch. This bit of reworking (which is not described very accurately by the title of the branch) turns the MatrixFile object into something that either reads or writes, but not both, and separates the FFT file cache reader and writer implementations separately. This allows the FFT data server to have a single thread owning writers and one reader per "customer" thread, and for all locking to be vastly simplified and concentrated in the data server alone (because none of the classes it makes use of is used in more than one thread at a time). The result is faster and more trustworthy code.
author Chris Cannam
date Tue, 27 Jan 2009 13:25:10 +0000
parents e340b2fb9471
children 1415e35881f6
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@460 56 TempDirectory::getContainingPath()
Chris@81 57 {
Chris@81 58 QMutexLocker locker(&m_mutex);
Chris@81 59
Chris@170 60 QSettings settings;
Chris@170 61 settings.beginGroup("TempDirectory");
Chris@170 62 QString svDirParent = settings.value("create-in", "$HOME").toString();
Chris@170 63 settings.endGroup();
Chris@170 64
Chris@520 65 #ifdef Q_OS_WIN32
Chris@520 66 char *homedrive = getenv("HOMEDRIVE");
Chris@520 67 char *homepath = getenv("HOMEPATH");
Chris@520 68 if (homedrive && homepath) {
Chris@520 69 svDirParent.replace("$HOME", QString("%1%2").arg(homedrive).arg(homepath));
Chris@520 70 } else {
Chris@520 71 svDirParent.replace("$HOME", QDir::home().absolutePath());
Chris@520 72 }
Chris@520 73 #else
Chris@170 74 svDirParent.replace("$HOME", QDir::home().absolutePath());
Chris@520 75 #endif
Chris@170 76
Chris@126 77 QString svDirBase = ".sv1";
Chris@170 78 QString svDir = QDir(svDirParent).filePath(svDirBase);
Chris@98 79 if (!QFileInfo(svDir).exists()) {
Chris@170 80 if (!QDir(svDirParent).mkdir(svDirBase)) {
Chris@170 81 throw DirectoryCreationFailed(QString("%1 directory in %2")
Chris@170 82 .arg(svDirBase).arg(svDirParent));
Chris@98 83 }
Chris@98 84 } else if (!QFileInfo(svDir).isDir()) {
Chris@170 85 throw DirectoryCreationFailed(QString("%1/%2 is not a directory")
Chris@170 86 .arg(svDirParent).arg(svDirBase));
Chris@98 87 }
Chris@98 88
Chris@98 89 cleanupAbandonedDirectories(svDir);
Chris@98 90
Chris@460 91 return svDir;
Chris@460 92 }
Chris@460 93
Chris@460 94 QString
Chris@460 95 TempDirectory::getPath()
Chris@460 96 {
Chris@460 97 if (m_tmpdir != "") return m_tmpdir;
Chris@460 98
Chris@460 99 return createTempDirectoryIn(getContainingPath());
Chris@98 100 }
Chris@98 101
Chris@98 102 QString
Chris@98 103 TempDirectory::createTempDirectoryIn(QString dir)
Chris@98 104 {
Chris@98 105 // Entered with mutex held.
Chris@98 106
Chris@98 107 QDir tempDirBase(dir);
Chris@81 108
Chris@81 109 // Generate a temporary directory. Qt4.1 doesn't seem to be able
Chris@81 110 // to do this for us, and mkdtemp is not standard. This method is
Chris@81 111 // based on the way glibc does mkdtemp.
Chris@81 112
Chris@81 113 static QString chars =
Chris@81 114 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Chris@81 115
Chris@81 116 QString suffix;
Chris@81 117 int padlen = 6, attempts = 100;
Chris@81 118 unsigned int r = time(0) ^ getpid();
Chris@81 119
Chris@81 120 for (int i = 0; i < padlen; ++i) {
Chris@81 121 suffix += "X";
Chris@81 122 }
Chris@81 123
Chris@81 124 for (int j = 0; j < attempts; ++j) {
Chris@81 125
Chris@81 126 unsigned int v = r;
Chris@81 127
Chris@81 128 for (int i = 0; i < padlen; ++i) {
Chris@81 129 suffix[i] = chars[v % 62];
Chris@81 130 v /= 62;
Chris@81 131 }
Chris@81 132
Chris@81 133 QString candidate = QString("sv_%1").arg(suffix);
Chris@81 134
Chris@86 135 if (tempDirBase.mkpath(candidate)) {
Chris@86 136 m_tmpdir = tempDirBase.filePath(candidate);
Chris@81 137 break;
Chris@81 138 }
Chris@81 139
Chris@81 140 r = r + 7777;
Chris@81 141 }
Chris@81 142
Chris@81 143 if (m_tmpdir == "") {
Chris@81 144 throw DirectoryCreationFailed(QString("temporary subdirectory in %1")
Chris@86 145 .arg(tempDirBase.canonicalPath()));
Chris@81 146 }
Chris@81 147
Chris@98 148 QString pidpath = QDir(m_tmpdir).filePath(QString("%1.pid").arg(getpid()));
Chris@98 149 QFile pidfile(pidpath);
Chris@98 150
Chris@98 151 if (!pidfile.open(QIODevice::WriteOnly)) {
Chris@98 152 throw DirectoryCreationFailed(QString("pid file creation in %1")
Chris@98 153 .arg(m_tmpdir));
Chris@98 154 } else {
Chris@98 155 pidfile.close();
Chris@98 156 }
Chris@98 157
Chris@81 158 return m_tmpdir;
Chris@81 159 }
Chris@81 160
Chris@81 161 QString
Chris@81 162 TempDirectory::getSubDirectoryPath(QString subdir)
Chris@81 163 {
Chris@81 164 QString tmpdirpath = getPath();
Chris@81 165
Chris@81 166 QMutexLocker locker(&m_mutex);
Chris@81 167
Chris@81 168 QDir tmpdir(tmpdirpath);
Chris@81 169 QFileInfo fi(tmpdir.filePath(subdir));
Chris@81 170
Chris@81 171 if (!fi.exists()) {
Chris@81 172 if (!tmpdir.mkdir(subdir)) {
Chris@81 173 throw DirectoryCreationFailed(fi.filePath());
Chris@81 174 } else {
Chris@81 175 return fi.filePath();
Chris@81 176 }
Chris@81 177 } else if (fi.isDir()) {
Chris@81 178 return fi.filePath();
Chris@81 179 } else {
Chris@81 180 throw DirectoryCreationFailed(fi.filePath());
Chris@81 181 }
Chris@81 182 }
Chris@81 183
Chris@81 184 void
Chris@81 185 TempDirectory::cleanupDirectory(QString tmpdir)
Chris@81 186 {
Chris@81 187 bool isRoot = false;
Chris@81 188
Chris@81 189 if (tmpdir == "") {
Chris@81 190
Chris@81 191 m_mutex.lock();
Chris@81 192
Chris@81 193 isRoot = true;
Chris@81 194 tmpdir = m_tmpdir;
Chris@81 195
Chris@81 196 if (tmpdir == "") {
Chris@81 197 m_mutex.unlock();
Chris@81 198 return;
Chris@81 199 }
Chris@81 200 }
Chris@81 201
Chris@81 202 QDir dir(tmpdir);
Chris@83 203 dir.setFilter(QDir::Dirs | QDir::Files);
Chris@81 204
Chris@81 205 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@81 206
Chris@83 207 if (dir[i] == "." || dir[i] == "..") continue;
Chris@81 208 QFileInfo fi(dir.filePath(dir[i]));
Chris@81 209
Chris@81 210 if (fi.isDir()) {
Chris@81 211 cleanupDirectory(fi.absoluteFilePath());
Chris@81 212 } else {
Chris@81 213 if (!QFile(fi.absoluteFilePath()).remove()) {
Chris@81 214 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 215 << "Failed to unlink file \""
Chris@81 216 << fi.absoluteFilePath().toStdString() << "\""
Chris@81 217 << std::endl;
Chris@81 218 }
Chris@81 219 }
Chris@81 220 }
Chris@81 221
Chris@81 222 QString dirname = dir.dirName();
Chris@81 223 if (dirname != "") {
Chris@81 224 if (!dir.cdUp()) {
Chris@81 225 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 226 << "Failed to cd to parent directory of "
Chris@81 227 << tmpdir.toStdString() << std::endl;
Chris@81 228 return;
Chris@81 229 }
Chris@81 230 if (!dir.rmdir(dirname)) {
Chris@81 231 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 232 << "Failed to remove directory "
Chris@81 233 << dirname.toStdString() << std::endl;
Chris@81 234 }
Chris@81 235 }
Chris@81 236
Chris@81 237 if (isRoot) {
Chris@81 238 m_tmpdir = "";
Chris@81 239 m_mutex.unlock();
Chris@81 240 }
Chris@81 241 }
Chris@98 242
Chris@98 243 void
Chris@98 244 TempDirectory::cleanupAbandonedDirectories(QString svDir)
Chris@98 245 {
Chris@98 246 QDir dir(svDir, "sv_*", QDir::Name, QDir::Dirs);
Chris@98 247
Chris@98 248 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@98 249
Chris@98 250 QDir subdir(dir.filePath(dir[i]), "*.pid", QDir::Name, QDir::Files);
Chris@98 251
Chris@98 252 for (unsigned int j = 0; j < subdir.count(); ++j) {
Chris@98 253
Chris@98 254 bool ok = false;
Chris@98 255 int pid = QFileInfo(subdir[j]).baseName().toInt(&ok);
Chris@98 256 if (!ok) continue;
Chris@98 257
Chris@98 258 if (GetProcessStatus(pid) == ProcessNotRunning) {
Chris@98 259 std::cerr << "INFO: Found abandoned temporary directory from "
Chris@327 260 << "a previous, defunct process\n(pid=" << pid
Chris@98 261 << ", directory=\""
Chris@98 262 << dir.filePath(dir[i]).toStdString()
Chris@98 263 << "\"). Removing it..." << std::endl;
Chris@98 264 cleanupDirectory(dir.filePath(dir[i]));
Chris@98 265 std::cerr << "...done." << std::endl;
Chris@98 266 break;
Chris@98 267 }
Chris@98 268 }
Chris@98 269 }
Chris@98 270 }
Chris@98 271
Chris@98 272
Chris@98 273