1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Test Executor
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 Cross-thread function call dispatcher.
22 *//*--------------------------------------------------------------------*/
23
24 #include "xeCallQueue.hpp"
25 #include "deInt32.h"
26 #include "deMemory.h"
27
28 using std::vector;
29
getNextQueueSize(int curSize,int minNewSize)30 static inline int getNextQueueSize(int curSize, int minNewSize)
31 {
32 return de::max(curSize * 2, 1 << deLog2Ceil32(minNewSize));
33 }
34
35 namespace xe
36 {
37
38 // CallQueue
39
CallQueue(void)40 CallQueue::CallQueue(void) : m_canceled(false), m_callSem(0), m_callQueue(64)
41 {
42 }
43
~CallQueue(void)44 CallQueue::~CallQueue(void)
45 {
46 // Destroy all calls.
47 for (vector<Call *>::iterator i = m_calls.begin(); i != m_calls.end(); i++)
48 delete *i;
49 }
50
cancel(void)51 void CallQueue::cancel(void)
52 {
53 m_canceled = true;
54 m_callSem.increment();
55 }
56
callNext(void)57 void CallQueue::callNext(void)
58 {
59 Call *call = DE_NULL;
60
61 // Wait for a call.
62 m_callSem.decrement();
63
64 if (m_canceled)
65 return;
66
67 // Acquire call from buffer.
68 {
69 de::ScopedLock lock(m_lock);
70 call = m_callQueue.popBack();
71 }
72
73 try
74 {
75 // \note Enqueue lock is not held during call so it is possible to enqueue more work from dispatched call.
76 CallReader reader(call);
77
78 call->getFunction()(reader);
79
80 // check callee consumed all
81 DE_ASSERT(reader.isDataConsumed());
82 call->clear();
83 }
84 catch (const std::exception &)
85 {
86 try
87 {
88 // Try to push call into free calls list.
89 de::ScopedLock lock(m_lock);
90 m_freeCalls.push_back(call);
91 }
92 catch (const std::exception &)
93 {
94 // We can't do anything but ignore this.
95 }
96
97 throw;
98 }
99
100 // Push back to free calls list.
101 {
102 de::ScopedLock lock(m_lock);
103 m_freeCalls.push_back(call);
104 }
105 }
106
getEmptyCall(void)107 Call *CallQueue::getEmptyCall(void)
108 {
109 de::ScopedLock lock(m_lock);
110 Call *call = DE_NULL;
111
112 // Try to get from free calls list.
113 if (!m_freeCalls.empty())
114 {
115 call = m_freeCalls.back();
116 m_freeCalls.pop_back();
117 }
118
119 // If no free calls were available, create a new.
120 if (!call)
121 {
122 m_calls.reserve(m_calls.size() + 1);
123 call = new Call();
124 m_calls.push_back(call);
125 }
126
127 return call;
128 }
129
enqueue(Call * call)130 void CallQueue::enqueue(Call *call)
131 {
132 de::ScopedLock lock(m_lock);
133
134 if (m_callQueue.getNumFree() == 0)
135 {
136 // Call queue must be grown.
137 m_callQueue.resize(getNextQueueSize(m_callQueue.getSize(), m_callQueue.getSize() + 1));
138 }
139
140 m_callQueue.pushFront(call);
141 m_callSem.increment();
142 }
143
freeCall(Call * call)144 void CallQueue::freeCall(Call *call)
145 {
146 de::ScopedLock lock(m_lock);
147 m_freeCalls.push_back(call);
148 }
149
150 // Call
151
Call(void)152 Call::Call(void) : m_func(DE_NULL)
153 {
154 }
155
~Call(void)156 Call::~Call(void)
157 {
158 }
159
clear(void)160 void Call::clear(void)
161 {
162 m_func = DE_NULL;
163 m_data.clear();
164 }
165
166 // CallReader
167
CallReader(Call * call)168 CallReader::CallReader(Call *call) : m_call(call), m_curPos(0)
169 {
170 }
171
read(uint8_t * bytes,size_t numBytes)172 void CallReader::read(uint8_t *bytes, size_t numBytes)
173 {
174 DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
175 deMemcpy(bytes, m_call->getData() + m_curPos, numBytes);
176 m_curPos += numBytes;
177 }
178
getDataBlock(size_t numBytes)179 const uint8_t *CallReader::getDataBlock(size_t numBytes)
180 {
181 DE_ASSERT(m_curPos + numBytes <= m_call->getDataSize());
182
183 const uint8_t *ptr = m_call->getData() + m_curPos;
184 m_curPos += numBytes;
185
186 return ptr;
187 }
188
isDataConsumed(void) const189 bool CallReader::isDataConsumed(void) const
190 {
191 return m_curPos == m_call->getDataSize();
192 }
193
operator >>(CallReader & reader,std::string & value)194 CallReader &operator>>(CallReader &reader, std::string &value)
195 {
196 value.clear();
197 for (;;)
198 {
199 char c;
200 reader.read((uint8_t *)&c, sizeof(char));
201 if (c != 0)
202 value.push_back(c);
203 else
204 break;
205 }
206
207 return reader;
208 }
209
210 // CallWriter
211
CallWriter(CallQueue * queue,Call::Function function)212 CallWriter::CallWriter(CallQueue *queue, Call::Function function)
213 : m_queue(queue)
214 , m_call(queue->getEmptyCall())
215 , m_enqueued(false)
216 {
217 m_call->setFunction(function);
218 }
219
~CallWriter(void)220 CallWriter::~CallWriter(void)
221 {
222 if (!m_enqueued)
223 m_queue->freeCall(m_call);
224 }
225
write(const uint8_t * bytes,size_t numBytes)226 void CallWriter::write(const uint8_t *bytes, size_t numBytes)
227 {
228 DE_ASSERT(!m_enqueued);
229 size_t curPos = m_call->getDataSize();
230 m_call->setDataSize(curPos + numBytes);
231 deMemcpy(m_call->getData() + curPos, bytes, numBytes);
232 }
233
enqueue(void)234 void CallWriter::enqueue(void)
235 {
236 DE_ASSERT(!m_enqueued);
237 m_queue->enqueue(m_call);
238 m_enqueued = true;
239 }
240
operator <<(CallWriter & writer,const char * str)241 CallWriter &operator<<(CallWriter &writer, const char *str)
242 {
243 int pos = 0;
244 for (;;)
245 {
246 writer.write((const uint8_t *)str + pos, sizeof(char));
247 if (str[pos] == 0)
248 break;
249 pos += 1;
250 }
251
252 return writer;
253 }
254
255 } // namespace xe
256