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