Skip to content

Commit

Permalink
Convert DeleteThread and TruncateThread to QRunnable
Browse files Browse the repository at this point in the history
Fixes #9757.

It seems that it might be the DeleteThread exiting poorly causing this issue.
As I'd earlier found that the this->deleteLater() code structure is nasty in
QThreads, this was ripe for a redo anyways.  I have changed it to using a
QRunnable (kinda a lightweight QThread) which actually will run in a thread
from a global threadpool, and will clean up after itself.  Since the
TruncateThread was nearly identical in structure, I changed it too.
  • Loading branch information
Beirdo committed May 5, 2011
1 parent 2d7a73e commit 8ea47bc
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 99 deletions.
121 changes: 52 additions & 69 deletions mythtv/programs/mythbackend/mainserver.cpp
Expand Up @@ -1764,19 +1764,16 @@ void MainServer::HandleFillProgramInfo(QStringList &slist, PlaybackSock *pbs)
SendResponse(pbssock, strlist);
}


void DeleteThread::run(void)
{
if (!m_parent)
if (!m_ms)
return;

MainServer *ms = m_parent->ms;
ms->DoDeleteThread(m_parent);

delete m_parent;
this->deleteLater();
m_ms->DoDeleteThread(this);
}

void MainServer::DoDeleteThread(const DeleteStruct *ds)
void MainServer::DoDeleteThread(DeleteStruct *ds)
{
// sleep a little to let frontends reload the recordings list
// after deleting a recording, then we can hammer the DB and filesystem
Expand All @@ -1786,17 +1783,19 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)
deletelock.lock();

QString logInfo = QString("chanid %1 at %2")
.arg(ds->chanid).arg(ds->recstartts.toString());
.arg(ds->m_chanid)
.arg(ds->m_recstartts.toString());

QString name = QString("deleteThread%1%2").arg(getpid()).arg(rand());
QFile checkFile(ds->filename);
QFile checkFile(ds->m_filename);

if (!MSqlQuery::testDBConnection())
{
QString msg = QString("ERROR opening database connection for Delete "
"Thread for chanid %1 recorded at %2. Program "
"will NOT be deleted.")
.arg(ds->chanid).arg(ds->recstartts.toString());
.arg(ds->m_chanid)
.arg(ds->m_recstartts.toString());
VERBOSE(VB_GENERAL, msg);
gCoreContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
QString("Unable to open database connection for %1. "
Expand All @@ -1807,14 +1806,15 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)
return;
}

ProgramInfo pginfo(ds->chanid, ds->recstartts);
ProgramInfo pginfo(ds->m_chanid, ds->m_recstartts);

if (!pginfo.GetChanID())
{
QString msg = QString("ERROR retrieving program info when trying to "
"delete program for chanid %1 recorded at %2. "
"Recording will NOT be deleted.")
.arg(ds->chanid).arg(ds->recstartts.toString());
.arg(ds->m_chanid)
.arg(ds->m_recstartts.toString());
VERBOSE(VB_GENERAL, msg);
gCoreContext->LogEntry("mythbackend", LP_ERROR, "Delete Recording",
QString("Unable to retrieve program info for %1. "
Expand All @@ -1830,24 +1830,24 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)
// deleting failed recordings without fuss, but blocks accidental
// deletion of metadata for files where the filesystem has gone missing.
if ((!checkFile.exists()) && pginfo.GetFilesize() &&
(!ds->forceMetadataDelete))
(!ds->m_forceMetadataDelete))
{
VERBOSE(VB_IMPORTANT, QString(
"ERROR when trying to delete file: %1. File "
"doesn't exist. Database metadata"
"will not be removed.")
.arg(ds->filename));
.arg(ds->m_filename));
gCoreContext->LogEntry("mythbackend", LP_WARNING, "Delete Recording",
QString("File %1 does not exist for %2 when trying "
"to delete recording.")
.arg(ds->filename).arg(logInfo));
.arg(ds->m_filename).arg(logInfo));

pginfo.SaveDeletePendingFlag(false);
deletelock.unlock();
return;
}

JobQueue::DeleteAllJobs(ds->chanid, ds->recstartts);
JobQueue::DeleteAllJobs(ds->m_chanid, ds->m_recstartts);

