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