jerrym | f157933 | 2013-02-07 01:56:28 +0000 | [diff] [blame] | 1 | /*----------------------------------------------------------------------------*/
|
| 2 | /* Copyright (c) FIRST 2008. All Rights Reserved. */
|
| 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */
|
| 4 | /* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
| 5 | /*----------------------------------------------------------------------------*/
|
| 6 |
|
| 7 | #include "Notifier.h"
|
| 8 | #include "Timer.h"
|
| 9 | #include "Utility.h"
|
| 10 | #include "WPIErrors.h"
|
| 11 |
|
| 12 | const UINT32 Notifier::kTimerInterruptNumber;
|
| 13 | Notifier *Notifier::timerQueueHead = NULL;
|
| 14 | ReentrantSemaphore Notifier::queueSemaphore;
|
| 15 | tAlarm *Notifier::talarm = NULL;
|
| 16 | tInterruptManager *Notifier::manager = NULL;
|
| 17 | int Notifier::refcount = 0;
|
| 18 |
|
| 19 | /**
|
| 20 | * Create a Notifier for timer event notification.
|
| 21 | * @param handler The handler is called at the notification time which is set
|
| 22 | * using StartSingle or StartPeriodic.
|
| 23 | */
|
| 24 | Notifier::Notifier(TimerEventHandler handler, void *param)
|
| 25 | {
|
| 26 | if (handler == NULL)
|
| 27 | wpi_setWPIErrorWithContext(NullParameter, "handler must not be NULL");
|
| 28 | m_handler = handler;
|
| 29 | m_param = param;
|
| 30 | m_periodic = false;
|
| 31 | m_expirationTime = 0;
|
| 32 | m_period = 0;
|
| 33 | m_nextEvent = NULL;
|
| 34 | m_queued = false;
|
| 35 | m_handlerSemaphore = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
|
| 36 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 37 | {
|
| 38 | Synchronized sync(queueSemaphore);
|
| 39 | // do the first time intialization of static variables
|
| 40 | if (refcount == 0)
|
| 41 | {
|
| 42 | manager = new tInterruptManager(1 << kTimerInterruptNumber, false, &localStatus);
|
| 43 | manager->registerHandler(ProcessQueue, NULL, &localStatus);
|
| 44 | manager->enable(&localStatus);
|
| 45 | talarm = tAlarm::create(&localStatus);
|
| 46 | }
|
| 47 | refcount++;
|
| 48 | }
|
| 49 | wpi_setError(localStatus);
|
| 50 | }
|
| 51 |
|
| 52 | /**
|
| 53 | * Free the resources for a timer event.
|
| 54 | * All resources will be freed and the timer event will be removed from the
|
| 55 | * queue if necessary.
|
| 56 | */
|
| 57 | Notifier::~Notifier()
|
| 58 | {
|
| 59 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 60 | {
|
| 61 | Synchronized sync(queueSemaphore);
|
| 62 | DeleteFromQueue();
|
| 63 |
|
| 64 | // Delete the static variables when the last one is going away
|
| 65 | if (!(--refcount))
|
| 66 | {
|
| 67 | talarm->writeEnable(false, &localStatus);
|
| 68 | delete talarm;
|
| 69 | talarm = NULL;
|
| 70 | manager->disable(&localStatus);
|
| 71 | delete manager;
|
| 72 | manager = NULL;
|
| 73 | }
|
| 74 | }
|
| 75 | wpi_setError(localStatus);
|
| 76 |
|
| 77 | // Acquire the semaphore; this makes certain that the handler is
|
| 78 | // not being executed by the interrupt manager.
|
| 79 | semTake(m_handlerSemaphore, WAIT_FOREVER);
|
| 80 | // Delete while holding the semaphore so there can be no race.
|
| 81 | semDelete(m_handlerSemaphore);
|
| 82 | }
|
| 83 |
|
| 84 | /**
|
| 85 | * Update the alarm hardware to reflect the current first element in the queue.
|
| 86 | * Compute the time the next alarm should occur based on the current time and the
|
| 87 | * period for the first element in the timer queue.
|
| 88 | * WARNING: this method does not do synchronization! It must be called from somewhere
|
| 89 | * that is taking care of synchronizing access to the queue.
|
| 90 | */
|
| 91 | void Notifier::UpdateAlarm()
|
| 92 | {
|
| 93 | if (timerQueueHead != NULL)
|
| 94 | {
|
| 95 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 96 | // write the first item in the queue into the trigger time
|
| 97 | talarm->writeTriggerTime((UINT32)(timerQueueHead->m_expirationTime * 1e6), &localStatus);
|
| 98 | // Enable the alarm. The hardware disables itself after each alarm.
|
| 99 | talarm->writeEnable(true, &localStatus);
|
| 100 | wpi_setStaticError(timerQueueHead, localStatus);
|
| 101 | }
|
| 102 | }
|
| 103 |
|
| 104 | /**
|
| 105 | * ProcessQueue is called whenever there is a timer interrupt.
|
| 106 | * We need to wake up and process the current top item in the timer queue as long
|
| 107 | * as its scheduled time is after the current time. Then the item is removed or
|
| 108 | * rescheduled (repetitive events) in the queue.
|
| 109 | */
|
| 110 | void Notifier::ProcessQueue(uint32_t mask, void *params)
|
| 111 | {
|
| 112 | Notifier *current;
|
| 113 | while (true) // keep processing past events until no more
|
| 114 | {
|
| 115 | {
|
| 116 | Synchronized sync(queueSemaphore);
|
| 117 | double currentTime = GetClock();
|
| 118 | current = timerQueueHead;
|
| 119 | if (current == NULL || current->m_expirationTime > currentTime)
|
| 120 | {
|
| 121 | break; // no more timer events to process
|
| 122 | }
|
| 123 | // need to process this entry
|
| 124 | timerQueueHead = current->m_nextEvent;
|
| 125 | if (current->m_periodic)
|
| 126 | {
|
| 127 | // if periodic, requeue the event
|
| 128 | // compute when to put into queue
|
| 129 | current->InsertInQueue(true);
|
| 130 | }
|
| 131 | else
|
| 132 | {
|
| 133 | // not periodic; removed from queue
|
| 134 | current->m_queued = false;
|
| 135 | }
|
| 136 | // Take handler semaphore while holding queue semaphore to make sure
|
| 137 | // the handler will execute to completion in case we are being deleted.
|
| 138 | semTake(current->m_handlerSemaphore, WAIT_FOREVER);
|
| 139 | }
|
| 140 |
|
| 141 | current->m_handler(current->m_param); // call the event handler
|
| 142 | semGive(current->m_handlerSemaphore);
|
| 143 | }
|
| 144 | // reschedule the first item in the queue
|
| 145 | Synchronized sync(queueSemaphore);
|
| 146 | UpdateAlarm();
|
| 147 | }
|
| 148 |
|
| 149 | /**
|
| 150 | * Insert this Notifier into the timer queue in right place.
|
| 151 | * WARNING: this method does not do synchronization! It must be called from somewhere
|
| 152 | * that is taking care of synchronizing access to the queue.
|
| 153 | * @param reschedule If false, the scheduled alarm is based on the curent time and UpdateAlarm
|
| 154 | * method is called which will enable the alarm if necessary.
|
| 155 | * If true, update the time by adding the period (no drift) when rescheduled periodic from ProcessQueue.
|
| 156 | * This ensures that the public methods only update the queue after finishing inserting.
|
| 157 | */
|
| 158 | void Notifier::InsertInQueue(bool reschedule)
|
| 159 | {
|
| 160 | if (reschedule)
|
| 161 | {
|
| 162 | m_expirationTime += m_period;
|
| 163 | }
|
| 164 | else
|
| 165 | {
|
| 166 | m_expirationTime = GetClock() + m_period;
|
| 167 | }
|
| 168 | if (timerQueueHead == NULL || timerQueueHead->m_expirationTime >= this->m_expirationTime)
|
| 169 | {
|
| 170 | // the queue is empty or greater than the new entry
|
| 171 | // the new entry becomes the first element
|
| 172 | this->m_nextEvent = timerQueueHead;
|
| 173 | timerQueueHead = this;
|
| 174 | if (!reschedule)
|
| 175 | {
|
| 176 | // since the first element changed, update alarm, unless we already plan to
|
| 177 | UpdateAlarm();
|
| 178 | }
|
| 179 | }
|
| 180 | else
|
| 181 | {
|
| 182 | for (Notifier **npp = &(timerQueueHead->m_nextEvent); ; npp = &(*npp)->m_nextEvent)
|
| 183 | {
|
| 184 | Notifier *n = *npp;
|
| 185 | if (n == NULL || n->m_expirationTime > this->m_expirationTime)
|
| 186 | {
|
| 187 | *npp = this;
|
| 188 | this->m_nextEvent = n;
|
| 189 | break;
|
| 190 | }
|
| 191 | }
|
| 192 | }
|
| 193 | m_queued = true;
|
| 194 | }
|
| 195 |
|
| 196 | /**
|
| 197 | * Delete this Notifier from the timer queue.
|
| 198 | * WARNING: this method does not do synchronization! It must be called from somewhere
|
| 199 | * that is taking care of synchronizing access to the queue.
|
| 200 | * Remove this Notifier from the timer queue and adjust the next interrupt time to reflect
|
| 201 | * the current top of the queue.
|
| 202 | */
|
| 203 | void Notifier::DeleteFromQueue()
|
| 204 | {
|
| 205 | if (m_queued)
|
| 206 | {
|
| 207 | m_queued = false;
|
| 208 | wpi_assert(timerQueueHead != NULL);
|
| 209 | if (timerQueueHead == this)
|
| 210 | {
|
| 211 | // remove the first item in the list - update the alarm
|
| 212 | timerQueueHead = this->m_nextEvent;
|
| 213 | UpdateAlarm();
|
| 214 | }
|
| 215 | else
|
| 216 | {
|
| 217 | for (Notifier *n = timerQueueHead; n != NULL; n = n->m_nextEvent)
|
| 218 | {
|
| 219 | if (n->m_nextEvent == this)
|
| 220 | {
|
| 221 | // this element is the next element from *n from the queue
|
| 222 | n->m_nextEvent = this->m_nextEvent; // point around this one
|
| 223 | }
|
| 224 | }
|
| 225 | }
|
| 226 | }
|
| 227 | }
|
| 228 |
|
| 229 | /**
|
| 230 | * Register for single event notification.
|
| 231 | * A timer event is queued for a single event after the specified delay.
|
| 232 | * @param delay Seconds to wait before the handler is called.
|
| 233 | */
|
| 234 | void Notifier::StartSingle(double delay)
|
| 235 | {
|
| 236 | Synchronized sync(queueSemaphore);
|
| 237 | m_periodic = false;
|
| 238 | m_period = delay;
|
| 239 | DeleteFromQueue();
|
| 240 | InsertInQueue(false);
|
| 241 | }
|
| 242 |
|
| 243 | /**
|
| 244 | * Register for periodic event notification.
|
| 245 | * A timer event is queued for periodic event notification. Each time the interrupt
|
| 246 | * occurs, the event will be immediately requeued for the same time interval.
|
| 247 | * @param period Period in seconds to call the handler starting one period after the call to this method.
|
| 248 | */
|
| 249 | void Notifier::StartPeriodic(double period)
|
| 250 | {
|
| 251 | Synchronized sync(queueSemaphore);
|
| 252 | m_periodic = true;
|
| 253 | m_period = period;
|
| 254 | DeleteFromQueue();
|
| 255 | InsertInQueue(false);
|
| 256 | }
|
| 257 |
|
| 258 | /**
|
| 259 | * Stop timer events from occuring.
|
| 260 | * Stop any repeating timer events from occuring. This will also remove any single
|
| 261 | * notification events from the queue.
|
| 262 | * If a timer-based call to the registered handler is in progress, this function will
|
| 263 | * block until the handler call is complete.
|
| 264 | */
|
| 265 | void Notifier::Stop()
|
| 266 | {
|
| 267 | {
|
| 268 | Synchronized sync(queueSemaphore);
|
| 269 | DeleteFromQueue();
|
| 270 | }
|
| 271 | // Wait for a currently executing handler to complete before returning from Stop()
|
| 272 | Synchronized sync(m_handlerSemaphore);
|
| 273 | }
|