LiveTVChain *tvchain = GetChainWithRecording(pginfo);
if (tvchain)
Expand All @@ -1864,16 +1864,16 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)
{
// Since stat fails after unlinking on some filesystems,
// get the filesize first
const QFileInfo info(ds->filename);
const QFileInfo info(ds->m_filename);
size = info.size();
fd = DeleteFile(ds->filename, followLinks, ds->forceMetadataDelete);
fd = DeleteFile(ds->m_filename, followLinks, ds->m_forceMetadataDelete);

if ((fd < 0) && checkFile.exists())
errmsg = true;
}
else
{
delete_file_immediately(ds->filename, followLinks, false);
delete_file_immediately(ds->m_filename, followLinks, false);
sleep(2);
if (checkFile.exists())
errmsg = true;
Expand All @@ -1883,10 +1883,10 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)
{
VERBOSE(VB_IMPORTANT,
QString("Error deleting file: %1. Keeping metadata in database.")
.arg(ds->filename));
.arg(ds->m_filename));
gCoreContext->LogEntry("mythbackend", LP_WARNING, "Delete Recording",
QString("File %1 for %2 could not be deleted.")
.arg(ds->filename).arg(logInfo));
.arg(ds->m_filename).arg(logInfo));

pginfo.SaveDeletePendingFlag(false);
deletelock.unlock();
Expand All @@ -1895,7 +1895,7 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)

/* Delete all preview thumbnails. */

QFileInfo fInfo( ds->filename );
QFileInfo fInfo( ds->m_filename );
QString nameFilter = fInfo.fileName() + "*.png";
// QDir's nameFilter uses spaces or semicolons to separate globs,
// so replace them with the "match any character" wildcard
Expand All @@ -1922,20 +1922,20 @@ void MainServer::DoDeleteThread(const DeleteStruct *ds)
deletelock.unlock();

if (slowDeletes && fd >= 0)
TruncateAndClose(&pginfo, fd, ds->filename, size);
TruncateAndClose(&pginfo, fd, ds->m_filename, size);
}

