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 deUint64 resetTime;
60 volatile deUint64 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 deUint64 curTime = deGetMicroseconds();
76 int totalSecondsPassed = (int)((curTime - dog->resetTime) / 1000000ull);
77 int secondsSinceLastTouch = (int)((curTime - dog->lastTouchTime) / 1000000ull);
78 deBool overIntervalLimit = secondsSinceLastTouch > dog->intervalTimeLimit;
79 deBool 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, int intervalTimeLimitSecs)
96 {
97 /* Allocate & initialize. */
98 qpWatchDog* dog = (qpWatchDog*)deCalloc(sizeof(qpWatchDog));
99 if (!dog)
100 return dog;
101
102 DE_ASSERT(timeOutFunc);
103 DE_ASSERT((totalTimeLimitSecs > 0) && (intervalTimeLimitSecs > 0));
104
105 DBGPRINT(("qpWatchDog::create(%ds, %ds)\n", totalTimeLimitSecs, intervalTimeLimitSecs));
106
107 dog->timeOutFunc = timeOutFunc;
108 dog->timeOutUserPtr = userPtr;
109 dog->totalTimeLimit = totalTimeLimitSecs;
110 dog->intervalTimeLimit = intervalTimeLimitSecs;
111 dog->defaultIntervalTimeLimit = intervalTimeLimitSecs;
112
113 /* Reset (sets time values). */
114 qpWatchDog_reset(dog);
115
116 /* Initialize watchdog thread. */
117 dog->status = STATUS_THREAD_RUNNING;
118 dog->watchDogThread = deThread_create(watchDogThreadFunc, dog, DE_NULL);
119 if (!dog->watchDogThread)
120 {
121 deFree(dog);
122 return DE_NULL;
123 }
124
125 return dog;
126 }
127
qpWatchDog_reset(qpWatchDog * dog)128 void qpWatchDog_reset (qpWatchDog* dog)
129 {
130 deUint64 curTime = deGetMicroseconds();
131
132 DE_ASSERT(dog);
133 DBGPRINT(("qpWatchDog::reset()\n"));
134
135 dog->resetTime = curTime;
136 dog->lastTouchTime = curTime;
137 }
138
qpWatchDog_destroy(qpWatchDog * dog)139 void qpWatchDog_destroy (qpWatchDog* dog)
140 {
141 DE_ASSERT(dog);
142 DBGPRINT(("qpWatchDog::destroy()\n"));
143
144 /* Finish the watchdog thread. */
145 dog->status = STATUS_STOP_THREAD;
146 deThread_join(dog->watchDogThread);
147 deThread_destroy(dog->watchDogThread);
148
149 DBGPRINT(("qpWatchDog::destroy() finished\n"));
150 deFree(dog);
151 }
152
qpWatchDog_touch(qpWatchDog * dog)153 void qpWatchDog_touch (qpWatchDog* dog)
154 {
155 DE_ASSERT(dog);
156 DBGPRINT(("qpWatchDog::touch()\n"));
157 dog->lastTouchTime = deGetMicroseconds();
158 }
159
160 /*
161 These function exists to allow the interval timer to be disabled for special cases
162 like very long shader compilations. Heavy code can be put between calls
163 to qpWatchDog_touchAndDisableIntervalTimeLimit and qpWatchDog_touchAndEnableIntervalTimeLimit
164 and during that period the interval time limit will become the same as the total
165 time limit. Afterwards, the interval timer is set back to its default.
166 */
qpWatchDog_touchAndDisableIntervalTimeLimit(qpWatchDog * dog)167 void qpWatchDog_touchAndDisableIntervalTimeLimit(qpWatchDog *dog)
168 {
169 dog->intervalTimeLimit = dog->totalTimeLimit;
170 qpWatchDog_touch(dog);
171 }
172
qpWatchDog_touchAndEnableIntervalTimeLimit(qpWatchDog * dog)173 void qpWatchDog_touchAndEnableIntervalTimeLimit(qpWatchDog *dog)
174 {
175 dog->intervalTimeLimit = dog->defaultIntervalTimeLimit;
176 qpWatchDog_touch(dog);
177 }
178