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(const 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  handler = m_newfiles.takeFirst();
105  }
106 
107  // empty path given to delete thread, this should not happen
108  //if (path.isEmpty())
109  // continue;
110 
111  QString path = handler->m_path;
112  QByteArray cpath_ba = handler->m_path.toLocal8Bit();
113  const char *cpath = cpath_ba.constData();
114 
115  QFileInfo finfo(handler->m_path);
116  if (finfo.isSymLink())
117  {
118  if (m_link)
119  {
120  // if file is a symlink and symlinks are processed,
121  // grab the target of the link, and attempt to unlink
122  // the link itself
123  QString tmppath = getSymlinkTarget(handler->m_path);
124 
125  if (unlink(cpath))
126  {
127  LOG(VB_GENERAL, LOG_ERR,
128  QString("Error deleting '%1' -> '%2': ")
129  .arg(handler->m_path).arg(tmppath) + ENO);
130  handler->DeleteFailed();
131  handler->DecrRef();
132  continue;
133  }
134 
135  // if successful, emit that the link has been removed,
136  // and continue processing the target of the link as
137  // normal
138  //
139  // this may cause problems in which the link is unlinked
140  // signalling the matching metadata for removal, but the
141  // target itself fails, resulting in a spurious file in
142  // an external directory with no link into mythtv
143  handler->DeleteSucceeded();
144  handler->m_path = tmppath;
145  cpath_ba = handler->m_path.toLocal8Bit();
146  cpath = cpath_ba.constData();
147  }
148  else
149  {
150  // symlinks are not followed, so unlink the link
151  // itself and continue
152  if (unlink(cpath))
153  {
154  LOG(VB_GENERAL, LOG_ERR,
155  QString("Error deleting '%1': count not unlink ")
156  .arg(path) + ENO);
157  handler->DeleteFailed();
158  }
159  else
160  handler->DeleteFailed();
161 
162  handler->DecrRef();
163  continue;
164  }
165  }
166 
167  // open the file so it can be unlinked without immediate deletion
168  LOG(VB_FILE, LOG_INFO, QString("About to unlink/delete file: '%1'")
169  .arg(handler->m_path));
170  int fd = open(cpath, O_WRONLY);
171  if (fd == -1)
172  {
173  LOG(VB_GENERAL, LOG_ERR,
174  QString("Error deleting '%1': could not open ")
175  .arg(handler->m_path) + ENO);
176  handler->DeleteFailed();
177  handler->DecrRef();
178  continue;
179  }
180 
181  // unlink the file so as soon as it is closed, the system will
182  // delete it from the filesystem
183  if (unlink(cpath))
184  {
185  LOG(VB_GENERAL, LOG_ERR,
186  QString("Error deleting '%1': could not unlink ")
187  .arg(path) + ENO);
188  handler->DeleteFailed();
189  close(fd);
190  handler->DecrRef();
191  continue;
192  }
193 
194  handler->DeleteSucceeded();
195 
196  // insert the file into a queue of opened references to be deleted
197  handler->m_fd = fd;
198  handler->m_size = finfo.size();
199  handler->m_wait = ctime.addSecs(3); // delay deletion a bit to allow
200  // UI to get any needed IO time
201 
202  m_files << handler;
203  }
204 }
205 
207 {
208  // im the only thread accessing this, so no need for a lock
209  if (m_files.empty())
210  return;
211 
212  QDateTime ctime = MythDate::current();
213 
214  // only operate on one file at a time
215  // delete that file completely before moving onto the next
216  while (true)
217  {
218  DeleteHandler *handler = m_files.first();
219 
220  // first file in the list has been delayed for deletion
221  if (handler->m_wait > ctime)
222  break;
223 
224  if (m_slow)
225  {
226  handler->m_size -= m_increment;
227  int err = ftruncate(handler->m_fd, handler->m_size);
228 
229  if (err)
230  {
231  LOG(VB_GENERAL, LOG_ERR, QString("Error truncating '%1'")
232  .arg(handler->m_path) + ENO);
233  handler->m_size = 0;
234  }
235  }
236  else
237  handler->m_size = 0;
238 
239  if (handler->m_size == 0)
240  {
241  handler->Close();
242  m_files.removeFirst();
243  handler->DecrRef();
244  }
245 
246  // fast delete can close out all, but slow delete needs
247  // to return to sleep
248  if (m_slow || m_files.empty())
249  break;
250  }
251 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
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
bool AddFile(const QString &path)
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:202
QDateTime m_wait
size_t m_increment
Definition: deletethread.h:33
static void usleep(unsigned long time)
Definition: mthread.cpp:348
void ProcessNew(void)
QMutex m_newlock
Definition: deletethread.h:39