114 lines
3.1 KiB
C++
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);
|
||
|
}
|