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