MythTV  master
deletethread.cpp
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <iostream>
3 #include <fcntl.h>
4 
5 using namespace std;
6 
7 #include <QList>
8 #include <QTimer>
9 #include <QString>
10 #include <QFileInfo>
11 #include <QDateTime>
12 #include <QStringList>
13 #include <QMutexLocker>
14 
16 #include "mythmiscutil.h"
17 #include "mythdb.h"
18 #include "mythcorecontext.h"
19 #include "mythlogging.h"
20 
21 /*
22  Rather than attempt to calculate a delete speed from tuner card information
23  that may be completely irrelevent to a machine that does not record, just
24  choose a reasonable value.
25 
26  38 Mbps (full QAM-256 multiplex) * 4 tuners = 9961472B/0.5s
27 */
28 
30  MThread("Delete"), m_increment(9961472), m_run(true)
31 {
32  m_slow = (bool) gCoreContext->GetNumSetting("TruncateDeletesSlowly", 0);
33  m_link = (bool) gCoreContext->GetNumSetting("DeletesFollowLinks", 0);
34 }
35 
37 {
38  RunProlog();
39 
40  LOG(VB_FILE, LOG_DEBUG, "Spawning new delete thread.");
41 
42  while (gCoreContext && m_run)
43  {
44  // loop through any stored files every half second
45  ProcessNew();
46  ProcessOld();
47  usleep(500000);
48  }
49 
50  if (!m_files.empty())
51  {
52  // this will only happen if the program is closing, so fast
53  // deletion is not a problem
54  QList<DeleteHandler*>::iterator i;
55  for (i = m_files.begin(); i != m_files.end(); ++i)
56  {
57  (*i)->Close();
58  (*i)->DecrRef();
59  }
60  m_files.clear();
61  }
62  else
63  LOG(VB_FILE, LOG_DEBUG, "Delete thread self-terminating due to idle.");
64 
65  RunEpilog();
66 }
67 
68 bool DeleteThread::AddFile(QString path)
69 {
70  // check if a file exists, and add to the list of new files to be deleted
71  QFileInfo finfo(path);
72  if (!finfo.exists())
73  return false;
74 
75  QMutexLocker lock(&m_newlock);
76  DeleteHandler *handler = new DeleteHandler(path);
77  m_newfiles << handler;
78  return true;
79 }
80 
82 {
83  handler->IncrRef();
84  QMutexLocker lock(&m_newlock);
85  m_newfiles << handler;
86  return true;
87 }
88 
90 {
91  // loop through new files, unlinking and adding for deletion
92  // until none are left
93 
94  QDateTime ctime = MythDate::current();
95 
96  while (true)
97  {
98  // pull a new path from the stack
99  DeleteHandler *handler;
100  {
101  QMutexLocker lock(&m_newlock);
102  if (m_newfiles.isEmpty())
103  break;
104  else
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).arg(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  handler->DeleteFailed();
162 
163  handler->DecrRef();
164  continue;
165  }
166  }
167 
168  // open the file so it can be unlinked without immediate deletion
169  LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file: '%1'")
170  .arg(handler->m_path));
171  int fd = open(cpath, O_WRONLY);
172  if (fd == -1)
173  {
174  LOG(VB_GENERAL, LOG_ERR,
175  QString("Error deleting '%1': could not open ")
176  .arg(handler->m_path) + ENO);
177  handler->DeleteFailed();
178  handler->DecrRef();
179  continue;
180  }
181 
182  // unlink the file so as soon as it is closed, the system will
183  // delete it from the filesystem
184  if (unlink(cpath))
185  {
186  LOG(VB_GENERAL, LOG_ERR,
187  QString("Error deleting '%1': could not unlink ")
188  .arg(path) + ENO);
189  handler->DeleteFailed();
190  close(fd);
191  handler->DecrRef();
192  continue;
193  }
194 
195  handler->DeleteSucceeded();
196 
197  // insert the file into a queue of opened references to be deleted
198  handler->m_fd = fd;
199  handler->m_size = finfo.size();
200  handler->m_wait = ctime.addSecs(3); // delay deletion a bit to allow
201  // UI to get any needed IO time
202 
203  m_files << handler;
204  }
205 }
206 
208 {
209  // im the only thread accessing this, so no need for a lock
210  if (m_files.empty())
211  return;
212 
213  QDateTime ctime = MythDate::current();
214 
215  // only operate on one file at a time
216  // delete that file completely before moving onto the next
217  while (true)
218  {
219  DeleteHandler *handler = m_files.first();
220 
221  // first file in the list has been delayed for deletion
222  if (handler->m_wait > ctime)
223  break;
224 
225  if (m_slow)
226  {
227  handler->m_size -= m_increment;
228  int err = ftruncate(handler->m_fd, handler->m_size);
229 
230  if (err)
231  {
232  LOG(VB_GENERAL, LOG_ERR, QString("Error truncating '%1'")
233  .arg(handler->m_path) + ENO);
234  handler->m_size = 0;
235  }
236  }
237  else
238  handler->m_size = 0;
239 
240  if (handler->m_size == 0)
241  {
242  handler->Close();
243  m_files.removeFirst();
244  handler->DecrRef();
245  }
246 
247  // fast delete can close out all, but slow delete needs
248  // to return to sleep
249  if (m_slow || m_files.empty())
250  break;
251  }
252 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:216
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
void Close(void)
QList< DeleteHandler * > m_newfiles
Definition: deletethread.h:38
bool
Definition: pxsup2dast.c:30
DeleteThread(void)
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
virtual void DeleteFailed(void)
QList< DeleteHandler * > m_files
Definition: deletethread.h:41
virtual int IncrRef(void)
Increments reference count.
#define close
Definition: compat.h:16
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
virtual void DeleteSucceeded(void)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
void ProcessOld(void)
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:203
QDateTime m_wait
size_t m_increment
Definition: deletethread.h:33
static void usleep(unsigned long time)
Definition: mthread.cpp:349
void ProcessNew(void)
QMutex m_newlock
Definition: deletethread.h:39
bool AddFile(QString path)