void JobManager::Schedule(InternalJob::Pointer job, Poco::Timestamp::TimeDiff delay, bool reschedule) { if (!m_active) throw Poco::IllegalStateException("Job manager has been shut down."); poco_assert(job); // "Job is null" poco_assert(delay >= 0); // "Scheduling delay is negative" { Poco::ScopedLock<Poco::Mutex> managerLock (m_mutex); //if the job is already running, set it to be rescheduled when done if (job->GetState() == Job::RUNNING) { job->SetStartTime(delay); return; } //can't schedule a job that is waiting or sleeping if (job->InternalGetState() != Job::NONE) return; //remember that we are about to schedule the job //to prevent multiple schedule attempts from succeeding (bug 68452) InternalJob::Pointer sptr_job(job); ChangeState(sptr_job, InternalJob::ABOUT_TO_SCHEDULE); } //notify listeners outside sync block m_JobListeners.Scheduled(job.Cast<Job>(), delay, reschedule); //schedule the job DoSchedule(job, delay); //call the pool outside sync block to avoid deadlock m_Pool->JobQueued(); }
bool JobManager::Sleep(InternalJob::Pointer job) { { Poco::ScopedLock<Poco::Mutex> lockMe (m_mutex); InternalJob::Pointer sptr_job(job); switch (job->GetState()) { case Job::RUNNING : //cannot be paused if it is already running (as opposed to ABOUT_TO_RUN) if (job->InternalGetState() == Job::RUNNING) return false; //job hasn't started running yet (aboutToRun listener) break; case Job::SLEEPING : //update the job wake time job->SetStartTime(InternalJob::T_INFINITE); //change state again to re-shuffle the sleep queue ChangeState(sptr_job, Job::SLEEPING); return true; case Job::NONE : return true; case Job::WAITING : //put the job to sleep break; } job->SetStartTime(InternalJob::T_INFINITE); ChangeState(sptr_job, Job::SLEEPING); } m_JobListeners.Sleeping(job.Cast<Job>()); return true; }
bool JobManager::IsBlocking(InternalJob::Pointer sptr_runningJob) { { Poco::ScopedLock<Poco::Mutex> lockMe (m_mutex); // if this job isn't running, it can't be blocking anyone if (sptr_runningJob->GetState() != Job::RUNNING) return false; // if any job is queued behind this one, it is blocked by it InternalJob::Pointer ptr_previous = sptr_runningJob->Previous(); while (ptr_previous != 0) { // ignore jobs of lower priority (higher priority value means lower priority) if (ptr_previous->GetPriority() < sptr_runningJob->GetPriority()) { if (!ptr_previous->IsSystem()) return true; // TODO Implicit Jobs // implicit jobs should interrupt unless they act on behalf of system jobs // if (previous instanceof ThreadJob && ((ThreadJob) previous).shouldInterrupt()) // return true; } ptr_previous = ptr_previous->previous; } // none found return false; } }
bool JobManager::RunNow(InternalJob::Pointer sptr_job) { { Poco::ScopedLock<Poco::Mutex> lockMe (m_mutex); //cannot start if there is a conflicting job if (FindBlockingJob(sptr_job) != 0) return false; ChangeState(sptr_job, Job::RUNNING); sptr_job->SetProgressMonitor(IProgressMonitor::Pointer(new NullProgressMonitor())); sptr_job->Run(IProgressMonitor::Pointer(nullptr)); } return true; }
InternalJob::Pointer JobManager::FindBlockingJob(InternalJob::Pointer waitingJob) { if (waitingJob->GetRule() == 0) return InternalJob::Pointer(nullptr); { Poco::ScopedLock<Poco::Mutex> managerLock (m_mutex); if (m_running.empty() ) { InternalJob::Pointer dummy; return (dummy); } //check the running jobs bool hasBlockedJobs = false; QSet<InternalJob::Pointer>::Iterator it; for ( it = m_running.begin(); it != m_running.end(); it ++ ) { InternalJob::Pointer sptr_job = *it ++; if (waitingJob->IsConflicting(sptr_job)) return sptr_job; if (!hasBlockedJobs) hasBlockedJobs = sptr_job->Previous() != 0; } // there are no blocked jobs, so we are done if (!hasBlockedJobs) { InternalJob::Pointer dummy; return (dummy); } //check all jobs blocked by running jobs QSet<InternalJob::Pointer>::Iterator it_blocked; for( it_blocked = m_running.begin(); it_blocked != m_running.end(); it_blocked ++ ) { InternalJob::Pointer sptr_job = *it_blocked ++; while (true) { sptr_job = sptr_job->Previous(); if (sptr_job == 0) break; if (waitingJob->IsConflicting(sptr_job)) return sptr_job; } } } InternalJob::Pointer sptr_null; return (sptr_null); }
IProgressMonitor::Pointer JobManager::CreateMonitor(InternalJob::Pointer sptr_job, IProgressMonitor::Pointer group, int ticks) { { Poco::ScopedLock<Poco::Mutex> managerLock(m_mutex); //group must be set before the job is scheduled //this includes the ABOUT_TO_SCHEDULE state, during which it is still //valid to set the progress monitor if (sptr_job->GetState() != Job::NONE) { IProgressMonitor::Pointer dummy(nullptr); return dummy; } IProgressMonitor::Pointer sptr_monitor(nullptr); if (m_sptr_progressProvider != 0) sptr_monitor = m_sptr_progressProvider->CreateMonitor(sptr_job.Cast<Job>() , group, ticks); if (sptr_monitor == 0) { // return a default NullprogressMonitor NullProgressMonitor::Pointer sptr_defaultMonitor(new NullProgressMonitor() ); return sptr_defaultMonitor; } return sptr_monitor; } }
void JobManager::DoSchedule(InternalJob::Pointer job, Poco::Timestamp::TimeDiff delay) { Poco::ScopedLock<Poco::Mutex> managerLock(m_mutex); //job may have been canceled already int state = job->InternalGetState(); if (state != InternalJob::ABOUT_TO_SCHEDULE && state != Job::SLEEPING) return; //if it's a decoration job with no rule, don't run it right now if the system is busy if (job->GetPriority() == Job::DECORATE && job->GetRule() == 0) { Poco::Timestamp::TimeDiff tmp_minDelay = m_running.size() * 100; delay = std::max(delay, tmp_minDelay); } if (delay > 0) { job->SetStartTime(Poco::Timestamp() + delay * 100); InternalJob::Pointer sptr_job(job); ChangeState(sptr_job, Job::SLEEPING); } else { job->SetStartTime(Poco::Timestamp() + DelayFor(job->GetPriority()) * 100); job->SetWaitQueueStamp(m_waitQueueCounter++); InternalJob::Pointer sptr_job(job); ChangeState(sptr_job, Job::WAITING); } }
void JobManager::SetPriority(InternalJob::Pointer job, int newPriority) { { Poco::ScopedLock<Poco::Mutex> lockMe (m_mutex); InternalJob::Pointer sptr_job(job); int oldPriority = job->GetPriority(); if (oldPriority == newPriority) return; job->InternalSetPriority(newPriority); //if the job is waiting to run, re-shuffle the queue if (sptr_job->GetState() == Job::WAITING) { Poco::Timestamp oldStart = job->GetStartTime(); job->SetStartTime(oldStart += (DelayFor(newPriority) - DelayFor(oldPriority))); m_JobQueueWaiting.Resort(job); } } }
void JobManager::ReportBlocked(IProgressMonitor::Pointer sptr_monitor, InternalJob::Pointer sptr_blockingJob) const { if ( sptr_monitor.Cast<IProgressMonitorWithBlocking>() == 0 ) return ; if (sptr_blockingJob == 0 || sptr_blockingJob->IsSystem()) { Status::Pointer sptr_reason( new Status(IStatus::INFO_TYPE, JobManager::PI_JOBS(), 1, "the user operation is waiting for background work to complete" ) ); } else { std::stringstream msg ; msg << "the user operation is waiting for : " << sptr_blockingJob->GetName() << " to complete. " ; JobStatus::Pointer sptr_reason(new JobStatus(IStatus::INFO_TYPE, sptr_blockingJob.Cast<Job>(), msg.str() )); } // ((IProgressmonitorWithBlocking) sptr_monitor)->SetBlocked(sptr_reason); }
bool InternalJob::IsConflicting(InternalJob::Pointer otherJob) const { ISchedulingRule::Pointer otherRule = otherJob->GetRule(); if (sptr_schedulingRule.GetPointer() == 0 || otherRule.GetPointer() == 0) return false; // TODO MultiRule: extend the IsConflicting (...) method with MultiRule // if one of the rules is a compound rule, it must be asked the question. //if (schedulingRule.GetClass() == MultiRule.class) // return schedulingRule.IsConflicting(otherRule); return otherRule->IsConflicting(sptr_schedulingRule); }
bool JobQueue::CanOvertake(InternalJob::Pointer newEntry, InternalJob::Pointer queueEntry) { //can never go past the end of the queue if (queueEntry == dummy.GetPointer()) return false; //if the new entry was already in the wait queue, ensure it is re-inserted in correct position (bug 211799) if (newEntry->GetWaitQueueStamp() > 0 && newEntry->GetWaitQueueStamp() < queueEntry->GetWaitQueueStamp()) return true; //if the new entry has lower priority, there is no need to overtake the existing entry if ((queueEntry == newEntry)) return false; // the new entry has higher priority, but only overtake the existing entry if the queue allows it InternalJob::Pointer sptr_queueEntry(queueEntry); return m_allowConflictOvertaking || !newEntry->IsConflicting(sptr_queueEntry); }
void JobManager::SetRule(InternalJob::Pointer job, ISchedulingRule::Pointer sptr_rule) { Poco::ScopedLock<Poco::Mutex> m_managerLock(m_mutex); //cannot change the rule of a job that is already running ( GetRule is set to protected which should be // changed if this assert is needed // assert(job->GetState() == Job.NONE); ValidateRule(sptr_rule); job->InternalSetRule(sptr_rule); }
void JobQueue::Enqueue(InternalJob::Pointer newEntry) { InternalJob::Pointer tail = dummy->Next(); //overtake lower priority jobs. Only overtake conflicting jobs if allowed to while (CanOvertake(newEntry, tail)) tail = tail->Next(); InternalJob::Pointer tailPrevious = tail->Previous(); newEntry->SetNext(tail); newEntry->SetPrevious(tailPrevious); tailPrevious->SetNext(newEntry); tail->SetPrevious(newEntry); }
void InternalJob::AddLast(InternalJob::Pointer entry) { InternalJob::Pointer last; last = this; //find the end of the queue while (last->previous) last = last->previous; //add the new entry to the end of the queue last->previous = entry.GetPointer(); entry->next = last; entry->previous = 0; }
void JobManager::EndJob(InternalJob::Pointer ptr_job, IStatus::Pointer result, bool notify) { Poco::Timestamp::TimeDiff rescheduleDelay(InternalJob::T_NONE); { Poco::ScopedLock<Poco::Mutex> lock ( m_mutex); // if the job is finishing asynchronously, there is nothing more to do for now if (result == Job::ASYNC_FINISH) return; //if job is not known then it cannot be done if (ptr_job->GetState() == Job::NONE) return; ptr_job->SetResult(result); ptr_job->SetProgressMonitor(IProgressMonitor::Pointer(nullptr)); ptr_job->SetThread(nullptr); rescheduleDelay = ptr_job->GetStartTime().epochMicroseconds(); InternalJob::Pointer sptr_job(ptr_job); ChangeState(sptr_job, Job::NONE); } //notify listeners outside sync block bool reschedule = m_active && rescheduleDelay > InternalJob::T_NONE && ptr_job->ShouldSchedule(); if (notify) m_JobListeners.Done(ptr_job.Cast<Job>(), result, reschedule); //reschedule the job if requested and we are still active if (reschedule) Schedule(ptr_job, rescheduleDelay, reschedule); }
void JobManager::WakeUp(InternalJob::Pointer job, Poco::Timestamp::TimeDiff delay) { poco_assert(delay >= 0); // "Scheduling delay is negative" { Poco::ScopedLock<Poco::Mutex> m_managerLock (m_mutex); //cannot wake up if it is not sleeping if (job->GetState() != Job::SLEEPING) return; DoSchedule(job, delay); } //call the pool outside sync block to avoid deadlock m_Pool->JobQueued(); /// IListenerExtension only notify of wake up if immediate if (delay == 0) m_JobListeners.Awake(job.Cast<Job>()); }
bool JobManager::Cancel(InternalJob::Pointer sptr_job) { IProgressMonitor::Pointer sptr_progressMonitor(nullptr); bool runCanceling = false; { Poco::ScopedLock<Poco::Mutex> mangerMutex (m_mutex); switch (sptr_job->GetState()) { case Job::NONE : return true; case Job::RUNNING : //cannot cancel a job that has already started (as opposed to ABOUT_TO_RUN) if (sptr_job->InternalGetState() == Job::RUNNING) { sptr_progressMonitor = sptr_job->GetProgressMonitor(); runCanceling = sptr_job->IsRunCanceled(); if(runCanceling) sptr_job->SetRunCanceled(true); break ; } //signal that the job should be canceled before it gets a chance to run sptr_job->SetAboutToRunCanceled(true); return false; default : ChangeState(sptr_job, Job::NONE); } } //call monitor outside sync block if (sptr_progressMonitor != 0) { if(runCanceling) { if (!sptr_progressMonitor->IsCanceled()) sptr_progressMonitor->SetCanceled(true); sptr_job->Canceling(); } return false; } //only notify listeners if the job was waiting or sleeping m_JobListeners.Done(sptr_job.Cast<Job>(), Status::CANCEL_STATUS(BERRY_STATUS_LOC), false); return true; }
Job::Pointer JobManager::StartJob() { Job::Pointer job(nullptr); while (true) { job = NextJob(); if (!job) return Job::Pointer(nullptr); //must perform this outside sync block because it is third party code bool shouldRun = job->ShouldRun(); //check for listener veto if (shouldRun) m_JobListeners.AboutToRun(job); //listeners may have canceled or put the job to sleep bool endJob = false; { Poco::ScopedLock<Poco::Mutex> lock(m_mutex); InternalJob::Pointer internal = job; if (internal->InternalGetState() == InternalJob::ABOUT_TO_RUN) { if (shouldRun && !internal->IsAboutToRunCanceled()) { internal->SetProgressMonitor(CreateMonitor(job)); //change from ABOUT_TO_RUN to RUNNING internal->InternalSetState(Job::RUNNING); break; } internal->SetAboutToRunCanceled(false); endJob = true; //fall through and end the job below } } if (endJob) { //job has been vetoed or canceled, so mark it as done EndJob(job,Status::CANCEL_STATUS(BERRY_STATUS_LOC), true); continue; } } m_JobListeners.Running(job); return job; }
void JobQueue::Remove(InternalJob::Pointer jobToRemove) { jobToRemove->Remove(); }
void InternalJob::SetPrevious(InternalJob::Pointer entry) { this->previous = entry.GetPointer(); }
void JobManager::ChangeState(InternalJob::Pointer sptr_job, int newState) { bool blockedJobs = false; { Poco::ScopedLock<Poco::Mutex> m_managerLock(m_mutex); int tmp_oldState = sptr_job->InternalGetState(); switch (tmp_oldState) { case Job::NONE: case InternalJob::ABOUT_TO_SCHEDULE: break; case InternalJob::BLOCKED: //remove this job from the linked list of blocked jobs sptr_job->Remove(); break; case Job::WAITING: m_JobQueueWaiting.Remove(sptr_job); // assert(false, "Tried to remove a job that wasn't in the queue"); break; case Job::SLEEPING: m_JobQueueSleeping.Remove(sptr_job); // assert(false, "Tried to remove a job that wasn't in the queue"); case Job::RUNNING: case InternalJob::ABOUT_TO_RUN: m_running.remove(sptr_job); //add any blocked jobs back to the wait queue InternalJob::Pointer sptr_blocked(sptr_job->Previous()); sptr_job->Remove(); blockedJobs = sptr_blocked != 0; while (sptr_blocked != 0) { InternalJob::Pointer previous = sptr_blocked->Previous(); ChangeState(sptr_blocked, Job::WAITING); sptr_blocked = previous; } break; // default : // Assert.isLegal(false, "Invalid job state: " + job + ", state: " + oldState); } sptr_job->InternalSetState(newState); switch (newState) { case Job::NONE: sptr_job->SetStartTime(InternalJob::T_NONE); sptr_job->SetWaitQueueStamp(InternalJob::T_NONE); case InternalJob::BLOCKED: break; case Job::WAITING: m_JobQueueWaiting.Enqueue(sptr_job); break; case Job::SLEEPING: //try { m_JobQueueSleeping.Enqueue(sptr_job); //} catch (RuntimeException e) { // throw new RuntimeException("Error changing from state: " + oldState); //} break; case Job::RUNNING: case InternalJob::ABOUT_TO_RUN: sptr_job->SetStartTime(InternalJob::T_NONE); sptr_job->SetWaitQueueStamp(InternalJob::T_NONE); m_running.insert(sptr_job); break; case InternalJob::ABOUT_TO_SCHEDULE: break; // default : // Assert.isLegal(false, "Invalid job state: " + job + ", state: " + newState); } } //notify queue outside sync block if (blockedJobs) m_Pool->JobQueued(); }