/* * 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 #include #include #include #include #include #include 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 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& 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 wrapper(task); try { scheduleTask(wrapper, delay, -1, false); } catch (Exception& ex) { wrapper.release(); ex.setMark(__FILE__, __LINE__); throw; } } //////////////////////////////////////////////////////////////////////////////// void Timer::schedule(const Pointer& 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 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& 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 wrapper(task); try { scheduleTask(wrapper, delay, period, false); } catch (Exception& ex) { wrapper.release(); ex.setMark(__FILE__, __LINE__); throw; } } //////////////////////////////////////////////////////////////////////////////// void Timer::schedule(const Pointer& 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 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& 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 wrapper(task); try { scheduleTask(wrapper, delay, period, true); } catch (Exception& ex) { wrapper.release(); ex.setMark(__FILE__, __LINE__); throw; } } //////////////////////////////////////////////////////////////////////////////// void Timer::scheduleAtFixedRate(const Pointer& 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 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& 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& 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); } }