unsigned sendData(const UdpPermitToSendMsg &permit, bool isLocal, TokenBucket *bucket, bool &moreRequested, unsigned &maxPackets) { moreRequested = false; maxPackets = permit.max_data; PointerArray toSend; unsigned totalSent = cleanRetryData(permit, toSend); while (toSend.length() < maxPackets && dataQueued()) { DataBuffer *buffer = popQueuedData(); if (buffer) // Aborted slave queries leave NULL records on queue { UdpPacketHeader *header = (UdpPacketHeader*) buffer->data; toSend.append(buffer); totalSent += header->length; #ifdef __linux__ if (isLocal && (totalSent> 100000)) break; #endif } } maxPackets = toSend.length(); for (unsigned idx = 0; idx < maxPackets; idx++) { DataBuffer *buffer = (DataBuffer *) toSend.item(idx); UdpPacketHeader *header = (UdpPacketHeader*) buffer->data; bool isRetry = (header->udpSequence != 0); if (isRetry) { if (checkTraceLevel(TRACE_RETRY_DATA, 1)) DBGLOG("UdpSender: Resending packet to destination node %u sequence %u", permit.destNodeIndex, header->udpSequence); atomic_inc(&packetsRetried); } else header->udpSequence = nextUdpSequence(); unsigned length = header->length; if (bucket) { MTIME_SECTION(timer, "bucket_wait"); bucket->wait((length / 1024)+1); } try { if (udpSendCompletedInData) { if (idx == maxPackets-1) { // MORE - is this safe ? Any other thread looking at the data right now? Don't _think_ so... if (false && dataQueued()) // Causes some problems because no flow control info gets through at all { moreRequested = true; header->udpSequence |= (UDP_SEQUENCE_COMPLETE|UDP_SEQUENCE_MORE); } else header->udpSequence |= UDP_SEQUENCE_COMPLETE; } } #ifdef _SIMULATE_LOST_PACKETS if (isRetry || (header->udpSequence % 100) != 0) #endif data_socket->write(buffer->data, length); header->udpSequence &= ~UDP_SEQUENCE_BITS; } catch(IException *e) { StringBuffer s; DBGLOG("UdpSender: write exception - write(%p, %u) - %s", buffer->data, length, e->errorMessage(s).str()); e->Release(); } catch(...) { DBGLOG("UdpSender: write exception - unknown exception"); } if (!isRetry && maxRetryData) { unsigned slot = (retryDataIdx + retryDataCount) % maxRetryData; if (retryDataCount < maxRetryData) retryDataCount++; else { if (udpTraceLevel > 0) DBGLOG("Overflow in resend packet buffer for destination node %u - discarding packet sequence %u", permit.destNodeIndex, header->udpSequence); ::Release(retryData[slot]); } retryData[slot] = buffer; } else { ::Release(buffer); } } return totalSent; }
unsigned cleanRetryData(const UdpPermitToSendMsg &permit, PointerArray &retries) { // Any saved packets < lastReceived that are not listed as missing can be deleted SpinBlock b(lock); unsigned totalData = 0; if (checkTraceLevel(TRACE_RETRY_DATA, 3)) { unsigned minUdpSequence; if (retryDataCount) minUdpSequence = ((UdpPacketHeader *) retryData[retryDataIdx]->data)->udpSequence; else minUdpSequence = maxUdpSequence; StringBuffer permitStr; permit.toString(permitStr); DBGLOG("UdpSender: cleanRetryData (%s), total %u available between %u and %u", permitStr.str(), retryDataCount, minUdpSequence, maxUdpSequence); } unsigned lastReceived = permit.lastSequenceSeen; unsigned missingIndex = 0; unsigned missingCount = permit.missingCount; unsigned i = 0; if (maxRetryData) { while (i < retryDataCount && retries.length() < permit.max_data) { unsigned idx = (retryDataIdx + i) % maxRetryData; DataBuffer *buffer = retryData[idx]; if (buffer) { UdpPacketHeader *header = (UdpPacketHeader*) buffer->data; unsigned thisSequence = header->udpSequence; if (thisSequence > lastReceived) break; if (!missingCount || thisSequence < permit.missingSequences[missingIndex]) { ::Release(buffer); retryData[idx] = NULL; if (i) i++; // MORE - leaves holes - is this smart? Alternatively could close up... Should be rare anyway else { retryDataIdx = (retryDataIdx + 1) % maxRetryData; retryDataCount--; } } else if (thisSequence == permit.missingSequences[missingIndex]) { totalData += header->length; retries.append(buffer); i++; missingIndex++; missingCount--; } else { missingIndex++; missingCount--; } } else { if (i) i++; else { // Removing leading nulls retryDataCount--; retryDataIdx = (retryDataIdx + 1) % maxRetryData; } } } } if (checkTraceLevel(TRACE_RETRY_DATA, 3)) DBGLOG("UdpSender: cleanRetryData found %u to resend total size %u, total %u still available", retries.length(), totalData, retryDataCount); return totalData; }