blob: f672e92a6807403c43274ad3705cc16a2dd2aee6 [file] [log] [blame]
Jon Medhurstaaf37a32013-06-11 12:10:56 +01001/**
Jon Medhurst15ce78d2014-04-10 09:02:02 +01002 * Copyright (C) ARM Limited 2010-2014. All rights reserved.
Jon Medhurstaaf37a32013-06-11 12:10:56 +01003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include "Fifo.h"
10
11#include <stdlib.h>
12#ifdef WIN32
13#define valloc malloc
14#endif
15
16#include "Logging.h"
17
18// bufferSize is the amount of data to be filled
19// singleBufferSize is the maximum size that may be filled during a single write
20// (bufferSize + singleBufferSize) will be allocated
21Fifo::Fifo(int singleBufferSize, int bufferSize, sem_t* readerSem) {
22 mWrite = mRead = mReadCommit = mRaggedEnd = 0;
23 mWrapThreshold = bufferSize;
24 mSingleBufferSize = singleBufferSize;
25 mReaderSem = readerSem;
26 mBuffer = (char*)valloc(bufferSize + singleBufferSize);
27 mEnd = false;
28
29 if (mBuffer == NULL) {
30 logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize);
31 handleException();
32 }
33
34 if (sem_init(&mWaitForSpaceSem, 0, 0)) {
35 logg->logError(__FILE__, __LINE__, "sem_init() failed");
36 handleException();
37 }
38}
39
40Fifo::~Fifo() {
41 free(mBuffer);
42 sem_destroy(&mWaitForSpaceSem);
43}
44
45int Fifo::numBytesFilled() const {
46 return mWrite - mRead + mRaggedEnd;
47}
48
49char* Fifo::start() const {
50 return mBuffer;
51}
52
53bool Fifo::isEmpty() const {
54 return mRead == mWrite && mRaggedEnd == 0;
55}
56
57bool Fifo::isFull() const {
58 return willFill(0);
59}
60
61// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer
62// 'full' means there is less than singleBufferSize bytes available contiguously; it does not mean there are zero bytes available
63bool Fifo::willFill(int additional) const {
64 if (mWrite > mRead) {
65 if (numBytesFilled() + additional < mWrapThreshold) {
66 return false;
67 }
68 } else {
69 if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) {
70 return false;
71 }
72 }
73 return true;
74}
75
76// This function will stall until contiguous singleBufferSize bytes are available
77char* Fifo::write(int length) {
78 if (length <= 0) {
79 length = 0;
80 mEnd = true;
81 }
82
83 // update the write pointer
84 mWrite += length;
85
86 // handle the wrap-around
87 if (mWrite >= mWrapThreshold) {
88 mRaggedEnd = mWrite;
89 mWrite = 0;
90 }
91
92 // send a notification that data is ready
93 sem_post(mReaderSem);
94
95 // wait for space
96 while (isFull()) {
97 sem_wait(&mWaitForSpaceSem);
98 }
99
100 return &mBuffer[mWrite];
101}
102
103void Fifo::release() {
104 // update the read pointer now that the data has been handled
105 mRead = mReadCommit;
106
107 // handle the wrap-around
108 if (mRead >= mWrapThreshold) {
109 mRaggedEnd = mRead = mReadCommit = 0;
110 }
111
112 // send a notification that data is free (space is available)
113 sem_post(&mWaitForSpaceSem);
114}
115
116// This function will return null if no data is available
117char* Fifo::read(int *const length) {
118 // wait for data
119 if (isEmpty() && !mEnd) {
120 return NULL;
121 }
122
123 // obtain the length
124 do {
125 mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite;
126 *length = mReadCommit - mRead;
127 } while (*length < 0); // plugs race condition without using semaphores
128
129 return &mBuffer[mRead];
130}