bool LLBufferArray::copyIntoBuffers( S32 channel, const U8* src, S32 len, std::vector<LLSegment>& segments) { LLMemType m1(LLMemType::MTYPE_IO_BUFFER); if(!src || !len) return false; S32 copied = 0; LLSegment segment; buffer_iterator_t it = mBuffers.begin(); buffer_iterator_t end = mBuffers.end(); for(; it != end;) { if(!(*it)->createSegment(channel, len, segment)) { ++it; continue; } segments.push_back(segment); S32 bytes = llmin(segment.size(), len); memcpy(segment.data(), src + copied, bytes); /* Flawfinder: Ignore */ copied += bytes; len -= bytes; if(0 == len) { break; } } while(len) { LLBuffer* buf = new LLHeapBuffer; mBuffers.push_back(buf); if(!buf->createSegment(channel, len, segment)) { // this totally failed - bail. This is the weird corner // case were we 'leak' memory. No worries about an actual // leak - we will still reclaim the memory later, but this // particular buffer array is hosed for some reason. // This should never happen. return false; } segments.push_back(segment); memcpy(segment.data(), src + copied, segment.size()); /*Flawfinder: ignore*/ copied += segment.size(); len -= segment.size(); } return true; }
// virtual bool LLHeapBuffer::containsSegment(const LLSegment& segment) const { // *NOTE: this check is fairly simple because heap buffers are // simple contiguous chunks of heap memory. if((mBuffer > segment.data()) || ((mBuffer + mSize) < (segment.data() + segment.size()))) { return false; } return true; }
// virtual bool LLHeapBuffer::reclaimSegment(const LLSegment& segment) { if(containsSegment(segment)) { mReclaimedBytes += segment.size(); if(mReclaimedBytes == mSize) { // We have reclaimed all of the memory from this // buffer. Therefore, we can reset the mNextFree to the // start of the buffer, and reset the reclaimed bytes. mReclaimedBytes = 0; mNextFree = mBuffer; } else if(mReclaimedBytes > mSize) { LL_WARNS() << "LLHeapBuffer reclaimed more memory than allocated." << " This is probably programmer error." << LL_ENDL; } return true; } return false; }
// virtual LLIOPipe::EStatus LLIOSocketWriter::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { LL_RECORD_BLOCK_TIME(FTM_PROCESS_SOCKET_WRITER); PUMP_DEBUG; if(!mDestination) return STATUS_PRECONDITION_NOT_MET; if(!mInitialized) { PUMP_DEBUG; // Since the write will not block, it's ok to initialize and // attempt to write immediately. mInitialized = true; if(pump) { PUMP_DEBUG; LL_DEBUGS() << "Initializing poll descriptor for LLIOSocketWriter." << LL_ENDL; apr_pollfd_t poll_fd; poll_fd.p = NULL; poll_fd.desc_type = APR_POLL_SOCKET; poll_fd.reqevents = APR_POLLOUT; poll_fd.rtnevents = 0x0; poll_fd.desc.s = mDestination->getSocket(); poll_fd.client_data = NULL; pump->setConditional(this, &poll_fd); } } PUMP_DEBUG; // *FIX: Some sort of writev implementation would be much more // efficient - not only because writev() is better, but also // because we won't have to do as much work to find the start // address. buffer->lock(); LLBufferArray::segment_iterator_t it; LLBufferArray::segment_iterator_t end = buffer->endSegment(); LLSegment segment; it = buffer->constructSegmentAfter(mLastWritten, segment); /* if(NULL == mLastWritten) { it = buffer->beginSegment(); segment = (*it); } else { it = buffer->getSegment(mLastWritten); segment = (*it); S32 size = segment.size(); U8* data = segment.data(); if((data + size) == mLastWritten) { ++it; segment = (*it); } else { // *FIX: check the math on this one segment = LLSegment( (*it).getChannelMask(), mLastWritten + 1, size - (mLastWritten - data)); } } */ PUMP_DEBUG; apr_size_t len; bool done = false; apr_status_t status = APR_SUCCESS; while(it != end) { PUMP_DEBUG; if((*it).isOnChannel(channels.in())) { PUMP_DEBUG; len = (apr_size_t)segment.size(); status = apr_socket_send( mDestination->getSocket(), (const char*)segment.data(), &len); // We sometimes get a 'non-blocking socket operation could not be // completed immediately' error from apr_socket_send. In this // case we break and the data will be sent the next time the chain // is pumped. if(APR_STATUS_IS_EAGAIN(status)) { ll_apr_warn_status(status); break; } mLastWritten = segment.data() + len - 1; PUMP_DEBUG; if((S32)len < segment.size()) { break; } } ++it; if(it != end) { segment = (*it); } else { done = true; } } buffer->unlock(); PUMP_DEBUG; if(done && eos) { return STATUS_DONE; } return STATUS_OK; }
// virtual int LLBufferStreamBuf::underflow() { LLMemType m1(LLMemType::MTYPE_IO_BUFFER); //lldebugs << "LLBufferStreamBuf::underflow()" << llendl; if(!mBuffer) { return EOF; } LLMutexLock lock(mBuffer->getMutex()); LLBufferArray::segment_iterator_t iter; LLBufferArray::segment_iterator_t end = mBuffer->endSegment(); U8* last_pos = (U8*)gptr(); LLSegment segment; if(last_pos) { // Back up into a piece of memory we know that we have // allocated so that calls for the next segment based on // 'after' will succeed. --last_pos; iter = mBuffer->splitAfter(last_pos); if(iter != end) { // We need to clear the read segment just in case we have // an early exit in the function and never collect the // next segment. Calling eraseSegment() with the same // segment twice is just like double deleting -- nothing // good comes from it. mBuffer->eraseSegment(iter++); if(iter != end) segment = (*iter); } else { // This should never really happen, but somehow, the // istream is telling the buf that it just finished // reading memory that is not in the buf. I think this // would only happen if there were a bug in the c++ stream // class. Just bail. // *TODO: can we set the fail bit on the stream somehow? return EOF; } } else { // Get iterator to full segment containing last_pos // and construct sub-segment starting at last_pos. // Note: segment may != *it at this point iter = mBuffer->constructSegmentAfter(last_pos, segment); } if(iter == end) { return EOF; } // Iterate through segments to find a non-empty segment on input channel. while((!segment.isOnChannel(mChannels.in()) || (segment.size() == 0))) { ++iter; if(iter == end) { return EOF; } segment = *(iter); } // set up the stream to read from the next segment. char* start = (char*)segment.data(); setg(start, start, start + segment.size()); return *gptr(); }
void buffer_object_t::test<4>() { S32 channel = 50; S32 bigSize = 16384*2; char str[] = "SecondLife"; S32 smallSize = sizeof(str); LLSegment segment; LLHeapBuffer buf; // use default size of DEFAULT_HEAP_BUFFER_SIZE = 16384 S32 requestSize; requestSize = 16384-1; ensure("1. LLHeapBuffer createSegment failed", (TRUE == buf.createSegment(channel, requestSize, segment)) && segment.size() == requestSize); // second request for remainign 1 byte requestSize = 1; ensure("2. LLHeapBuffer createSegment failed", (TRUE == buf.createSegment(channel, requestSize, segment)) && segment.size() == requestSize); // it should fail now. requestSize = 1; ensure("3. LLHeapBuffer createSegment failed", (FALSE == buf.createSegment(channel, requestSize, segment))); LLHeapBuffer buf1(bigSize); // requst for more than default size but less than total sizeit should fail now. requestSize = 16384 + 1; ensure("4. LLHeapBuffer createSegment failed", (TRUE == buf1.createSegment(channel, requestSize, segment)) && segment.size() == requestSize); LLHeapBuffer buf2((U8*) str, smallSize); requestSize = smallSize; ensure("5. LLHeapBuffer createSegment failed", (TRUE == buf2.createSegment(channel, requestSize, segment)) && segment.size() == requestSize && memcmp(segment.data(), (U8*) str, requestSize) == 0); requestSize = smallSize+1; ensure("6. LLHeapBuffer createSegment failed", (FALSE == buf2.createSegment(channel, requestSize, segment))); }
void buffer_object_t::test<2>() { LLSegment segment; ensure("LLSegment get functions failed", (0 == segment.getChannel() && NULL == segment.data() && 0 == segment.size())); segment.setChannel(50); ensure_equals("LLSegment setChannel() function failed", segment.getChannel(), 50); ensure("LLSegment isOnChannel() function failed", (TRUE == segment.isOnChannel(50))); }