/** * The thread method that handles calling send for the individual sockets. * * Control packets will always be tried to be sent first. If there is any bandwidth leftover * after that, send() for the upload slot sockets will be called in priority order until we have run * out of available bandwidth for this loop. Upload slots will not be allowed to go without having sent * called for more than a defined amount of time (i.e. two seconds). * * @return always returns 0. */ void* UploadBandwidthThrottler::Entry() { const uint32 TIME_BETWEEN_UPLOAD_LOOPS = 1; uint32 lastLoopTick = GetTickCountFullRes(); // Bytes to spend in current cycle. If we spend more this becomes negative and causes a wait next time. sint32 bytesToSpend = 0; uint32 allowedDataRate = 0; uint32 rememberedSlotCounter = 0; uint32 extraSleepTime = TIME_BETWEEN_UPLOAD_LOOPS; while (m_doRun && !TestDestroy()) { uint32 timeSinceLastLoop = GetTickCountFullRes() - lastLoopTick; // Calculate data rate if (thePrefs::GetMaxUpload() == UNLIMITED) { // Try to increase the upload rate from UploadSpeedSense allowedDataRate = (uint32)theStats::GetUploadRate() + 5 * 1024; } else { allowedDataRate = thePrefs::GetMaxUpload() * 1024; } uint32 minFragSize = 1300; uint32 doubleSendSize = minFragSize*2; // send two packages at a time so they can share an ACK if (allowedDataRate < 6*1024) { minFragSize = 536; doubleSendSize = minFragSize; // don't send two packages at a time at very low speeds to give them a smoother load } uint32 sleepTime; if (bytesToSpend < 1) { // We have sent more than allowed in last cycle so we have to wait now // until we can send at least 1 byte. sleepTime = std::max((-bytesToSpend + 1) * 1000 / allowedDataRate + 2, // add 2 ms to allow for rounding inaccuracies extraSleepTime); } else { // We could send at once, but sleep a while to not suck up all cpu sleepTime = extraSleepTime; } if (timeSinceLastLoop < sleepTime) { Sleep(sleepTime-timeSinceLastLoop); } // Check after sleep in case the thread has been signaled to end if (!m_doRun || TestDestroy()) { break; } const uint32 thisLoopTick = GetTickCountFullRes(); timeSinceLastLoop = thisLoopTick - lastLoopTick; lastLoopTick = thisLoopTick; if (timeSinceLastLoop > sleepTime + 2000) { AddDebugLogLineN(logGeneral, CFormat(wxT("UploadBandwidthThrottler: Time since last loop too long. time: %ims wanted: %ims Max: %ims")) % timeSinceLastLoop % sleepTime % (sleepTime + 2000)); timeSinceLastLoop = sleepTime + 2000; } // Calculate how many bytes we can spend bytesToSpend += (sint32) (allowedDataRate / 1000.0 * timeSinceLastLoop); if (bytesToSpend >= 1) { sint32 spentBytes = 0; sint32 spentOverhead = 0; wxMutexLocker sendLock(m_sendLocker); { wxMutexLocker queueLock(m_tempQueueLocker); // are there any sockets in m_TempControlQueue_list? Move them to normal m_ControlQueue_list; m_ControlQueueFirst_list.insert( m_ControlQueueFirst_list.end(), m_TempControlQueueFirst_list.begin(), m_TempControlQueueFirst_list.end() ); m_ControlQueue_list.insert( m_ControlQueue_list.end(), m_TempControlQueue_list.begin(), m_TempControlQueue_list.end() ); m_TempControlQueue_list.clear(); m_TempControlQueueFirst_list.clear(); } // Send any queued up control packets first while (spentBytes < bytesToSpend && (!m_ControlQueueFirst_list.empty() || !m_ControlQueue_list.empty())) { ThrottledControlSocket* socket = NULL; if (!m_ControlQueueFirst_list.empty()) { socket = m_ControlQueueFirst_list.front(); m_ControlQueueFirst_list.pop_front(); } else if (!m_ControlQueue_list.empty()) { socket = m_ControlQueue_list.front(); m_ControlQueue_list.pop_front(); } if (socket != NULL) { SocketSentBytes socketSentBytes = socket->SendControlData(bytesToSpend-spentBytes, minFragSize); spentBytes += socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentOverhead += socketSentBytes.sentBytesControlPackets; } } // Check if any sockets haven't gotten data for a long time. Then trickle them a package. uint32 slots = m_StandardOrder_list.size(); for (uint32 slotCounter = 0; slotCounter < slots; slotCounter++) { ThrottledFileSocket* socket = m_StandardOrder_list[ slotCounter ]; if (socket != NULL) { if (thisLoopTick-socket->GetLastCalledSend() > SEC2MS(1)) { // trickle uint32 neededBytes = socket->GetNeededBytes(); if (neededBytes > 0) { SocketSentBytes socketSentBytes = socket->SendFileAndControlData(neededBytes, minFragSize); spentBytes += socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentOverhead += socketSentBytes.sentBytesControlPackets; } } } else { AddDebugLogLineN(logGeneral, CFormat( wxT("There was a NULL socket in the UploadBandwidthThrottler Standard list (trickle)! Prevented usage. Index: %i Size: %i")) % slotCounter % m_StandardOrder_list.size()); } } // Give available bandwidth to slots, starting with the one we ended with last time. // There are two passes. First pass gives packets of doubleSendSize, second pass // gives as much as possible. // Second pass starts with the last slot of the first pass actually. for (uint32 slotCounter = 0; (slotCounter < slots * 2) && spentBytes < bytesToSpend; slotCounter++) { if (rememberedSlotCounter >= slots) { // wrap around pointer rememberedSlotCounter = 0; } uint32 data = (slotCounter < slots - 1) ? doubleSendSize // pass 1 : (bytesToSpend - spentBytes); // pass 2 ThrottledFileSocket* socket = m_StandardOrder_list[ rememberedSlotCounter ]; if (socket != NULL) { SocketSentBytes socketSentBytes = socket->SendFileAndControlData(data, doubleSendSize); spentBytes += socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentOverhead += socketSentBytes.sentBytesControlPackets; } else { AddDebugLogLineN(logGeneral, CFormat(wxT("There was a NULL socket in the UploadBandwidthThrottler Standard list (equal-for-all)! Prevented usage. Index: %i Size: %i")) % rememberedSlotCounter % m_StandardOrder_list.size()); } rememberedSlotCounter++; } // Do some limiting of what we keep for the next loop. bytesToSpend -= spentBytes; sint32 minBytesToSpend = (slots + 1) * minFragSize; if (bytesToSpend < - minBytesToSpend) { bytesToSpend = - minBytesToSpend; } else { sint32 bandwidthSavedTolerance = slots * 512 + 1; if (bytesToSpend > bandwidthSavedTolerance) { bytesToSpend = bandwidthSavedTolerance; } } m_SentBytesSinceLastCall += spentBytes; m_SentBytesSinceLastCallOverhead += spentOverhead; if (spentBytes == 0) { // spentBytes includes the overhead extraSleepTime = std::min<uint32>(extraSleepTime * 5, 1000); // 1s at most } else { extraSleepTime = TIME_BETWEEN_UPLOAD_LOOPS; } } } { wxMutexLocker queueLock(m_tempQueueLocker); m_TempControlQueue_list.clear(); m_TempControlQueueFirst_list.clear(); } wxMutexLocker sendLock(m_sendLocker); m_ControlQueue_list.clear(); m_StandardOrder_list.clear(); return 0; }
/** * The thread method that handles calling send for the individual sockets. * * Control packets will always be tried to be sent first. If there is any bandwidth leftover * after that, send() for the upload slot sockets will be called in priority order until we have run * out of available bandwidth for this loop. Upload slots will not be allowed to go without having sent * called for more than a defined amount of time (i.e. two seconds). * * @return always returns 0. */ UINT UploadBandwidthThrottler::RunInternal() { DWORD lastLoopTick = timeGetTime(); sint64 realBytesToSpend = 0; uint32 allowedDataRate = 0; uint32 rememberedSlotCounter = 0; DWORD lastTickReachedBandwidth = timeGetTime(); uint32 nEstiminatedLimit = 0; int nSlotsBusyLevel = 0; DWORD nUploadStartTime = 0; uint32 numberOfConsecutiveUpChanges = 0; uint32 numberOfConsecutiveDownChanges = 0; uint32 changesCount = 0; uint32 loopsCount = 0; bool estimateChangedLog = false; bool lotsOfLog = false; while(doRun) { pauseEvent->Lock(); DWORD timeSinceLastLoop = timeGetTime() - lastLoopTick; // Get current speed from UploadSpeedSense allowedDataRate = theApp.lastCommonRouteFinder->GetUpload(); // check busy level for all the slots (WSAEWOULDBLOCK status) uint32 cBusy = 0; uint32 nCanSend = 0; sendLocker.Lock(); for (int i = 0; i < m_StandardOrder_list.GetSize() && (i < 3 || (UINT)i < GetSlotLimit(theApp.uploadqueue->GetDatarate())); i++) { if (m_StandardOrder_list[i] != NULL && m_StandardOrder_list[i]->HasQueues()) { nCanSend++; if(m_StandardOrder_list[i]->IsBusy()) cBusy++; } } sendLocker.Unlock(); // if this is kept, the loop above can be a little optimized (don't count nCanSend, just use nCanSend = GetSlotLimit(theApp.uploadqueue->GetDatarate()) if(theApp.uploadqueue) nCanSend = max(nCanSend, GetSlotLimit(theApp.uploadqueue->GetDatarate())); // When no upload limit has been set in options, try to guess a good upload limit. bool bUploadUnlimited = (thePrefs.GetMaxUpload() == UNLIMITED); if (bUploadUnlimited) { loopsCount++; //if(lotsOfLog) theApp.QueueDebugLogLine(false,_T("Throttler: busy: %i/%i nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount: %i loopsCount: %i"), cBusy, nCanSend, nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); if(nCanSend > 0) { float fBusyPercent = ((float)cBusy/(float)nCanSend) * 100; if (cBusy > 2 && fBusyPercent > 75.00f && nSlotsBusyLevel < 255) { nSlotsBusyLevel++; changesCount++; if(thePrefs.GetVerbose() && lotsOfLog && nSlotsBusyLevel%25==0) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount: %i loopsCount: %i"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); } else if ( (cBusy <= 2 || fBusyPercent < 25.00f) && nSlotsBusyLevel > (-255)) { nSlotsBusyLevel--; changesCount++; if(thePrefs.GetVerbose() && lotsOfLog && nSlotsBusyLevel%25==0) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount %i loopsCount: %i"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); } } if(nUploadStartTime == 0) { if (m_StandardOrder_list.GetSize() >= 3) nUploadStartTime = timeGetTime(); } else if(timeGetTime()- nUploadStartTime > SEC2MS(60)) { if (theApp.uploadqueue) { if (nEstiminatedLimit == 0) { // no autolimit was set yet if (nSlotsBusyLevel >= 250) { // sockets indicated that the BW limit has been reached nEstiminatedLimit = theApp.uploadqueue->GetDatarate(); allowedDataRate = min(nEstiminatedLimit, allowedDataRate); nSlotsBusyLevel = -200; if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: Set inital estimated limit to %0.5f changesCount: %i loopsCount: %i"), (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); changesCount = 0; loopsCount = 0; } } else { if (nSlotsBusyLevel > 250) { if(changesCount > 500 || changesCount > 300 && loopsCount > 1000 || loopsCount > 2000) { numberOfConsecutiveDownChanges = 0; } numberOfConsecutiveDownChanges++; uint32 changeDelta = CalculateChangeDelta(numberOfConsecutiveDownChanges); // Don't lower speed below 1 KBytes/s if(nEstiminatedLimit < changeDelta + 1024) { if(nEstiminatedLimit > 1024) { changeDelta = nEstiminatedLimit - 1024; } else { changeDelta = 0; } } ASSERT(nEstiminatedLimit >= changeDelta + 1024); nEstiminatedLimit -= changeDelta; if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: REDUCED limit #%i with %i bytes to: %0.5f changesCount: %i loopsCount: %i"), numberOfConsecutiveDownChanges, changeDelta, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); numberOfConsecutiveUpChanges = 0; nSlotsBusyLevel = 0; changesCount = 0; loopsCount = 0; } else if (nSlotsBusyLevel < (-250)) { if(changesCount > 500 || changesCount > 300 && loopsCount > 1000 || loopsCount > 2000) { numberOfConsecutiveUpChanges = 0; } numberOfConsecutiveUpChanges++; uint32 changeDelta = CalculateChangeDelta(numberOfConsecutiveUpChanges); // Don't raise speed unless we are under current allowedDataRate if(nEstiminatedLimit+changeDelta > allowedDataRate) { if(nEstiminatedLimit < allowedDataRate) { changeDelta = allowedDataRate - nEstiminatedLimit; } else { changeDelta = 0; } } ASSERT(nEstiminatedLimit < allowedDataRate && nEstiminatedLimit+changeDelta <= allowedDataRate || nEstiminatedLimit >= allowedDataRate && changeDelta == 0); nEstiminatedLimit += changeDelta; if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: INCREASED limit #%i with %i bytes to: %0.5f changesCount: %i loopsCount: %i"), numberOfConsecutiveUpChanges, changeDelta, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); numberOfConsecutiveDownChanges = 0; nSlotsBusyLevel = 0; changesCount = 0; loopsCount = 0; } allowedDataRate = min(nEstiminatedLimit, allowedDataRate); } } } } if(cBusy == nCanSend && m_StandardOrder_list.GetSize() > 0) { allowedDataRate = 0; if(nSlotsBusyLevel < 125 && bUploadUnlimited) { nSlotsBusyLevel = 125; if(thePrefs.GetVerbose() && lotsOfLog) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount %i loopsCount: %i (set due to all slots busy)"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount); } } uint32 minFragSize = 1300; uint32 doubleSendSize = minFragSize*2; // send two packages at a time so they can share an ACK if(allowedDataRate < 6*1024) { minFragSize = 536; doubleSendSize = minFragSize; // don't send two packages at a time at very low speeds to give them a smoother load } #define TIME_BETWEEN_UPLOAD_LOOPS 1 uint32 sleepTime; if(allowedDataRate == _UI32_MAX || realBytesToSpend >= 1000 || allowedDataRate == 0 && nEstiminatedLimit == 0) { // we could send at once, but sleep a while to not suck up all cpu sleepTime = TIME_BETWEEN_UPLOAD_LOOPS; } else if(allowedDataRate == 0) { sleepTime = max((uint32)ceil(((double)doubleSendSize*1000)/nEstiminatedLimit), TIME_BETWEEN_UPLOAD_LOOPS); } else { // sleep for just as long as we need to get back to having one byte to send sleepTime = max((uint32)ceil((double)(-realBytesToSpend + 1000)/allowedDataRate), TIME_BETWEEN_UPLOAD_LOOPS); } if(timeSinceLastLoop < sleepTime) { Sleep(sleepTime-timeSinceLastLoop); } const DWORD thisLoopTick = timeGetTime(); timeSinceLastLoop = thisLoopTick - lastLoopTick; // Calculate how many bytes we can spend sint64 bytesToSpend = 0; if(allowedDataRate != _UI32_MAX) { // prevent overflow if(timeSinceLastLoop == 0) { // no time has passed, so don't add any bytes. Shouldn't happen. bytesToSpend = 0; //realBytesToSpend/1000; } else if(_I64_MAX/timeSinceLastLoop > allowedDataRate && _I64_MAX-allowedDataRate*timeSinceLastLoop > realBytesToSpend) { if(timeSinceLastLoop > sleepTime + 2000) { theApp.QueueDebugLogLine(false,_T("UploadBandwidthThrottler: Time since last loop too long. time: %ims wanted: %ims Max: %ims"), timeSinceLastLoop, sleepTime, sleepTime + 2000); timeSinceLastLoop = sleepTime + 2000; lastLoopTick = thisLoopTick - timeSinceLastLoop; } realBytesToSpend += allowedDataRate*timeSinceLastLoop; bytesToSpend = realBytesToSpend/1000; } else { realBytesToSpend = _I64_MAX; bytesToSpend = _I32_MAX; } } else { realBytesToSpend = 0; //_I64_MAX; bytesToSpend = _I32_MAX; } lastLoopTick = thisLoopTick; if(bytesToSpend >= 1 || allowedDataRate == 0) { uint64 spentBytes = 0; uint64 spentOverhead = 0; sendLocker.Lock(); tempQueueLocker.Lock(); // are there any sockets in m_TempControlQueue_list? Move them to normal m_ControlQueue_list; while(!m_TempControlQueueFirst_list.IsEmpty()) { ThrottledControlSocket* moveSocket = m_TempControlQueueFirst_list.RemoveHead(); m_ControlQueueFirst_list.AddTail(moveSocket); } while(!m_TempControlQueue_list.IsEmpty()) { ThrottledControlSocket* moveSocket = m_TempControlQueue_list.RemoveHead(); m_ControlQueue_list.AddTail(moveSocket); } tempQueueLocker.Unlock(); // Send any queued up control packets first while((bytesToSpend > 0 && spentBytes < (uint64)bytesToSpend || allowedDataRate == 0 && spentBytes < 500) && (!m_ControlQueueFirst_list.IsEmpty() || !m_ControlQueue_list.IsEmpty())) { ThrottledControlSocket* socket = NULL; if(!m_ControlQueueFirst_list.IsEmpty()) { socket = m_ControlQueueFirst_list.RemoveHead(); } else if(!m_ControlQueue_list.IsEmpty()) { socket = m_ControlQueue_list.RemoveHead(); } if(socket != NULL) { SocketSentBytes socketSentBytes = socket->SendControlData(allowedDataRate > 0?(UINT)(bytesToSpend - spentBytes):1, minFragSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; } } // Check if any sockets haven't gotten data for a long time. Then trickle them a package. for(uint32 slotCounter = 0; slotCounter < (uint32)m_StandardOrder_list.GetSize(); slotCounter++) { ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter); if(socket != NULL) { if(thisLoopTick-socket->GetLastCalledSend() > SEC2MS(1)) { // trickle uint32 neededBytes = socket->GetNeededBytes(); if(neededBytes > 0) { SocketSentBytes socketSentBytes = socket->SendFileAndControlData(neededBytes, minFragSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; if(lastSpentBytes > 0 && slotCounter < m_highestNumberOfFullyActivatedSlots) { m_highestNumberOfFullyActivatedSlots = slotCounter; } } } } else { theApp.QueueDebugLogLine(false,_T("There was a NULL socket in the UploadBandwidthThrottler Standard list (trickle)! Prevented usage. Index: %i Size: %i"), slotCounter, m_StandardOrder_list.GetSize()); } } // Equal bandwidth for all slots uint32 maxSlot = (uint32)m_StandardOrder_list.GetSize(); if(maxSlot > 0 && allowedDataRate/maxSlot < UPLOAD_CLIENT_DATARATE) { maxSlot = allowedDataRate/UPLOAD_CLIENT_DATARATE; } if(maxSlot > m_highestNumberOfFullyActivatedSlots) { m_highestNumberOfFullyActivatedSlots = maxSlot; } for(uint32 maxCounter = 0; maxCounter < min(maxSlot, (uint32)m_StandardOrder_list.GetSize()) && bytesToSpend > 0 && spentBytes < (uint64)bytesToSpend; maxCounter++) { if(rememberedSlotCounter >= (uint32)m_StandardOrder_list.GetSize() || rememberedSlotCounter >= maxSlot) { rememberedSlotCounter = 0; } ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(rememberedSlotCounter); if(socket != NULL) { SocketSentBytes socketSentBytes = socket->SendFileAndControlData((UINT)min(doubleSendSize, bytesToSpend-spentBytes), doubleSendSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; } else { theApp.QueueDebugLogLine(false,_T("There was a NULL socket in the UploadBandwidthThrottler Standard list (equal-for-all)! Prevented usage. Index: %i Size: %i"), rememberedSlotCounter, m_StandardOrder_list.GetSize()); } rememberedSlotCounter++; } // Any bandwidth that hasn't been used yet are used first to last. for(uint32 slotCounter = 0; slotCounter < (uint32)m_StandardOrder_list.GetSize() && bytesToSpend > 0 && spentBytes < (uint64)bytesToSpend; slotCounter++) { ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter); if(socket != NULL) { uint32 bytesToSpendTemp = (UINT)(bytesToSpend-spentBytes); SocketSentBytes socketSentBytes = socket->SendFileAndControlData(bytesToSpendTemp, doubleSendSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; if(slotCounter+1 > m_highestNumberOfFullyActivatedSlots && (lastSpentBytes < bytesToSpendTemp || lastSpentBytes >= doubleSendSize)) { // || lastSpentBytes > 0 && spentBytes == bytesToSpend /*|| slotCounter+1 == (uint32)m_StandardOrder_list.GetSize())*/)) { m_highestNumberOfFullyActivatedSlots = slotCounter+1; } } else { theApp.QueueDebugLogLine(false,_T("There was a NULL socket in the UploadBandwidthThrottler Standard list (fully activated)! Prevented usage. Index: %i Size: %i"), slotCounter, m_StandardOrder_list.GetSize()); } } realBytesToSpend -= spentBytes*1000; // If we couldn't spend all allocated bandwidth this loop, some of it is allowed to be saved // and used the next loop if(realBytesToSpend < -(((sint64)m_StandardOrder_list.GetSize()+1)*minFragSize)*1000) { sint64 newRealBytesToSpend = -(((sint64)m_StandardOrder_list.GetSize()+1)*minFragSize)*1000; realBytesToSpend = newRealBytesToSpend; lastTickReachedBandwidth = thisLoopTick; } else { uint64 bandwidthSavedTolerance = 0; if(realBytesToSpend > 0 && (uint64)realBytesToSpend > 999+bandwidthSavedTolerance) { sint64 newRealBytesToSpend = 999+bandwidthSavedTolerance; //theApp.QueueDebugLogLine(false,_T("UploadBandwidthThrottler::RunInternal(): Too high saved bytesToSpend. Limiting value. Old value: %I64i New value: %I64i"), realBytesToSpend, newRealBytesToSpend); realBytesToSpend = newRealBytesToSpend; if(thisLoopTick-lastTickReachedBandwidth > max(1000, timeSinceLastLoop*2)) { m_highestNumberOfFullyActivatedSlots = m_StandardOrder_list.GetSize()+1; lastTickReachedBandwidth = thisLoopTick; //theApp.QueueDebugLogLine(false, _T("UploadBandwidthThrottler: Throttler requests new slot due to bw not reached. m_highestNumberOfFullyActivatedSlots: %i m_StandardOrder_list.GetSize(): %i tick: %i"), m_highestNumberOfFullyActivatedSlots, m_StandardOrder_list.GetSize(), thisLoopTick); } } else { lastTickReachedBandwidth = thisLoopTick; } } // save info about how much bandwidth we've managed to use since the last time someone polled us about used bandwidth m_SentBytesSinceLastCall += spentBytes; m_SentBytesSinceLastCallOverhead += spentOverhead; sendLocker.Unlock(); } } threadEndedEvent->SetEvent(); tempQueueLocker.Lock(); m_TempControlQueue_list.RemoveAll(); m_TempControlQueueFirst_list.RemoveAll(); tempQueueLocker.Unlock(); sendLocker.Lock(); m_ControlQueue_list.RemoveAll(); m_StandardOrder_list.RemoveAll(); sendLocker.Unlock(); return 0; }
/** * The thread method that handles calling send for the individual sockets. * * Control packets will always be tried to be sent first. If there is any bandwidth leftover * after that, send() for the upload slot sockets will be called in priority order until we have run * out of available bandwidth for this loop. Upload slots will not be allowed to go without having sent * called for more than a defined amount of time (i.e. two seconds). * * @return always returns 0. */ UINT UploadBandwidthThrottler::RunInternal() { #define ADJUSTING_STEP (1*1024) DWORD lastLoopTick = ::GetTickCount(); sint64 realBytesToSpend = 0; uint32 allowedDataRate = 0; uint32 rememberedSlotCounter = 0; DWORD lastTickReachedBandwidth = ::GetTickCount(); // WebCache //////////////////////////////////////////////////////////////////////////////////// ////JP Proxy configuration testing START!!! This should probably be somewhere else. if (thePrefs.expectingWebCachePing && (::GetTickCount() - thePrefs.WebCachePingSendTime > SEC2MS(30))) { thePrefs.expectingWebCachePing = false; thePrefs.WebCacheDisabledThisSession = true; //Disable webcache downloads for the current proxy settings //JP we need a modeless dialogue here!! // AfxMessageBox(_T("Proxy configuration Test Failed please review your proxy-settings")); theApp.QueueLogLine(false, _T("Proxy configuration Test Failed please review your proxy-settings. Webcache downloads have been deactivated until emule is restarted.")); } ////JP Proxy configuration testing END!!! This should probably be somewhere else. uint32 nEstiminatedLimit = 0; sint32 nSlotsBusyLevel = 0; DWORD nUploadStartTime = 0; bool bUploadUnlimited; while(doRun) { pauseEvent->Lock(); DWORD timeSinceLastLoop = ::GetTickCount() - lastLoopTick; // Get current speed from UploadSpeedSense allowedDataRate = theApp.lastCommonRouteFinder->GetUpload(); bUploadUnlimited = thePrefs.GetMaxUpload() == UNLIMITED; if (bUploadUnlimited && nUploadStartTime != 0 && ::GetTickCount()- nUploadStartTime > SEC2MS(60) ) { // upload is unlimited if (theApp.uploadqueue) { if (nEstiminatedLimit == 0) { // no autolimit was set yet if (nSlotsBusyLevel >= 250) { // sockets indicated that the BW limit has been reached nEstiminatedLimit = theApp.uploadqueue->GetDatarate() - (3*ADJUSTING_STEP); allowedDataRate = min(nEstiminatedLimit, allowedDataRate); nSlotsBusyLevel = -200; } } else { if (nSlotsBusyLevel > 250) { nEstiminatedLimit -= ADJUSTING_STEP; nSlotsBusyLevel = 0; } else if (nSlotsBusyLevel < (-250)) { nEstiminatedLimit += ADJUSTING_STEP; nSlotsBusyLevel = 0; } allowedDataRate = min(nEstiminatedLimit, allowedDataRate); } } } uint32 minFragSize = 1300; uint32 doubleSendSize = minFragSize*2; // send two packages at a time so they can share an ACK if(allowedDataRate < 6*1024) { minFragSize = 536; doubleSendSize = minFragSize; // don't send two packages at a time at very low speeds to give them a smoother load } #define TIME_BETWEEN_UPLOAD_LOOPS 1 uint32 sleepTime; if(allowedDataRate == 0 || allowedDataRate == _UI32_MAX || realBytesToSpend >= 1000) { // we could send at once, but sleep a while to not suck up all cpu sleepTime = TIME_BETWEEN_UPLOAD_LOOPS; } else { // sleep for just as long as we need to get back to having one byte to send sleepTime = max((uint32)ceil((double)(-realBytesToSpend + 1000)/allowedDataRate), TIME_BETWEEN_UPLOAD_LOOPS); } if(timeSinceLastLoop < sleepTime) { Sleep(sleepTime-timeSinceLastLoop); } const DWORD thisLoopTick = ::GetTickCount(); timeSinceLastLoop = thisLoopTick - lastLoopTick; // Calculate how many bytes we can spend sint64 bytesToSpend = 0; if(allowedDataRate != 0 && allowedDataRate != _UI32_MAX) { // prevent overflow if(timeSinceLastLoop == 0) { // no time has passed, so don't add any bytes. Shouldn't happen. bytesToSpend = 0; //realBytesToSpend/1000; } else if(_I64_MAX/timeSinceLastLoop > allowedDataRate && _I64_MAX-allowedDataRate*timeSinceLastLoop > realBytesToSpend) { if(timeSinceLastLoop > sleepTime + 2000) { theApp.QueueDebugLogLine(false,_T("UploadBandwidthThrottler: Time since last loop too long. time: %ims wanted: %ims Max: %ims"), timeSinceLastLoop, sleepTime, sleepTime + 2000); timeSinceLastLoop = sleepTime + 2000; lastLoopTick = thisLoopTick - timeSinceLastLoop; } realBytesToSpend += allowedDataRate*timeSinceLastLoop; bytesToSpend = realBytesToSpend/1000; } else { realBytesToSpend = _I64_MAX; bytesToSpend = _I32_MAX; } } else { realBytesToSpend = 0; //_I64_MAX; bytesToSpend = _I32_MAX; } lastLoopTick = thisLoopTick; if(bytesToSpend >= 1) { uint64 spentBytes = 0; uint64 spentOverhead = 0; sendLocker.Lock(); tempQueueLocker.Lock(); // are there any sockets in m_TempControlQueue_list? Move them to normal m_ControlQueue_list; while(!m_TempControlQueueFirst_list.IsEmpty()) { ThrottledControlSocket* moveSocket = m_TempControlQueueFirst_list.RemoveHead(); m_ControlQueueFirst_list.AddTail(moveSocket); } while(!m_TempControlQueue_list.IsEmpty()) { ThrottledControlSocket* moveSocket = m_TempControlQueue_list.RemoveHead(); m_ControlQueue_list.AddTail(moveSocket); } tempQueueLocker.Unlock(); // Send any queued up control packets first while(bytesToSpend > 0 && spentBytes < (uint64)bytesToSpend && (!m_ControlQueueFirst_list.IsEmpty() || !m_ControlQueue_list.IsEmpty())) { ThrottledControlSocket* socket = NULL; if(!m_ControlQueueFirst_list.IsEmpty()) { socket = m_ControlQueueFirst_list.RemoveHead(); } else if(!m_ControlQueue_list.IsEmpty()) { socket = m_ControlQueue_list.RemoveHead(); } if(socket != NULL) { SocketSentBytes socketSentBytes = socket->SendControlData((UINT)(bytesToSpend - spentBytes), minFragSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; } } // Check if any sockets haven't gotten data for a long time. Then trickle them a package. for(uint32 slotCounter = 0; slotCounter < (uint32)m_StandardOrder_list.GetSize(); slotCounter++) { ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter); if(socket != NULL) { if(thisLoopTick-socket->GetLastCalledSend() > SEC2MS(1)) { // trickle uint32 neededBytes = socket->GetNeededBytes(); if(neededBytes > 0) { SocketSentBytes socketSentBytes = socket->SendFileAndControlData(neededBytes, minFragSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; if(lastSpentBytes > 0 && slotCounter < m_highestNumberOfFullyActivatedSlots) { m_highestNumberOfFullyActivatedSlots = slotCounter; } } } } else { theApp.QueueDebugLogLine(false,_T("There was a NULL socket in the UploadBandwidthThrottler Standard list (trickle)! Prevented usage. Index: %i Size: %i"), slotCounter, m_StandardOrder_list.GetSize()); } } // Any bandwidth that hasn't been used yet are used first to last. //Telp + Add Slot focus if((thePrefs.GetSlotFocus() == true)) { // Equal bandwidth for all slots uint32 maxSlot = (uint32)m_StandardOrder_list.GetSize(); if(maxSlot > 0 && allowedDataRate/maxSlot < UPLOAD_CLIENT_DATARATE) { maxSlot = allowedDataRate/UPLOAD_CLIENT_DATARATE; } if(maxSlot > m_highestNumberOfFullyActivatedSlots) { m_highestNumberOfFullyActivatedSlots = maxSlot; } for(uint32 maxCounter = 0; maxCounter < min(maxSlot, (uint32)m_StandardOrder_list.GetSize()) && bytesToSpend > 0 && spentBytes < (uint64)bytesToSpend; maxCounter++) { if(rememberedSlotCounter >= (uint32)m_StandardOrder_list.GetSize() || rememberedSlotCounter >= maxSlot) { rememberedSlotCounter = 0; } ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(rememberedSlotCounter); if(socket != NULL) { SocketSentBytes socketSentBytes = socket->SendFileAndControlData((UINT)min(doubleSendSize, bytesToSpend-spentBytes), doubleSendSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; } else { theApp.QueueDebugLogLine(false,_T("There was a NULL socket in the UploadBandwidthThrottler Standard list (equal-for-all)! Prevented usage. Index: %i Size: %i"), rememberedSlotCounter, m_StandardOrder_list.GetSize()); } rememberedSlotCounter++; } if (bUploadUnlimited) { int cBusy = 0; for (int i = 0; i < m_StandardOrder_list.GetSize(); i++) { if (m_StandardOrder_list[i] != NULL && m_StandardOrder_list[i]->IsBusy()) cBusy++; } if (m_StandardOrder_list.GetSize() > 0) { float fBusyPercent = ((float)cBusy/(float)m_StandardOrder_list.GetSize()) * 100; if (cBusy > 2 && fBusyPercent > 75.00f && nSlotsBusyLevel < 255) { nSlotsBusyLevel++; } else if ( (cBusy <= 2 || fBusyPercent < 25.00f) && nSlotsBusyLevel > (-255)) { nSlotsBusyLevel--; } if (m_StandardOrder_list.GetSize() >= 3 && nUploadStartTime == 0) nUploadStartTime = ::GetTickCount(); } } // Any bandwidth that hasn't been used yet are used first to last. } for(uint32 slotCounter = 0; slotCounter < (uint32)m_StandardOrder_list.GetSize() && bytesToSpend > 0 && spentBytes < (uint64)bytesToSpend; slotCounter++) { ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(slotCounter); if(socket != NULL) { uint32 bytesToSpendTemp = (UINT)(bytesToSpend-spentBytes); SocketSentBytes socketSentBytes = socket->SendFileAndControlData(bytesToSpendTemp, doubleSendSize); uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets; spentBytes += lastSpentBytes; spentOverhead += socketSentBytes.sentBytesControlPackets; if(slotCounter+1 > m_highestNumberOfFullyActivatedSlots && (lastSpentBytes < bytesToSpendTemp || lastSpentBytes >= doubleSendSize)) { // || lastSpentBytes > 0 && spentBytes == bytesToSpend /*|| slotCounter+1 == (uint32)m_StandardOrder_list.GetSize())*/)) { m_highestNumberOfFullyActivatedSlots = slotCounter+1; } } else { theApp.QueueDebugLogLine(false,_T("There was a NULL socket in the UploadBandwidthThrottler Standard list (fully activated)! Prevented usage. Index: %i Size: %i"), slotCounter, m_StandardOrder_list.GetSize()); } } realBytesToSpend -= spentBytes*1000; if(realBytesToSpend < -(((sint64)m_StandardOrder_list.GetSize()+1)*minFragSize)*1000) { sint64 newRealBytesToSpend = -(((sint64)m_StandardOrder_list.GetSize()+1)*minFragSize)*1000; realBytesToSpend = newRealBytesToSpend; lastTickReachedBandwidth = thisLoopTick; } else { uint64 bandwidthSavedTolerance = m_StandardOrder_list.GetSize()*512*1000; if(realBytesToSpend > 0 && (uint64)realBytesToSpend > 999+bandwidthSavedTolerance) { sint64 newRealBytesToSpend = 999+bandwidthSavedTolerance; //theApp.QueueDebugLogLine(false,_T("UploadBandwidthThrottler::RunInternal(): Too high saved bytesToSpend. Limiting value. Old value: %I64i New value: %I64i"), realBytesToSpend, newRealBytesToSpend); realBytesToSpend = newRealBytesToSpend; if(thisLoopTick-lastTickReachedBandwidth > max(1000, timeSinceLastLoop*2)) { m_highestNumberOfFullyActivatedSlots = m_StandardOrder_list.GetSize()+1; lastTickReachedBandwidth = thisLoopTick; //theApp.QueueDebugLogLine(false, _T("UploadBandwidthThrottler: Throttler requests new slot due to bw not reached. m_highestNumberOfFullyActivatedSlots: %i m_StandardOrder_list.GetSize(): %i tick: %i"), m_highestNumberOfFullyActivatedSlots, m_StandardOrder_list.GetSize(), thisLoopTick); } } else { lastTickReachedBandwidth = thisLoopTick; } } m_SentBytesSinceLastCall += spentBytes; m_SentBytesSinceLastCallOverhead += spentOverhead; sendLocker.Unlock(); } } threadEndedEvent->SetEvent(); tempQueueLocker.Lock(); m_TempControlQueue_list.RemoveAll(); m_TempControlQueueFirst_list.RemoveAll(); tempQueueLocker.Unlock(); sendLocker.Lock(); m_ControlQueue_list.RemoveAll(); m_StandardOrder_list.RemoveAll(); sendLocker.Unlock(); return 0; }