1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Helper Library
3 * -------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Watch dog for detecting timeouts
22 *//*--------------------------------------------------------------------*/
23
24 #include "qpWatchDog.h"
25
26 #include "deThread.h"
27 #include "deClock.h"
28 #include "deMemory.h"
29
30 #include <stdio.h>
31
32 #if 0
33 #define DBGPRINT(X) qpPrintf X
34 #else
35 #define DBGPRINT(X)
36 #endif
37
38 typedef enum Status_e
39 {
40 STATUS_THREAD_RUNNING = 0,
41 STATUS_STOP_THREAD,
42
43 STATUS_LAST
44 } Status;
45
46 struct qpWatchDog_s
47 {
48 qpWatchDogFunc timeOutFunc;
49 void *timeOutUserPtr;
50 int totalTimeLimit; /* Total test case time limit in seconds */
51 int intervalTimeLimit; /* Iteration length limit in seconds */
52 /*
53 Iteration time limit in seconds specified to the constructor. This is stored so that
54 intervalTimeLimit can be restored after qpWatchDog_touchAndDisableIntervalTimeLimit
55 is called.
56 */
57 int defaultIntervalTimeLimit;
58
59 volatile uint64_t resetTime;
60 volatile uint64_t lastTouchTime;
61
62 deThread watchDogThread;
63 volatile Status status;
64 };
65
watchDogThreadFunc(void * arg)66 static void watchDogThreadFunc(void *arg)
67 {
68 qpWatchDog *dog = (qpWatchDog *)arg;
69 DE_ASSERT(dog);
70
71 DBGPRINT(("watchDogThreadFunc(): start\n"));
72
73 while (dog->status == STATUS_THREAD_RUNNING)
74 {
75 uint64_t curTime = deGetMicroseconds();
76 int totalSecondsPassed = (int)((curTime - dog->resetTime) / 1000000ull);
77 int secondsSinceLastTouch = (int)((curTime - dog->lastTouchTime) / 1000000ull);
78 bool overIntervalLimit = secondsSinceLastTouch > dog->intervalTimeLimit;
79 bool overTotalLimit = totalSecondsPassed > dog->totalTimeLimit;
80
81 if (overIntervalLimit || overTotalLimit)
82 {
83 qpTimeoutReason reason = overTotalLimit ? QP_TIMEOUT_REASON_TOTAL_LIMIT : QP_TIMEOUT_REASON_INTERVAL_LIMIT;
84 DBGPRINT(("watchDogThreadFunc(): call timeout func\n"));
85 dog->timeOutFunc(dog, dog->timeOutUserPtr, reason);
86 break;
87 }
88
89 deSleep(100);
90 }
91
92 DBGPRINT(("watchDogThreadFunc(): stop\n"));
93 }
94
qpWatchDog_create(qpWatchDogFunc timeOutFunc,void * userPtr,int totalTimeLimitSecs,int intervalTimeLimitSecs)95 qpWatchDog *qpWatchDog_create(qpWatchDogFunc timeOutFunc, void *userPtr, int totalTimeLimitSecs,
96 int intervalTimeLimitSecs)
97 {
98 /* Allocate & initialize. */
99 qpWatchDog *dog = (qpWatchDog *)deCalloc(sizeof(qpWatchDog));
100 if (!dog)
101 return dog;
102
103 DE_ASSERT(timeOutFunc);
104 DE_ASSERT((totalTimeLimitSecs > 0) && (intervalTimeLimitSecs > 0));
105
106 DBGPRINT(("qpWatchDog::create(%ds, %ds)\n", totalTimeLimitSecs, intervalTimeLimitSecs));
107
108 dog->timeOutFunc = timeOutFunc;
109 dog->timeOutUserPtr = userPtr;
110 dog->totalTimeLimit = totalTimeLimitSecs;
111 dog->intervalTimeLimit = intervalTimeLimitSecs;
112 dog->defaultIntervalTimeLimit = intervalTimeLimitSecs;
113
114 /* Reset (sets time values). */
115 qpWatchDog_reset(dog);
116
117 /* Initialize watchdog thread. */
118 dog->status = STATUS_THREAD_RUNNING;
119 dog->watchDogThread = deThread_create(watchDogThreadFunc, dog, DE_NULL);
120 if (!dog->watchDogThread)
121 {
122 deFree(dog);
123 return DE_NULL;
124 }
125
126 return dog;
127 }
128
qpWatchDog_reset(qpWatchDog * dog)129 void qpWatchDog_reset(qpWatchDog *dog)
130 {
131 uint64_t curTime = deGetMicroseconds();
132
133 DE_ASSERT(dog);
134 DBGPRINT(("qpWatchDog::reset()\n"));
135
136 dog->resetTime = curTime;
137 dog->lastTouchTime = curTime;
138 }
139
qpWatchDog_destroy(qpWatchDog * dog)140 void qpWatchDog_destroy(qpWatchDog *dog)
141 {
142 DE_ASSERT(dog);
143 DBGPRINT(("qpWatchDog::destroy()\n"));
144
145 /* Finish the watchdog thread. */
146 dog->status = STATUS_STOP_THREAD;
147 deThread_join(dog->watchDogThread);
148 deThread_destroy(dog->watchDogThread);
149
150 DBGPRINT(("qpWatchDog::destroy() finished\n"));
151 deFree(dog);
152 }
153
qpWatchDog_touch(qpWatchDog * dog)154 void qpWatchDog_touch(qpWatchDog *dog)
155 {
156 DE_ASSERT(dog);
157 DBGPRINT(("qpWatchDog::touch()\n"));
158 dog->lastTouchTime = deGetMicroseconds();
159 }
160
161 /*
162 These function exists to allow the interval timer to be disabled for special cases
163 like very long shader compilations. Heavy code can be put between calls
164 to qpWatchDog_touchAndDisableIntervalTimeLimit and qpWatchDog_touchAndEnableIntervalTimeLimit
165 and during that period the interval time limit will become the same as the total
166 time limit. Afterwards, the interval timer is set back to its default.
167 */
qpWatchDog_touchAndDisableIntervalTimeLimit(qpWatchDog * dog)168 void qpWatchDog_touchAndDisableIntervalTimeLimit(qpWatchDog *dog)
169 {
170 dog->intervalTimeLimit = dog->totalTimeLimit;
171 qpWatchDog_touch(dog);
172 }
173
qpWatchDog_touchAndEnableIntervalTimeLimit(qpWatchDog * dog)174 void qpWatchDog_touchAndEnableIntervalTimeLimit(qpWatchDog *dog)
175 {
176 dog->intervalTimeLimit = dog->defaultIntervalTimeLimit;
177 qpWatchDog_touch(dog);
178 }
179