barrier/lib/base/CJobList.cpp

114 lines
3.1 KiB
C++

/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CJobList.h"
#include "IJob.h"
#include "CArch.h"
//
// CJobList
//
CJobList::CJobList()
{
m_mutex = ARCH->newMutex();
}
CJobList::~CJobList()
{
ARCH->closeMutex(m_mutex);
}
void
CJobList::addJob(IJob* job)
{
// ignore bogus job
if (job == NULL) {
return;
}
// make a temporary list with the job. later we'll splice this
// list into our jobs list. since splice() never throws we
// don't have to try/catch to unlock the mutex.
CJobs tmpList;
tmpList.push_front(job);
// add job to list
ARCH->lockMutex(m_mutex);
m_jobs.splice(m_jobs.begin(), tmpList);
ARCH->unlockMutex(m_mutex);
}
void
CJobList::removeJob(IJob* job)
{
if (job != NULL) {
ARCH->lockMutex(m_mutex);
m_jobs.remove(job);
ARCH->unlockMutex(m_mutex);
}
}
void
CJobList::runJobs() const
{
// this is a little tricky. to allow any number of threads to
// traverse the list while jobs are added and removed while
// not holding the mutex when running a job (so other threads
// or the running job can add and remove jobs), we insert a
// new element into the list. this element has a NULL job and
// is a "safe place" while we traverse the list. the safe place
// is inserted at the start of the list (with the mutex locked)
// then, for each job, we lock the mutex and move the safe place
// past the next job, unlock the mutex, run the job and repeat
// until there are no more jobs. the safe place will not be
// removed by any other thread and is after every job that has
// been run and before every job that still needs to run. when
// all the jobs have been run we remove the safe place.
// add the safe place
CJobs tmpList;
tmpList.push_front(NULL);
ARCH->lockMutex(m_mutex);
m_jobs.splice(m_jobs.begin(), tmpList);
CJobs::iterator safePlace = m_jobs.begin();
// find the next non-NULL job (NULL jobs are safe places)
CJobs::iterator next = safePlace;
while (next != m_jobs.end() && *next == NULL)
++next;
while (next != m_jobs.end()) {
// found a job. run it without holding a lock. note the
// race condition here: we release the lock, allowing
// removeJob() to remove this job before we run the job.
// therefore the caller cannot safely destroy the job
// until all runJobs() complete.
IJob* job = *next;
++next;
m_jobs.splice(next, m_jobs, safePlace);
ARCH->unlockMutex(m_mutex);
job->run();
// next real job
ARCH->lockMutex(m_mutex);
next = safePlace;
while (next != m_jobs.end() && *next == NULL)
++next;
}
// remove the safe place
m_jobs.erase(safePlace);
ARCH->unlockMutex(m_mutex);
}