1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
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 Thread test utilities
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuThreadUtil.hpp"
25
26#include "deClock.h"
27#include "deMemory.h"
28
29using std::vector;
30using de::SharedPtr;
31
32namespace tcu
33{
34namespace ThreadUtil
35{
36
37Event::Event (void)
38	: m_result		(RESULT_NOT_READY)
39	, m_waiterCount	(0)
40	, m_waiters		(0, 0)
41{
42}
43
44Event::~Event (void)
45{
46}
47
48void Event::setResult (Result result)
49{
50	m_lock.lock();
51	DE_ASSERT(m_result == RESULT_NOT_READY);
52	m_result = result;
53	m_lock.unlock();
54
55	for (int i = 0; i < m_waiterCount; i++)
56		m_waiters.increment();
57}
58
59Event::Result Event::waitReady (void)
60{
61	m_lock.lock();
62
63	if (m_result == RESULT_NOT_READY)
64		m_waiterCount++;
65	else
66	{
67		m_lock.unlock();
68		return m_result;
69	}
70
71	m_lock.unlock();
72
73	m_waiters.decrement();
74
75	return m_result;
76}
77
78Object::Object (const char* type, SharedPtr<Event> e)
79	: m_type	(type)
80	, m_modify	(e)
81{
82}
83
84Object::~Object	(void)
85{
86}
87
88void Object::read (SharedPtr<Event> event, std::vector<SharedPtr<Event> >& deps)
89{
90	// Make call depend on last modifying call
91	deps.push_back(m_modify);
92
93	// Add read dependency
94	m_reads.push_back(event);
95}
96
97void Object::modify (SharedPtr<Event> event, std::vector<SharedPtr<Event> >& deps)
98{
99	// Make call depend on all reads
100	for (int readNdx = 0; readNdx < (int)m_reads.size(); readNdx++)
101	{
102		deps.push_back(m_reads[readNdx]);
103	}
104	deps.push_back(m_modify);
105
106	// Update last modifying call
107	m_modify = event;
108
109	// Clear read dependencies of last "version" of this object
110	m_reads.clear();
111}
112
113Operation::Operation (const char* name)
114	: m_name	(name)
115	, m_event	(new Event)
116{
117}
118
119Operation::~Operation (void)
120{
121}
122
123void Operation::execute (Thread& thread)
124{
125	bool success = true;
126
127	// Wait for dependencies and check that they succeeded
128	for (int depNdx = 0; depNdx < (int)m_deps.size(); depNdx++)
129	{
130		if (m_deps[depNdx]->waitReady() != Event::RESULT_OK)
131			success = false;
132	}
133
134	// Try execute operation
135	if (success)
136	{
137		try
138		{
139			exec(thread);
140		}
141		catch (...)
142		{
143			// Got exception event failed
144			m_event->setResult(Event::RESULT_FAILED);
145			throw;
146		}
147
148		m_event->setResult(Event::RESULT_OK);
149	}
150	else
151		// Some dependencies failed
152		m_event->setResult(Event::RESULT_FAILED);
153
154	// Release resources
155	m_deps.clear();
156	m_event = SharedPtr<Event>();
157}
158
159const MessageBuilder::EndToken Message::End = MessageBuilder::EndToken();
160
161void MessageBuilder::operator<< (const EndToken&)
162{
163	m_thread.pushMessage(m_stream.str());
164}
165
166Thread::Thread (int seed)
167	: m_random	(seed)
168	, m_status	(THREADSTATUS_NOT_STARTED)
169{
170}
171
172Thread::~Thread (void)
173{
174	for (int operationNdx = 0; operationNdx < (int)m_operations.size(); operationNdx++)
175		delete m_operations[operationNdx];
176
177	m_operations.clear();
178}
179
180deUint8* Thread::getDummyData (size_t size)
181{
182	if (m_dummyData.size() < size)
183	{
184		m_dummyData.resize(size);
185	}
186
187	return &(m_dummyData[0]);
188}
189
190void Thread::addOperation (Operation* operation)
191{
192	m_operations.push_back(operation);
193}
194
195void Thread::run (void)
196{
197	m_status = THREADSTATUS_RUNNING;
198	bool initOk = false;
199
200	// Reserve at least two messages for each operation
201	m_messages.reserve(m_operations.size()*2);
202	try
203	{
204		init();
205		initOk = true;
206		for (int operationNdx = 0; operationNdx < (int)m_operations.size(); operationNdx++)
207			m_operations[operationNdx]->execute(*this);
208
209		deinit();
210		m_status =  THREADSTATUS_READY;
211	}
212	catch (const tcu::NotSupportedError& e)
213	{
214		newMessage() << "tcu::NotSupportedError '" << e.what() << "'" << Message::End;
215		deinit();
216		m_status = (initOk ? THREADSTATUS_NOT_SUPPORTED : THREADSTATUS_INIT_FAILED);
217	}
218	catch (const tcu::Exception& e)
219	{
220		newMessage() << "tcu::Exception '" << e.what() << "'" << Message::End;
221		deinit();
222		m_status = (initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED);
223	}
224	catch (const std::exception& error)
225	{
226		newMessage() << "std::exception '" << error.what() << "'" << Message::End;
227		deinit();
228		m_status = (initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED);
229	}
230	catch (...)
231	{
232		newMessage() << "Unkown exception" << Message::End;
233		deinit();
234		m_status = (initOk ? THREADSTATUS_FAILED : THREADSTATUS_INIT_FAILED);
235	}
236}
237
238void Thread::exec (void)
239{
240	start();
241}
242
243void Thread::pushMessage (const std::string& str)
244{
245	de::ScopedLock lock(m_messageLock);
246	m_messages.push_back(Message(deGetMicroseconds(), str.c_str()));
247}
248
249int Thread::getMessageCount	 (void) const
250{
251	de::ScopedLock lock(m_messageLock);
252	return (int)(m_messages.size());
253}
254
255Message Thread::getMessage (int index) const
256{
257	de::ScopedLock lock(m_messageLock);
258	return m_messages[index];
259}
260
261
262DataBlock::DataBlock (SharedPtr<Event> event)
263	: Object("DataBlock", event)
264{
265}
266
267void DataBlock::setData (size_t size, const void* data)
268{
269	m_data = std::vector<deUint8>(size);
270	deMemcpy(&(m_data[0]), data, (int)size);
271}
272
273CompareData::CompareData (SharedPtr<DataBlock> a, SharedPtr<DataBlock> b)
274	: Operation	("CompareData")
275	, m_a		(a)
276	, m_b		(b)
277{
278	readObject(SharedPtr<Object>(a));
279	readObject(SharedPtr<Object>(b));
280}
281
282void CompareData::exec (Thread& thread)
283{
284	bool result = true;
285	DE_ASSERT(m_a->getSize() == m_b->getSize());
286
287	thread.newMessage() << "Begin -- CompareData" << Message::End;
288
289	for (int byteNdx = 0; byteNdx < (int)m_a->getSize(); byteNdx++)
290	{
291		if (m_a->getData()[byteNdx] != m_b->getData()[byteNdx])
292		{
293			result = false;
294			thread.newMessage() << "CompareData failed at offset :" << byteNdx << Message::End;
295			break;
296		}
297	}
298
299	if (result)
300		thread.newMessage() << "CompareData passed" << Message::End;
301	else
302		TCU_FAIL("Data comparision failed");
303
304	thread.newMessage() << "End -- CompareData" << Message::End;
305}
306
307} // ThreadUtil
308} // tcu
309