blob: 1d04bc050de79cc4db8d5c76dd8ae40fba20dfc3 [file] [log] [blame]
Ernesto Posse8a4f2962015-05-12 13:28:46 -04001// umlrttimerqueue.cc
2
3/*******************************************************************************
4* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
5* All rights reserved. This program and the accompanying materials
6* are made available under the terms of the Eclipse Public License v1.0
7* which accompanies this distribution, and is available at
8* http://www.eclipse.org/legal/epl-v10.html
9*******************************************************************************/
10
11#include "basefatal.hh"
12#include "basedebug.hh"
13#include "umlrtapi.hh"
14#include "umlrtguard.hh"
15#include "umlrttimer.hh"
16#include "umlrttimerqueue.hh"
17#include <stdlib.h>
18#include <unistd.h>
19#include <fcntl.h>
20#include <sys/ioctl.h>
Barry Maher7560f242015-06-04 19:50:39 -040021#include <sys/socket.h>
Ernesto Posse8a4f2962015-05-12 13:28:46 -040022
23// See umlrttimerqueue.hh for documentation.
24
25UMLRTTimerQueue::UMLRTTimerQueue() : UMLRTQueue()
26{
27 // Appenders write the pipe. Controllers 'wait' on a select() on the pipe with
28 // a timeout associated with any running timer. A write on the pipe wakes the controller.
29 if (pipe(notifyFd) < 0)
30 {
31 FATAL_ERRNO("pipe");
32 }
33 // Bytes available in the pipe wake up the controller as 'notify'. The controller
34 // has to be able to clean out the pipe (read) without blocking.
35 int flags = fcntl(notifyFd[0], F_GETFL, 0);
36 if (flags < 0)
37 {
38 FATAL_ERRNO("fcntl F_GETFL");
39 }
40 if (fcntl(notifyFd[0], F_SETFL, flags | O_NONBLOCK) < 0)
41 {
42 FATAL_ERRNO("fcntl F_SETFL");
43 }
44}
45
46// Remove the first timer on the queue. Returns NULL if first timer has not yet expired.
47UMLRTTimer * UMLRTTimerQueue::dequeue()
48{
49 UMLRTGuard(getMutex());
50
51 UMLRTTimer * first = (UMLRTTimer *)head;
52
53 if (first)
54 {
55 UMLRTTimespec now;
56 UMLRTTimespec::getClock(&now);
57
58 if (now >= first->due)
59 {
60 // First timer is due - dequeue it and return it.
61 head = first->next;
62 if (head == NULL)
63 {
64 tail = NULL; // Not required, but cleaner.
65 }
66 BDEBUG(BD_TIMER, "this(%p) dequeue found first timer due.\n", this);
67 }
68 else
69 {
70 // First timer still running - leave it there.
71 first = NULL;
72 BDEBUG(BD_TIMER, "this(%p) dequeue found first timer still running.\n", this);
73 }
74 }
75 else
76 {
77 BDEBUG(BD_TIMER, "this(%p) dequeue found no timers.\n", this);
78 }
79 return first;
80}
81
82// Add a timer to the queue in the order of when they will expire.
83void UMLRTTimerQueue::enqueue( const UMLRTTimer * timer )
84{
85 UMLRTGuard(getMutex());
86
87 UMLRTTimer * next = (UMLRTTimer *)head;
88 UMLRTTimer * previous = NULL;
89
90 char tmbuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
91 BDEBUG(BD_TIMER, "this(%p) timer-enqueue due(%s)\n", this, timer->due.toString(tmbuf,sizeof(tmbuf)));
92
93 timer->next = NULL; // Initialize as last-in-queue.
94
95 // Only need to notify the controller if the new timer ends up at the head of the queue.
96 // The wait mechanism is only interested in the time remaining for the timer at the head of the queue.
97 if (!head)
98 {
99 // List was empty. Put it in there as only element.
100 head = tail = timer;
101 sendNotification();
102 }
103 else
104 {
105 // Skip ahead until we meet a timer due after this one.
106 while (next && (next->due <= timer->due))
107 {
108 previous = next;
109 next = (UMLRTTimer *)next->next;
110 }
111 if (!next)
112 {
113 // We're appending this timer to the end of the queue.
114 tail->next = timer;
115 tail = timer;
116 }
117 else if (!previous)
118 {
119 // This timer is before the first element in the queue - prepend it.
120 timer->next = head;
121 head = timer;
122 sendNotification();
123 }
124 else
125 {
126 // This timer goes after 'previous' and before 'next'.
127 previous->next = timer;
128 timer->next = next;
129 }
130 }
131}
132
133// Calculate how much time left before timer on the head of the queue is due.
134UMLRTTimespec UMLRTTimerQueue::timeRemaining() const
135{
136 UMLRTGuard(getMutex());
137
138 // NOTE: Intended only for the consumer of the queue elements which has confirmed
139 // the queue was non-empty. An alternate implementation is required if an empty queue
140 // is possible.
141 if (isEmpty())
142 {
143 FATAL("timer queue was empty in timeRemaining()");
144 }
145 UMLRTTimespec now;
146 UMLRTTimespec::getClock(&now);
147
148 BDEBUG(BD_TIMER, "this(%p) head(%p) tail(%p)\n", this, head, tail);
149
150 UMLRTTimespec remain = ((UMLRTTimer*)head)->due - now;
151
152 char tmbuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
153 BDEBUG(BD_TIMER, "timeRemaining %s\n", remain.toStringRelative(tmbuf, sizeof(tmbuf)));
154
155 return remain;
156}
157
158// Add a timer to the queue in the order of when they will expire.
159bool UMLRTTimerQueue::cancel( UMLRTTimerId id )
160{
161 UMLRTGuard(getMutex());
162
163 bool ok = false;
164 UMLRTTimer * next = (UMLRTTimer *)head;
165 UMLRTTimer * previous = NULL;
166
167 while (next && (next != id.getTimer()))
168 {
169 previous = next;
170 next = (UMLRTTimer *)next->next;
171 }
172 // Only need to notify the controller if the cancelled timer was at the head of the queue.
173 if (next)
174 {
175 // Found the one to delete.
176 if (!previous)
177 {
178 // This timer was at the head of the queue.
179 head = (UMLRTTimer *)next->next;
180 if (head == NULL)
181 {
182 tail = NULL; // Not strictly required, but cleaner.
183 }
184 sendNotification();
185 }
186 else
187 {
188 // Unlink the timer being deallocated by setting next of previous to be
189 // the cancelled timer's next.
190 previous->next = next->next;
191
192 // If the timer was last in the queue, the tail has to be updated.
193 if (tail == next)
194 {
195 tail = previous;
196 }
197 }
198 // Return it to the pool.
199 umlrt::TimerPutToPool(next);
200
201 ok = true;
202 }
203 return ok;
204}
205
206void UMLRTTimerQueue::sendNotification()
207{
208 int bytes_ready;
209
210 if (ioctl(notifyFd[0], FIONREAD, &bytes_ready) < 0)
211 {
212 FATAL_ERRNO("ioctl");
213 }
214 if (!bytes_ready)
215 {
216 // Write a byte to the notification-pipe as a notification of a pending message.
217 uint8_t notifyByte = 0;
218
219 if (write(notifyFd[1], &notifyByte, 1) < 0)
220 {
221 FATAL_ERRNO("write");
222 }
223 }
224}
225
226// See umlrtprioritymessagequeue.hh for documentation.
227void UMLRTTimerQueue::clearNotifyFd()
228{
229 uint8_t ignore;
230 int bytes;
231 if ((bytes = read(notifyFd[0], &ignore, 1)) < 0)
232 {
233 FATAL_ERRNO("initial read - synchronization mechanism implies a byte is waiting");
234 }
235 else if (!bytes)
236 {
237 // Indicates synchronization logic error. Should always get at least one byte.
238 FATAL("Should never find pipe empty when clearing a notification.");
239 }
240 // Clear out the notification-pipe.
241 // Can shrink this after notify is debugged.
242 bool done = false;
243 while (!done)
244 {
245 int bytes = read(notifyFd[0], &ignore, 1);
246 if (bytes < 0)
247 {
248 if (errno != EAGAIN)
249 {
250 FATAL_ERRNO("read");
251 }
252 else
253 {
254 done = true;
255 }
256 }
257 if (bytes > 0)
258 {
259 // BDEBUG(0, "read returned bytes(%d)\n", bytes);
260 }
261 else
262 {
263 done = true;
264 }
265 }
266}
267
268// See umlrtprioritymessagequeue.hh for documentation.
269int UMLRTTimerQueue::getNotifyFd()
270{
271 return notifyFd[0];
272}
273
274