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