void MainServer::DeleteRecordedFiles(const DeleteStruct *ds)
void MainServer::DeleteRecordedFiles(DeleteStruct *ds)
{
QString logInfo = QString("chanid %1 at %2")
.arg(ds->chanid).arg(ds->recstartts.toString());
.arg(ds->m_chanid).arg(ds->m_recstartts.toString());

MSqlQuery update(MSqlQuery::InitCon());
MSqlQuery query(MSqlQuery::InitCon());
query.prepare("SELECT basename, hostname, storagegroup FROM recordedfile "
"WHERE chanid = :CHANID AND starttime = :STARTTIME;");
query.bindValue(":CHANID", ds->chanid);
query.bindValue(":STARTTIME", ds->recstartts);
query.bindValue(":CHANID", ds->m_chanid);
query.bindValue(":STARTTIME", ds->m_recstartts);

if (!query.exec() || !query.isActive())
{
Expand All @@ -1955,7 +1955,7 @@ void MainServer::DeleteRecordedFiles(const DeleteStruct *ds)
storagegroup = query.value(2).toString();
deleteInDB = false;

if (basename == ds->filename)
if (basename == ds->m_filename)
deleteInDB = true;
else
{
Expand Down Expand Up @@ -1986,8 +1986,8 @@ void MainServer::DeleteRecordedFiles(const DeleteStruct *ds)
"WHERE chanid = :CHANID "
"AND starttime = :STARTTIME "
"AND basename = :BASENAME ;");
update.bindValue(":CHANID", ds->chanid);
update.bindValue(":STARTTIME", ds->recstartts);
update.bindValue(":CHANID", ds->m_chanid);
update.bindValue(":STARTTIME", ds->m_recstartts);
update.bindValue(":BASENAME", basename);
if (!update.exec())
{
Expand All @@ -2002,17 +2002,17 @@ void MainServer::DeleteRecordedFiles(const DeleteStruct *ds)
}
}

void MainServer::DoDeleteInDB(const DeleteStruct *ds)
void MainServer::DoDeleteInDB(DeleteStruct *ds)
{
QString logInfo = QString("chanid %1 at %2")
.arg(ds->chanid).arg(ds->recstartts.toString());
.arg(ds->m_chanid).arg(ds->m_recstartts.toString());

MSqlQuery query(MSqlQuery::InitCon());
query.prepare("DELETE FROM recorded WHERE chanid = :CHANID AND "
"title = :TITLE AND starttime = :STARTTIME;");
query.bindValue(":CHANID", ds->chanid);
query.bindValue(":TITLE", ds->title);
query.bindValue(":STARTTIME", ds->recstartts);
query.bindValue(":CHANID", ds->m_chanid);
query.bindValue(":TITLE", ds->m_title);
query.bindValue(":STARTTIME", ds->m_recstartts);

if (!query.exec() || !query.isActive())
{
Expand All @@ -2026,16 +2026,16 @@ void MainServer::DoDeleteInDB(const DeleteStruct *ds)

// Notify the frontend so it can requery for Free Space
QString msg = QString("RECORDING_LIST_CHANGE DELETE %1 %2")
.arg(ds->chanid).arg(ds->recstartts.toString(Qt::ISODate));
.arg(ds->m_chanid).arg(ds->m_recstartts.toString(Qt::ISODate));
RemoteSendEvent(MythEvent(msg));

// sleep a little to let frontends reload the recordings list
sleep(3);

query.prepare("DELETE FROM recordedmarkup "
"WHERE chanid = :CHANID AND starttime = :STARTTIME;");
query.bindValue(":CHANID", ds->chanid);
query.bindValue(":STARTTIME", ds->recstartts);
query.bindValue(":CHANID", ds->m_chanid);
query.bindValue(":STARTTIME", ds->m_recstartts);

if (!query.exec())
{
Expand All @@ -2047,8 +2047,8 @@ void MainServer::DoDeleteInDB(const DeleteStruct *ds)

query.prepare("DELETE FROM recordedseek "
"WHERE chanid = :CHANID AND starttime = :STARTTIME;");
query.bindValue(":CHANID", ds->chanid);
query.bindValue(":STARTTIME", ds->recstartts);
query.bindValue(":CHANID", ds->m_chanid);
query.bindValue(":STARTTIME", ds->m_recstartts);

if (!query.exec())
{
Expand Down Expand Up @@ -2520,19 +2520,12 @@ void MainServer::DoHandleDeleteRecording(
// most likely absent and deleting the file metadata is unsafe.
if (fileExists || !recinfo.GetFilesize() || forceMetadataDelete)
{
DeleteStruct *ds = new DeleteStruct;
ds->ms = this;
ds->filename = filename;
ds->title = recinfo.GetTitle();
ds->chanid = recinfo.GetChanID();
ds->recstartts = recinfo.GetRecordingStartTime();
ds->recendts = recinfo.GetRecordingEndTime();
ds->forceMetadataDelete = forceMetadataDelete;

recinfo.SaveDeletePendingFlag(true);

DeleteThread *deleteThread = new DeleteThread;
deleteThread->SetParent(ds);
DeleteThread *deleteThread = new DeleteThread(this, filename,
recinfo.GetTitle(), recinfo.GetChanID(),
recinfo.GetRecordingStartTime(), recinfo.GetRecordingEndTime(),
forceMetadataDelete);
deleteThread->start();
}
else
Expand Down Expand Up @@ -4492,24 +4485,20 @@ void MainServer::GetFilesystemInfos(vector <FileSystemInfo> &fsInfos)

void TruncateThread::run(void)
{
if (!m_parent)
if (!m_ms)
return;

MainServer *ms = m_parent->ms;
ms->DoTruncateThread(m_parent);

delete m_parent;
this->deleteLater();
m_ms->DoTruncateThread(this);
}

void MainServer::DoTruncateThread(const DeleteStruct *ds)
void MainServer::DoTruncateThread(DeleteStruct *ds)
{
if (gCoreContext->GetNumSetting("TruncateDeletesSlowly", 0))
TruncateAndClose(NULL, ds->fd, ds->filename, ds->size);
TruncateAndClose(NULL, ds->m_fd, ds->m_filename, ds->m_size);
else
{
QMutexLocker dl(&deletelock);
close(ds->fd);
close(ds->m_fd);
}
}

Expand Down Expand Up @@ -4584,15 +4573,9 @@ bool MainServer::HandleDeleteFile(QString filename, QString storagegroup,
// DeleteFile() opened up a file for us to delete
if (fd >= 0)
{
// Thread off the actual file delete
DeleteStruct *ds = new DeleteStruct;
ds->ms = this;
ds->filename = fullfile;
ds->fd = fd;
ds->size = size;

TruncateThread *truncateThread = new TruncateThread;
truncateThread->SetParent(ds);
// Thread off the actual file truncate
TruncateThread *truncateThread =
new TruncateThread(this, fullfile, fd, size);
truncateThread->run();
}

Expand Down

0 comments on commit 8ea47bc

Please sign in to comment.