• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2009 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  #include "rsLocklessFifo.h"
18  
19  using namespace android;
20  
21  
LocklessCommandFifo()22  LocklessCommandFifo::LocklessCommandFifo()
23  {
24  }
25  
~LocklessCommandFifo()26  LocklessCommandFifo::~LocklessCommandFifo()
27  {
28      if (!mInShutdown) {
29          shutdown();
30      }
31      free(mBuffer);
32  }
33  
shutdown()34  void LocklessCommandFifo::shutdown()
35  {
36      mInShutdown = true;
37      mSignalToWorker.set();
38  }
39  
init(uint32_t sizeInBytes)40  bool LocklessCommandFifo::init(uint32_t sizeInBytes)
41  {
42      // Add room for a buffer reset command
43      mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4));
44      if (!mBuffer) {
45          LOGE("LocklessFifo allocation failure");
46          return false;
47      }
48  
49      if (!mSignalToControl.init() || !mSignalToWorker.init()) {
50          LOGE("Signal setup failed");
51          free(mBuffer);
52          return false;
53      }
54  
55      mInShutdown = false;
56      mSize = sizeInBytes;
57      mPut = mBuffer;
58      mGet = mBuffer;
59      mEnd = mBuffer + (sizeInBytes) - 1;
60      //dumpState("init");
61      return true;
62  }
63  
getFreeSpace() const64  uint32_t LocklessCommandFifo::getFreeSpace() const
65  {
66      int32_t freeSpace = 0;
67      //dumpState("getFreeSpace");
68  
69      if (mPut >= mGet) {
70          freeSpace = mEnd - mPut;
71      } else {
72          freeSpace = mGet - mPut;
73      }
74  
75      if (freeSpace < 0) {
76          freeSpace = 0;
77      }
78      return freeSpace;
79  }
80  
isEmpty() const81  bool LocklessCommandFifo::isEmpty() const
82  {
83      return mPut == mGet;
84  }
85  
86  
reserve(uint32_t sizeInBytes)87  void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
88  {
89      // Add space for command header and loop token;
90      sizeInBytes += 8;
91  
92      //dumpState("reserve");
93      if (getFreeSpace() < sizeInBytes) {
94          makeSpace(sizeInBytes);
95      }
96  
97      return mPut + 4;
98  }
99  
commit(uint32_t command,uint32_t sizeInBytes)100  void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes)
101  {
102      if (mInShutdown) {
103          return;
104      }
105      //dumpState("commit 1");
106      reinterpret_cast<uint16_t *>(mPut)[0] = command;
107      reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
108      mPut += ((sizeInBytes + 3) & ~3) + 4;
109      //dumpState("commit 2");
110      mSignalToWorker.set();
111  }
112  
commitSync(uint32_t command,uint32_t sizeInBytes)113  void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes)
114  {
115      if (mInShutdown) {
116          return;
117      }
118      commit(command, sizeInBytes);
119      flush();
120  }
121  
flush()122  void LocklessCommandFifo::flush()
123  {
124      //dumpState("flush 1");
125      while(mPut != mGet) {
126          mSignalToControl.wait();
127      }
128      //dumpState("flush 2");
129  }
130  
get(uint32_t * command,uint32_t * bytesData)131  const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData)
132  {
133      while(1) {
134          //dumpState("get");
135          while(isEmpty() && !mInShutdown) {
136              mSignalToControl.set();
137              mSignalToWorker.wait();
138          }
139  
140          if (mInShutdown) {
141              *command = 0;
142              *bytesData = 0;
143              return 0;
144          }
145  
146          *command = reinterpret_cast<const uint16_t *>(mGet)[0];
147          *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
148          if (*command) {
149              // non-zero command is valid
150              return mGet+4;
151          }
152  
153          // zero command means reset to beginning.
154          mGet = mBuffer;
155      }
156  }
157  
next()158  void LocklessCommandFifo::next()
159  {
160      uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
161      mGet += ((bytes + 3) & ~3) + 4;
162      if (isEmpty()) {
163          mSignalToControl.set();
164      }
165      //dumpState("next");
166  }
167  
makeSpace(uint32_t bytes)168  void LocklessCommandFifo::makeSpace(uint32_t bytes)
169  {
170      //dumpState("make space");
171      if ((mPut+bytes) > mEnd) {
172          // Need to loop regardless of where get is.
173          while((mGet > mPut) && (mBuffer+4 >= mGet)) {
174              usleep(100);
175          }
176  
177          // Toss in a reset then the normal wait for space will do the rest.
178          reinterpret_cast<uint16_t *>(mPut)[0] = 0;
179          reinterpret_cast<uint16_t *>(mPut)[1] = 0;
180          mPut = mBuffer;
181      }
182  
183      // it will fit here so we just need to wait for space.
184      while(getFreeSpace() < bytes) {
185          usleep(100);
186      }
187  
188  }
189  
dumpState(const char * s) const190  void LocklessCommandFifo::dumpState(const char *s) const
191  {
192      LOGV("%s  put %p, get %p,  buf %p,  end %p", s, mPut, mGet, mBuffer, mEnd);
193  }
194  
Signal()195  LocklessCommandFifo::Signal::Signal()
196  {
197      mSet = true;
198  }
199  
~Signal()200  LocklessCommandFifo::Signal::~Signal()
201  {
202      pthread_mutex_destroy(&mMutex);
203      pthread_cond_destroy(&mCondition);
204  }
205  
init()206  bool LocklessCommandFifo::Signal::init()
207  {
208      int status = pthread_mutex_init(&mMutex, NULL);
209      if (status) {
210          LOGE("LocklessFifo mutex init failure");
211          return false;
212      }
213  
214      status = pthread_cond_init(&mCondition, NULL);
215      if (status) {
216          LOGE("LocklessFifo condition init failure");
217          pthread_mutex_destroy(&mMutex);
218          return false;
219      }
220  
221      return true;
222  }
223  
set()224  void LocklessCommandFifo::Signal::set()
225  {
226      int status;
227  
228      status = pthread_mutex_lock(&mMutex);
229      if (status) {
230          LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
231          return;
232      }
233  
234      mSet = true;
235  
236      status = pthread_cond_signal(&mCondition);
237      if (status) {
238          LOGE("LocklessCommandFifo: error %i on set condition.", status);
239      }
240  
241      status = pthread_mutex_unlock(&mMutex);
242      if (status) {
243          LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
244      }
245  }
246  
wait()247  void LocklessCommandFifo::Signal::wait()
248  {
249      int status;
250  
251      status = pthread_mutex_lock(&mMutex);
252      if (status) {
253          LOGE("LocklessCommandFifo: error %i locking for condition.", status);
254          return;
255      }
256  
257      if (!mSet) {
258          status = pthread_cond_wait(&mCondition, &mMutex);
259          if (status) {
260              LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
261          }
262      }
263      mSet = false;
264  
265      status = pthread_mutex_unlock(&mMutex);
266      if (status) {
267          LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
268      }
269  }
270  
271