blob: 008d8f81d17e8afc621c43cd9291d8c44ca95b0d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Timer.h"
#include <decaf/lang/Thread.h>
#include <decaf/lang/Pointer.h>
#include <decaf/lang/System.h>
#include <decaf/util/concurrent/Mutex.h>
#include <decaf/internal/util/TimerTaskHeap.h>
#include <decaf/internal/util/concurrent/SynchronizableImpl.h>
#include <decaf/lang/exceptions/InterruptedException.h>
using namespace decaf;
using namespace decaf::util;
using namespace decaf::util::concurrent;
using namespace decaf::internal;
using namespace decaf::internal::util;
using namespace decaf::internal::util::concurrent;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
////////////////////////////////////////////////////////////////////////////////
namespace decaf {
namespace util {
class TimerImpl: public decaf::lang::Thread, public SynchronizableImpl {
public:
TimerTaskHeap heap;
bool cancelled;
public:
TimerImpl() : Thread(), heap(), cancelled(false) {}
TimerImpl(const std::string& name) : Thread(name), heap(), cancelled(false) {}
virtual ~TimerImpl() {
try {
this->cancel();
this->join();
}
DECAF_CATCHALL_NOTHROW()
}
/**
* This method will be launched on separate thread for each Timer
* object.
*/
virtual void run() {
while (true) {
Pointer<TimerTask> task;
synchronized(this) {
if (cancelled) {
return;
}
if (heap.isEmpty()) {
// no tasks scheduled -- sleep until any task appear
try {
this->wait();
} catch (InterruptedException& e) {}
continue;
}
long long currentTime = System::currentTimeMillis();
task = heap.peek();
long long timeToSleep = 0LL;
synchronized(&(task->lock)) {
if (task->cancelled) {
heap.remove(0);
continue;
}
// check the time to sleep for the first task scheduled
timeToSleep = task->when - currentTime;
}
if (timeToSleep > 0) {
task.reset(NULL);
try {
this->wait(timeToSleep);
} catch (InterruptedException& e) {
}
continue;
}
// Time to run the task, but first we need to check to see if any other
// tasks where scheduled and caused this one to be moved someplace new on
// the heap and find it new location.. We also need to check that the task
// wasn't canceled while we were sleeping.
synchronized(&(task->lock)) {
std::size_t pos = 0;
if (heap.peek()->when != task->when) {
pos = heap.find(task);
}
if (task->cancelled) {
heap.remove(heap.find(task));
continue;
}
// set time to schedule
task->setScheduledTime(task->when);
// remove task from queue
heap.remove(pos);
// set when the next task should be launched
if (task->period >= 0) {
// this is a repeating task,
if (task->fixedRate) {
// task is scheduled at fixed rate
task->when = task->when + task->period;
} else {
// task is scheduled at fixed delay
task->when = System::currentTimeMillis() + task->period;
}
// insert this task into queue, it will be ordered by the heap for
// its next run time.
insertTask(task);
} else {
// Task was a one-shot, setting when to zero indicates it
// won't run anymore.
task->when = 0;
}
}
}
// run the task, suppress all exceptions, we can't deal with them.
if (task != NULL && !task->cancelled) {
try {
task->run();
} catch(...) {
}
}
}
}
void insertTask(const Pointer<TimerTask>& task) {
// callers are synchronized
heap.insert(task);
this->notify();
}
void cancel() {
synchronized(this) {
cancelled = true;
heap.reset();
this->notify();
}
}
int purge() {
std::size_t result = 0;
synchronized(this) {
if (heap.isEmpty()) {
return 0;
}
result = heap.deleteIfCancelled();
}
return (int)result;
}
};
}}
////////////////////////////////////////////////////////////////////////////////
Timer::Timer() : internal(new TimerImpl()) {
try {
this->internal->start();
} catch(...) {
delete this->internal;
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
Timer::Timer(const std::string& name) : internal(new TimerImpl(name)) {
try {
this->internal->start();
} catch(...) {
delete this->internal;
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
Timer::~Timer() {
try {
delete this->internal;
}
DECAF_CATCH_NOTHROW(Exception)
DECAF_CATCHALL_NOTHROW()
}
////////////////////////////////////////////////////////////////////////////////
void Timer::cancel() {
this->internal->cancel();
}
////////////////////////////////////////////////////////////////////////////////
bool Timer::awaitTermination(long long timeout, const TimeUnit& unit) {
if (!this->internal->isAlive()) {
return true;
}
this->internal->join(unit.toMillis(timeout));
return this->internal->isAlive();
}
////////////////////////////////////////////////////////////////////////////////
int Timer::purge() {
return this->internal->purge();
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(TimerTask* task, long long delay) {
if (delay < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
Pointer<TimerTask> wrapper(task);
try {
scheduleTask(wrapper, delay, -1, false);
} catch (Exception& ex) {
wrapper.release();
ex.setMark(__FILE__, __LINE__);
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(const Pointer<TimerTask>& task, long long delay) {
if (delay < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
scheduleTask(task, delay, -1, false);
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(TimerTask* task, const Date& when) {
if (when.getTime() < 0) {
throw IllegalArgumentException(__FILE__, __LINE__, "Task must be scheduled to start in the Future.");
}
Pointer<TimerTask> wrapper(task);
long long delay = when.getTime() - System::currentTimeMillis();
try {
scheduleTask(wrapper, delay < 0 ? 0 : delay, -1, false);
} catch (Exception& ex) {
wrapper.release();
ex.setMark(__FILE__, __LINE__);
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(const Pointer<TimerTask>& task, const Date& when) {
if (when.getTime() < 0) {
throw IllegalArgumentException(__FILE__, __LINE__, "Task must be scheduled to start in the Future.");
}
long long delay = when.getTime() - System::currentTimeMillis();
scheduleTask(task, delay < 0 ? 0 : delay, -1, false);
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(TimerTask* task, long long delay, long long period) {
if (delay < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
Pointer<TimerTask> wrapper(task);
try {
scheduleTask(wrapper, delay, period, false);
} catch (Exception& ex) {
wrapper.release();
ex.setMark(__FILE__, __LINE__);
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(const Pointer<TimerTask>& task, long long delay, long long period) {
if (delay < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
scheduleTask(task, delay, period, false);
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(TimerTask* task, const Date& when, long long period) {
if (when.getTime() < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future.");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
Pointer<TimerTask> wrapper(task);
long long delay = when.getTime() - System::currentTimeMillis();
try {
scheduleTask(wrapper, delay < 0 ? 0 : delay, period, false);
} catch (Exception& ex) {
wrapper.release();
ex.setMark(__FILE__, __LINE__);
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::schedule(const Pointer<TimerTask>& task, const Date& when, long long period) {
if (when.getTime() < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future.");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
long long delay = when.getTime() - System::currentTimeMillis();
scheduleTask(task, delay < 0 ? 0 : delay, period, false);
}
////////////////////////////////////////////////////////////////////////////////
void Timer::scheduleAtFixedRate(TimerTask* task, long long delay, long long period) {
if (delay < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
Pointer<TimerTask> wrapper(task);
try {
scheduleTask(wrapper, delay, period, true);
} catch (Exception& ex) {
wrapper.release();
ex.setMark(__FILE__, __LINE__);
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::scheduleAtFixedRate(const Pointer<TimerTask>& task, long long delay, long long period) {
if (delay < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
scheduleTask(task, delay, period, true);
}
////////////////////////////////////////////////////////////////////////////////
void Timer::scheduleAtFixedRate(TimerTask* task, const Date& when, long long period) {
if (when.getTime() < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future.");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
Pointer<TimerTask> wrapper(task);
long long delay = when.getTime() - System::currentTimeMillis();
try {
scheduleTask(wrapper, delay < 0 ? 0 : delay, period, true);
} catch (Exception& ex) {
wrapper.release();
ex.setMark(__FILE__, __LINE__);
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::scheduleAtFixedRate(const Pointer<TimerTask>& task, const Date& when, long long period) {
if (when.getTime() < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future.");
}
if (period <= 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled non-negative or non-zero period.");
}
long long delay = when.getTime() - System::currentTimeMillis();
scheduleTask(task, delay < 0 ? 0 : delay, period, true);
}
////////////////////////////////////////////////////////////////////////////////
void Timer::scheduleTask(const Pointer<TimerTask>& task, long long delay, long long period, bool fixed) {
if (task == NULL) {
throw NullPointerException(__FILE__, __LINE__, "Task pointer passed in was Null");
}
synchronized(this->internal) {
if (this->internal->cancelled) {
throw IllegalStateException(__FILE__, __LINE__, "Timer was cancelled.");
}
long long when = delay + System::currentTimeMillis();
if (when < 0) {
throw IllegalArgumentException(__FILE__, __LINE__,
"Task must be scheduled to start in the Future but delay was Negative");
}
synchronized(&(task->lock)) {
if (task->isScheduled()) {
throw IllegalStateException(__FILE__, __LINE__,
"Task is already scheduled in a Timer, cannot add again.");
}
if (task->cancelled) {
throw IllegalStateException(__FILE__, __LINE__,
"Task is already has been cancelled cannot be restarted.");
}
task->when = when;
task->period = period;
task->fixedRate = fixed;
}
// insert the new Task into priority queue
this->internal->insertTask(task);
}
}