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