1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2010-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /******************************* DisplayPort *******************************\
25 * *
26 * Module: dp_timer.cpp *
27 * *
28 \***************************************************************************/
29 #include "dp_internal.h"
30 #include "dp_timer.h"
31 using namespace DisplayPort;
32
expired()33 void Timer::expired()
34 {
35 fire(false);
36 }
37
38 // Take care, this function is re-entrant.
39 // Consider that sleep() is effectively a call to fire().
40 // Clients may sleep in response to a timer callback.
fire(bool fromSleep)41 unsigned Timer::fire(bool fromSleep) // returns min time to next item to be fired
42 {
43 restart:
44
45 NvU64 now = getTimeUs();
46 NvU64 nearest = (NvU64)-1;
47 for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); )
48 {
49 if (fromSleep && !i->executeInSleep) {
50 i = (PendingCallback*)i->next;
51 continue;
52 }
53
54 if (now >= i->timestamp)
55 {
56 const void * context = i->context;
57 TimerCallback * target = i->target;
58 delete i;
59 if (target)
60 target->expired(context); // Take care, the client may have made
61 // a recursive call to fire in here.
62 // Easy solution: Restart at front of list.
63 // current time may have also changed
64 // drastically from a nested sleep
65 goto restart;
66 }
67 else
68 {
69 if (i->timestamp < nearest)
70 nearest = i->timestamp;
71 i = (PendingCallback*)i->next;
72 }
73 }
74 unsigned minleft = (unsigned)((nearest - now + 999)/ 1000);
75 return minleft;
76 }
77
_pump(unsigned milliseconds,bool fromSleep)78 void Timer::_pump(unsigned milliseconds, bool fromSleep)
79 {
80 do
81 {
82 unsigned amt = fire(fromSleep);
83 if (amt >= milliseconds) {
84 raw->sleep(milliseconds);
85 return;
86 }
87 raw->sleep(amt);
88 milliseconds-=amt;
89 } while(milliseconds);
90 }
91
92 //
93 // Queue a timer callback.
94 // Unless the dont-execute-in-sleep flag is set
95 //
queueCallback(Timer::TimerCallback * target,const void * context,unsigned milliseconds,bool executeInSleep)96 void Timer::queueCallback(Timer::TimerCallback * target, const void * context, unsigned milliseconds, bool executeInSleep)
97 {
98 NvU64 now = getTimeUs();
99 PendingCallback * callback = new PendingCallback();
100 if (callback == NULL)
101 {
102 DP_LOG(("DP> %s: Failed to allocate callback",
103 __FUNCTION__));
104 return;
105 }
106 callback->target = target;
107 callback->context = context;
108 callback->timestamp = now + milliseconds * 1000;
109 callback->executeInSleep = executeInSleep;
110 pending.insertBack(callback);
111 raw->queueCallback(this, milliseconds);
112 }
113
getTimeUs()114 NvU64 Timer::getTimeUs()
115 {
116 return raw->getTimeUs();
117 }
118
119 // Sleep a number of milliseconds.
120 // timer callbacks will be serviced!
sleep(unsigned milliseconds)121 void Timer::sleep(unsigned milliseconds)
122 {
123 _pump(milliseconds, true);
124 }
125
cancelCallbacks(Timer::TimerCallback * to)126 void Timer::cancelCallbacks(Timer::TimerCallback * to)
127 {
128 if (!to)
129 return;
130 for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
131 if (i->target == to)
132 i->target = 0;
133 }
134
cancelCallback(Timer::TimerCallback * to,const void * context)135 void Timer::cancelCallback(Timer::TimerCallback * to, const void * context)
136 {
137 if (!to)
138 return;
139 for (PendingCallback * i = (PendingCallback *)pending.begin(); i!=pending.end(); i = (PendingCallback*)i->next)
140 if (i->target == to && i->context == context)
141 i->target = 0;
142 }
143
144 // Queue callbacks in order.
queueCallbackInOrder(Timer::TimerCallback * target,const void * context,unsigned milliseconds,bool executeInSleep)145 void Timer::queueCallbackInOrder(Timer::TimerCallback * target, const void * context, unsigned milliseconds, bool executeInSleep)
146 {
147 NvU64 now = getTimeUs();
148 PendingCallback * callback = new PendingCallback();
149 callback->target = target;
150 callback->context = context;
151 callback->timestamp = now + milliseconds * 1000;
152 callback->executeInSleep = executeInSleep;
153
154 //Figure out where to insert the current callback
155 Timer::PendingCallback* i;
156
157 for (i = (PendingCallback*)pending.begin(); i != pending.end();)
158 {
159 // only for the given context.
160 if(i->context == context)
161 {
162 if(i->timestamp > callback->timestamp)
163 break;
164 }
165 i = (PendingCallback*) i->next;
166 }
167 if (i == pending.end())
168 {
169 pending.insertBack(callback);
170 }
171 else
172 {
173 pending.insertBefore(i, callback);
174 }
175
176 raw->queueCallback(this, milliseconds);
177 }
178
cancelAllCallbacks()179 void Timer::cancelAllCallbacks()
180 {
181 for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
182 i->target = 0;
183 }
184
cancelCallbacksWithoutContext(const void * context)185 void Timer::cancelCallbacksWithoutContext(const void * context)
186 {
187 for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
188 if(i->context != context)
189 i->target = 0;
190 }
191
checkCallbacksOfSameContext(const void * context)192 bool Timer::checkCallbacksOfSameContext(const void * context)
193 {
194 for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
195 if(i->context == context)
196 return true;
197
198 return false;
199 }
200