MythTV master
deletethread.cpp
Go to the documentation of this file.
1#include <cstdlib>
2#include <iostream>
3#include <fcntl.h>
4
5#include <QList>
6#include <QTimer>
7#include <QString>
8#include <QFileInfo>
9#include <QDateTime>
10#include <QStringList>
11#include <QMutexLocker>
12
14#include "libmythbase/mythdb.h"
17
19
20/*
21 Rather than attempt to calculate a delete speed from tuner card information
22 that may be completely irrelevent to a machine that does not record, just
23 choose a reasonable value.
24
25 38 Mbps (full QAM-256 multiplex) * 4 tuners = 9961472B/0.5s
26*/
27
29 MThread("Delete")
30{
31 m_slow = (bool) gCoreContext->GetNumSetting("TruncateDeletesSlowly", 0);
32 m_link = (bool) gCoreContext->GetNumSetting("DeletesFollowLinks", 0);
33}
34
36{
37 RunProlog();
38
39 LOG(VB_FILE, LOG_DEBUG, "Spawning new delete thread.");
40
41 while (gCoreContext && m_run)
42 {
43 // loop through any stored files every half second
44 ProcessNew();
45 ProcessOld();
46 usleep(0.5s);
47 }
48
49 if (!m_files.empty())
50 {
51 // this will only happen if the program is closing, so fast
52 // deletion is not a problem
53 QList<DeleteHandler*>::iterator i;
54 for (i = m_files.begin(); i != m_files.end(); ++i)
55 {
56 (*i)->Close();
57 (*i)->DecrRef();
58 }
59 m_files.clear();
60 }
61 else
62 {
63 LOG(VB_FILE, LOG_DEBUG, "Delete thread self-terminating due to idle.");
64 }
65
66 RunEpilog();
67}
68
69bool DeleteThread::AddFile(const QString& path)
70{
71 // check if a file exists, and add to the list of new files to be deleted
72 QFileInfo finfo(path);
73 if (!finfo.exists())
74 return false;
75
76 QMutexLocker lock(&m_newlock);
77 auto *handler = new DeleteHandler(path);
78 m_newfiles << handler;
79 return true;
80}
81
83{
84 handler->IncrRef();
85 QMutexLocker lock(&m_newlock);
86 m_newfiles << handler;
87 return true;
88}
89
91{
92 // loop through new files, unlinking and adding for deletion
93 // until none are left
94
95 QDateTime ctime = MythDate::current();
96
97 while (true)
98 {
99 // pull a new path from the stack
100 DeleteHandler *handler = nullptr;
101 {
102 QMutexLocker lock(&m_newlock);
103 if (m_newfiles.isEmpty())
104 break;
105 handler = m_newfiles.takeFirst();
106 }
107
108 // empty path given to delete thread, this should not happen
109 //if (path.isEmpty())
110 // continue;
111
112 QString path = handler->m_path;
113 QByteArray cpath_ba = handler->m_path.toLocal8Bit();
114 const char *cpath = cpath_ba.constData();
115
116 QFileInfo finfo(handler->m_path);
117 if (finfo.isSymLink())
118 {
119 if (m_link)
120 {
121 // if file is a symlink and symlinks are processed,
122 // grab the target of the link, and attempt to unlink
123 // the link itself
124 QString tmppath = getSymlinkTarget(handler->m_path);
125
126 if (unlink(cpath))
127 {
128 LOG(VB_GENERAL, LOG_ERR,
129 QString("Error deleting '%1' -> '%2': ")
130 .arg(handler->m_path, tmppath) + ENO);
131 handler->DeleteFailed();
132 handler->DecrRef();
133 continue;
134 }
135
136 // if successful, emit that the link has been removed,
137 // and continue processing the target of the link as
138 // normal
139 //
140 // this may cause problems in which the link is unlinked
141 // signalling the matching metadata for removal, but the
142 // target itself fails, resulting in a spurious file in
143 // an external directory with no link into mythtv
144 handler->DeleteSucceeded();
145 handler->m_path = tmppath;
146 cpath_ba = handler->m_path.toLocal8Bit();
147 cpath = cpath_ba.constData();
148 }
149 else
150 {
151 // symlinks are not followed, so unlink the link
152 // itself and continue
153 if (unlink(cpath))
154 {
155 LOG(VB_GENERAL, LOG_ERR,
156 QString("Error deleting '%1': count not unlink ")
157 .arg(path) + ENO);
158 handler->DeleteFailed();
159 }
160 else
161 {
162 handler->DeleteFailed();
163 }
164
165 handler->DecrRef();
166 continue;
167 }
168 }
169
170 // open the file so it can be unlinked without immediate deletion
171 LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file: '%1'")
172 .arg(handler->m_path));
173 int fd = open(cpath, O_WRONLY);
174 if (fd == -1)
175 {
176 LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file"));
177
178 QDir dir(cpath);
179 if(MythRemoveDirectory(dir))
180 {
181 LOG(VB_GENERAL, LOG_ERR,
182 QString("Error deleting '%1': is no directory ")
183 .arg(cpath) + ENO);
184 handler->DeleteFailed();
185 handler->DecrRef();
186 continue;
187 }
188 }
189 // unlink the file so as soon as it is closed, the system will
190 // delete it from the filesystem
191 else if (unlink(cpath))
192 {
193 LOG(VB_GENERAL, LOG_ERR,
194 QString("Error deleting '%1': could not unlink ")
195 .arg(path) + ENO);
196 handler->DeleteFailed();
197 close(fd);
198 handler->DecrRef();
199 continue;
200 }
201
202 handler->DeleteSucceeded();
203
204 // insert the file into a queue of opened references to be deleted
205 handler->m_fd = fd;
206 handler->m_size = finfo.size();
207 handler->m_wait = ctime.addSecs(3); // delay deletion a bit to allow
208 // UI to get any needed IO time
209
210 m_files << handler;
211 }
212}
213
215{
216 // im the only thread accessing this, so no need for a lock
217 if (m_files.empty())
218 return;
219
220 QDateTime ctime = MythDate::current();
221
222 // only operate on one file at a time
223 // delete that file completely before moving onto the next
224 while (true)
225 {
226 DeleteHandler *handler = m_files.first();
227
228 // first file in the list has been delayed for deletion
229 if (handler->m_wait > ctime)
230 break;
231
232 if (m_slow)
233 {
234 handler->m_size -= m_increment;
235 int err = ftruncate(handler->m_fd, handler->m_size);
236
237 if (err)
238 {
239 LOG(VB_GENERAL, LOG_ERR, QString("Error truncating '%1'")
240 .arg(handler->m_path) + ENO);
241 handler->m_size = 0;
242 }
243 }
244 else
245 {
246 handler->m_size = 0;
247 }
248
249 if (handler->m_size == 0)
250 {
251 handler->Close();
252 m_files.removeFirst();
253 handler->DecrRef();
254 }
255
256 // fast delete can close out all, but slow delete needs
257 // to return to sleep
258 if (m_slow || m_files.empty())
259 break;
260 }
261}
void Close(void)
QDateTime m_wait
virtual void DeleteSucceeded(void)
virtual void DeleteFailed(void)
void ProcessNew(void)
DeleteThread(void)
void ProcessOld(void)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
QList< DeleteHandler * > m_newfiles
Definition: deletethread.h:38
QList< DeleteHandler * > m_files
Definition: deletethread.h:41
bool AddFile(const QString &path)
QMutex m_newlock
Definition: deletethread.h:39
size_t m_increment
Definition: deletethread.h:33
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
int GetNumSetting(const QString &key, int defaultval=0)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
#define close
Definition: compat.h:39
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool MythRemoveDirectory(QDir &aDir)
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
bool
Definition: pxsup2dast.c:31