Update Wiki URL
[amule.git] / src / UploadQueue.cpp
blobab022b4f419941dae5fa96b8579ef5d8d481c265
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "UploadQueue.h" // Interface declarations
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/Client2Client/TCP.h>
30 #include <common/Macros.h>
31 #include <common/Constants.h>
33 #include <cmath>
35 #include "Types.h" // Do_not_auto_remove (win32)
37 #ifdef __WINDOWS__
38 #include <winsock.h> // Do_not_auto_remove
39 #else
40 #include <sys/types.h> // Do_not_auto_remove
41 #include <netinet/in.h> // Do_not_auto_remove
42 #include <arpa/inet.h> // Do_not_auto_remove
43 #endif
45 #include "ServerConnect.h" // Needed for CServerConnect
46 #include "KnownFile.h" // Needed for CKnownFile
47 #include "Packet.h" // Needed for CPacket
48 #include "ClientTCPSocket.h" // Needed for CClientTCPSocket
49 #include "SharedFileList.h" // Needed for CSharedFileList
50 #include "updownclient.h" // Needed for CUpDownClient
51 #include "amule.h" // Needed for theApp
52 #include "Preferences.h"
53 #include "ClientList.h"
54 #include "Statistics.h" // Needed for theStats
55 #include "Logger.h"
56 #include <common/Format.h>
57 #include "UploadBandwidthThrottler.h"
58 #include "GuiEvents.h" // Needed for Notify_*
59 #include "ListenSocket.h"
60 #include "DownloadQueue.h"
61 #include "PartFile.h"
64 //TODO rewrite the whole networkcode, use overlapped sockets
66 CUploadQueue::CUploadQueue()
68 m_nLastStartUpload = 0;
69 m_lastSort = 0;
70 lastupslotHighID = true;
71 m_allowKicking = true;
72 m_allUploadingKnownFile = new CKnownFile;
76 void CUploadQueue::SortGetBestClient(CClientRef * bestClient)
78 uint32 tick = GetTickCount();
79 m_lastSort = tick;
80 CClientRefList::iterator it = m_waitinglist.begin();
81 for (; it != m_waitinglist.end(); ) {
82 CClientRefList::iterator it2 = it++;
83 CUpDownClient* cur_client = it2->GetClient();
85 // clear dead clients
86 if (tick - cur_client->GetLastUpRequest() > MAX_PURGEQUEUETIME
87 || !theApp->sharedfiles->GetFileByID(cur_client->GetUploadFileID())) {
88 cur_client->ClearWaitStartTime();
89 RemoveFromWaitingQueue(it2);
90 if (!cur_client->GetSocket()) {
91 if (cur_client->Disconnected(wxT("AddUpNextClient - purged"))) {
92 cur_client->Safe_Delete();
93 cur_client = NULL;
96 continue;
99 if (cur_client->IsBanned() || IsSuspended(cur_client->GetUploadFileID())) { // Banned client or suspended upload ?
100 cur_client->ClearScore();
101 continue;
103 // finished clearing
105 // Calculate score of current client
106 uint32 cur_score = cur_client->CalculateScore();
107 // Check if it's better than that of a previous one, and move it up then.
108 CClientRefList::iterator it1 = it2;
109 while (it1 != m_waitinglist.begin()) {
110 --it1;
111 if (cur_score > it1->GetClient()->GetScore()) {
112 // swap them
113 std::swap(*it2, *it1);
114 --it2;
115 } else {
116 // no need to check further since list is already sorted
117 break;
122 // Second Pass:
123 // - calculate queue rank
124 // - find best high id client
125 // - mark all better low id clients as enabled for upload
126 uint16 rank = 1;
127 bool bestClientFound = false;
128 for (it = m_waitinglist.begin(); it != m_waitinglist.end(); ) {
129 CClientRefList::iterator it2 = it++;
130 CUpDownClient* cur_client = it2->GetClient();
131 cur_client->SetUploadQueueWaitingPosition(rank++);
132 if (bestClientFound) {
133 // There's a better high id client
134 cur_client->m_bAddNextConnect = false;
135 } else {
136 if (cur_client->HasLowID() && !cur_client->IsConnected()) {
137 // No better high id client, so start upload to this one once it connects
138 cur_client->m_bAddNextConnect = true;
139 } else {
140 // We found a high id client (or a currently connected low id client)
141 bestClientFound = true;
142 cur_client->m_bAddNextConnect = false;
143 if (bestClient) {
144 bestClient->Link(cur_client CLIENT_DEBUGSTRING("CUploadQueue::SortGetBestClient"));
145 RemoveFromWaitingQueue(it2);
146 rank--;
147 lastupslotHighID = true; // VQB LowID alternate
153 #ifdef __DEBUG__
154 AddDebugLogLineN(logLocalClient, CFormat(wxT("Current UL queue (%d):")) % (rank - 1));
155 for (it = m_waitinglist.begin(); it != m_waitinglist.end(); ++it) {
156 CUpDownClient* c = it->GetClient();
157 AddDebugLogLineN(logLocalClient, CFormat(wxT("%4d %7d %s %5d %s"))
158 % c->GetUploadQueueWaitingPosition()
159 % c->GetScore()
160 % (c->HasLowID() ? (c->IsConnected() ? wxT("LoCon") : wxT("LowId")) : wxT("High "))
161 % c->ECID()
162 % c->GetUserName()
165 #endif // __DEBUG__
169 void CUploadQueue::AddUpNextClient(CUpDownClient* directadd)
171 CUpDownClient* newclient = NULL;
172 CClientRef newClientRef;
173 // select next client or use given client
174 if (!directadd) {
175 SortGetBestClient(&newClientRef);
176 newclient = newClientRef.GetClient();
177 #if EXTENDED_UPLOADQUEUE
178 if (!newclient) {
179 // Nothing to upload. Try to find something from the sources.
180 if (PopulatePossiblyWaitingList() > 0) {
181 newClientRef = * m_possiblyWaitingList.begin();
182 m_possiblyWaitingList.pop_front();
183 newclient = newClientRef.GetClient();
184 AddDebugLogLineN( logLocalClient, wxT("Added client from possiblyWaitingList ") + newclient->GetFullIP() );
187 #endif
188 if (!newclient) {
189 return;
191 } else {
192 // Check if requested file is suspended or not shared (maybe deleted recently)
194 if (IsSuspended(directadd->GetUploadFileID())
195 || !theApp->sharedfiles->GetFileByID(directadd->GetUploadFileID())) {
196 return;
197 } else {
198 newclient = directadd;
202 if (IsDownloading(newclient)) {
203 return;
205 // tell the client that we are now ready to upload
206 if (!newclient->IsConnected()) {
207 newclient->SetUploadState(US_CONNECTING);
208 if (!newclient->TryToConnect(true)) {
209 return;
211 } else {
212 CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT);
213 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
214 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + newclient->GetFullIP() );
215 newclient->SendPacket(packet,true);
216 newclient->SetUploadState(US_UPLOADING);
218 newclient->SetUpStartTime();
219 newclient->ResetSessionUp();
221 theApp->uploadBandwidthThrottler->AddToStandardList(m_uploadinglist.size(), newclient->GetSocket());
222 m_uploadinglist.push_back(CCLIENTREF(newclient, wxT("CUploadQueue::AddUpNextClient")));
223 m_allUploadingKnownFile->AddUploadingClient(newclient);
224 theStats::AddUploadingClient();
226 // Statistic
227 CKnownFile* reqfile = const_cast<CKnownFile*>(newclient->GetUploadFile());
228 if (reqfile) {
229 reqfile->statistic.AddAccepted();
232 Notify_SharedCtrlRefreshClient(newclient->ECID(), AVAILABLE_SOURCE);
235 void CUploadQueue::Process()
237 // Check if someone's waiting, if there is a slot for him,
238 // or if we should try to free a slot for him
239 uint32 tick = GetTickCount();
240 // Nobody waiting or upload started recently
241 // (Actually instead of "empty" it should check for "no HighID clients queued",
242 // but the cost for that outweights the benefit. As it is, a slot will be freed
243 // even if it can't be taken because all of the queue is LowID. But just one,
244 // and the kicked client will instantly get it back if he has HighID.)
245 // Also, if we are running out of sockets, don't add new clients, but also don't kick existing ones,
246 // or uploading will cease right away.
247 #if EXTENDED_UPLOADQUEUE
248 if (tick - m_nLastStartUpload < 1000
249 #else
250 if (m_waitinglist.empty() || tick - m_nLastStartUpload < 1000
251 #endif
252 || theApp->listensocket->TooManySockets()) {
253 m_allowKicking = false;
254 // Already a slot free, try to fill it
255 } else if (m_uploadinglist.size() < GetMaxSlots()) {
256 m_allowKicking = false;
257 m_nLastStartUpload = tick;
258 AddUpNextClient();
259 // All slots taken, try to free one
260 } else {
261 m_allowKicking = true;
264 // The loop that feeds the upload slots with data.
265 CClientRefList::iterator it = m_uploadinglist.begin();
266 while (it != m_uploadinglist.end()) {
267 // Get the client. Note! Also updates pos as a side effect.
268 CUpDownClient* cur_client = it++->GetClient();
270 // It seems chatting or friend slots can get stuck at times in upload.. This needs looked into..
271 if (!cur_client->GetSocket()) {
272 RemoveFromUploadQueue(cur_client);
273 if(cur_client->Disconnected(_T("CUploadQueue::Process"))){
274 cur_client->Safe_Delete();
276 } else {
277 cur_client->SendBlockData();
281 // Save used bandwidth for speed calculations
282 uint64 sentBytes = theApp->uploadBandwidthThrottler->GetNumberOfSentBytesSinceLastCallAndReset();
283 (void)theApp->uploadBandwidthThrottler->GetNumberOfSentBytesOverheadSinceLastCallAndReset();
285 // Update statistics
286 if (sentBytes) {
287 theStats::AddSentBytes(sentBytes);
290 // Periodically resort queue if it doesn't happen anyway
291 if ((sint32) (tick - m_lastSort) > MIN2MS(2)) {
292 SortGetBestClient();
297 uint16 CUploadQueue::GetMaxSlots() const
299 uint16 nMaxSlots = 0;
300 float kBpsUpPerClient = (float)thePrefs::GetSlotAllocation();
301 if (thePrefs::GetMaxUpload() == UNLIMITED) {
302 float kBpsUp = theStats::GetUploadRate() / 1024.0f;
303 nMaxSlots = (uint16)(kBpsUp / kBpsUpPerClient) + 2;
304 } else {
305 if (thePrefs::GetMaxUpload() >= 10) {
306 nMaxSlots = (uint16)floor((float)thePrefs::GetMaxUpload() / kBpsUpPerClient + 0.5);
307 // floor(x + 0.5) is a way of doing round(x) that works with gcc < 3 ...
308 if (nMaxSlots < MIN_UP_CLIENTS_ALLOWED) {
309 nMaxSlots=MIN_UP_CLIENTS_ALLOWED;
311 } else {
312 nMaxSlots = MIN_UP_CLIENTS_ALLOWED;
315 if (nMaxSlots > MAX_UP_CLIENTS_ALLOWED) {
316 nMaxSlots = MAX_UP_CLIENTS_ALLOWED;
318 return nMaxSlots;
322 CUploadQueue::~CUploadQueue()
324 wxASSERT(m_waitinglist.empty());
325 wxASSERT(m_uploadinglist.empty());
326 delete m_allUploadingKnownFile;
330 bool CUploadQueue::IsOnUploadQueue(const CUpDownClient* client) const
332 for (CClientRefList::const_iterator it = m_waitinglist.begin(); it != m_waitinglist.end(); ++it) {
333 if (it->GetClient() == client) {
334 return true;
337 return false;
341 bool CUploadQueue::IsDownloading(const CUpDownClient* client) const
343 for (CClientRefList::const_iterator it = m_uploadinglist.begin(); it != m_uploadinglist.end(); ++it) {
344 if (it->GetClient() == client) {
345 return true;
348 return false;
352 CUpDownClient* CUploadQueue::GetWaitingClientByIP_UDP(uint32 dwIP, uint16 nUDPPort, bool bIgnorePortOnUniqueIP, bool* pbMultipleIPs)
354 CUpDownClient* pMatchingIPClient = NULL;
356 int cMatches = 0;
358 CClientRefList::iterator it = m_waitinglist.begin();
359 for (; it != m_waitinglist.end(); ++it) {
360 CUpDownClient* cur_client = it->GetClient();
362 if ((dwIP == cur_client->GetIP()) && (nUDPPort == cur_client->GetUDPPort())) {
363 return cur_client;
364 } else if ((dwIP == cur_client->GetIP()) && bIgnorePortOnUniqueIP) {
365 pMatchingIPClient = cur_client;
366 cMatches++;
370 if (pbMultipleIPs) {
371 *pbMultipleIPs = cMatches > 1;
374 if (pMatchingIPClient && cMatches == 1) {
375 return pMatchingIPClient;
376 } else {
377 return NULL;
382 void CUploadQueue::AddClientToQueue(CUpDownClient* client)
384 if (theApp->serverconnect->IsConnected() && theApp->serverconnect->IsLowID() && !theApp->serverconnect->IsLocalServer(client->GetServerIP(),client->GetServerPort()) && client->GetDownloadState() == DS_NONE && !client->IsFriend() && theStats::GetWaitingUserCount() > 50) {
385 // Well, all that issues finish in the same: don't allow to add to the queue
386 return;
389 if ( client->IsBanned() ) {
390 return;
393 client->AddAskedCount();
394 client->SetLastUpRequest();
396 // Find all clients with the same user-hash
397 CClientList::SourceList found = theApp->clientlist->GetClientsByHash( client->GetUserHash() );
399 CClientList::SourceList::iterator it = found.begin();
400 while (it != found.end()) {
401 CUpDownClient* cur_client = it++->GetClient();
403 if ( IsOnUploadQueue( cur_client ) ) {
404 if ( cur_client == client ) {
405 // This is where LowID clients get their upload slot assigned.
406 // They can't be contacted if they reach top of the queue, so they are just marked for uploading.
407 // When they reconnect next time AddClientToQueue() is called, and they get their slot
408 // through the connection they initiated.
409 // Since at that time no slot is free they get assigned an extra slot,
410 // so then the number of slots exceeds the configured number by one.
411 // To prevent a further increase no more LowID clients get a slot, until
412 // - a HighID client has got one (which happens only after two clients
413 // have been kicked so a slot is free again)
414 // - or there is a free slot, which means there is no HighID client on queue
415 if (client->m_bAddNextConnect) {
416 uint16 maxSlots = GetMaxSlots();
417 if (lastupslotHighID) {
418 maxSlots++;
420 if (m_uploadinglist.size() < maxSlots) {
421 client->m_bAddNextConnect = false;
422 RemoveFromWaitingQueue(client);
423 AddUpNextClient(client);
424 lastupslotHighID = false; // LowID alternate
425 return;
429 client->SendRankingInfo();
430 Notify_SharedCtrlRefreshClient(client->ECID(), AVAILABLE_SOURCE);
431 return;
432 } else {
433 // Hash-clash, remove unidentified clients (possibly both)
435 if ( !cur_client->IsIdentified() ) {
436 // Cur_client isn't identifed, remove it
437 theApp->clientlist->AddTrackClient( cur_client );
439 RemoveFromWaitingQueue( cur_client );
440 if ( !cur_client->GetSocket() ) {
441 if (cur_client->Disconnected( wxT("AddClientToQueue - same userhash") ) ) {
442 cur_client->Safe_Delete();
447 if ( !client->IsIdentified() ) {
448 // New client isn't identified, remove it
449 theApp->clientlist->AddTrackClient( client );
451 if ( !client->GetSocket() ) {
452 if ( client->Disconnected( wxT("AddClientToQueue - same userhash") ) ) {
453 client->Safe_Delete();
457 return;
463 // Count the number of clients with the same IP-address
464 found = theApp->clientlist->GetClientsByIP( client->GetIP() );
466 int ipCount = 0;
467 for ( it = found.begin(); it != found.end(); ++it ) {
468 if ( ( it->GetClient() == client ) || IsOnUploadQueue( it->GetClient() ) ) {
469 ipCount++;
473 // We do not accept more than 3 clients from the same IP
474 if ( ipCount > 3 ) {
475 return;
476 } else if ( theApp->clientlist->GetClientsFromIP(client->GetIP()) >= 3 ) {
477 return;
480 // statistic values
481 CKnownFile* reqfile = const_cast<CKnownFile*>(client->GetUploadFile());
482 if (reqfile) {
483 reqfile->statistic.AddRequest();
486 if (client->IsDownloading()) {
487 // he's already downloading and wants probably only another file
488 CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT);
489 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
490 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + client->GetFullIP() );
491 client->SendPacket(packet,true);
492 return;
495 // TODO find better ways to cap the list
496 if (m_waitinglist.size() >= (thePrefs::GetQueueSize())) {
497 return;
500 uint32 tick = GetTickCount();
501 client->ClearWaitStartTime();
502 // if possible start upload right away
503 if (m_waitinglist.empty() && tick - m_nLastStartUpload >= 1000
504 && m_uploadinglist.size() < GetMaxSlots()
505 && !theApp->listensocket->TooManySockets()) {
506 AddUpNextClient(client);
507 m_nLastStartUpload = tick;
508 } else {
509 // add to waiting queue
510 m_waitinglist.push_back(CCLIENTREF(client, wxT("CUploadQueue::AddClientToQueue m_waitinglist.push_back")));
511 // and sort it to update queue ranks
512 SortGetBestClient();
513 theStats::AddWaitingClient();
514 client->ClearAskedCount();
515 client->SetUploadState(US_ONUPLOADQUEUE);
516 client->SendRankingInfo();
517 //Notify_QlistAddClient(client);
522 bool CUploadQueue::RemoveFromUploadQueue(CUpDownClient* client)
524 // Keep track of this client
525 theApp->clientlist->AddTrackClient(client);
527 CClientRefList::iterator it = std::find(m_uploadinglist.begin(),
528 m_uploadinglist.end(), CCLIENTREF(client, wxEmptyString));
530 if (it != m_uploadinglist.end()) {
531 m_uploadinglist.erase(it);
532 m_allUploadingKnownFile->RemoveUploadingClient(client);
533 theStats::RemoveUploadingClient();
534 if( client->GetTransferredUp() ) {
535 theStats::AddSuccessfulUpload();
536 theStats::AddUploadTime(client->GetUpStartTimeDelay() / 1000);
537 } else {
538 theStats::AddFailedUpload();
540 client->SetUploadState(US_NONE);
541 client->ClearUploadBlockRequests();
542 return true;
545 return false;
549 bool CUploadQueue::CheckForTimeOver(CUpDownClient* client)
551 // Don't kick anybody if there's no need to
552 if (!m_allowKicking) {
553 return false;
555 // First, check if it is a VIP slot (friend or Release-Prio).
556 if (client->GetFriendSlot()) {
557 return false; // never drop the friend
559 // Release-Prio and nobody on queue for it?
560 if (client->GetUploadFile()->GetUpPriority() == PR_POWERSHARE) {
561 // Keep it unless half of the UL slots are occupied with friends or Release uploads.
562 uint16 vips = 0;
563 for (CClientRefList::iterator it = m_uploadinglist.begin(); it != m_uploadinglist.end(); ++it) {
564 CUpDownClient* cur_client = it->GetClient();
565 if (cur_client->GetFriendSlot() || cur_client->GetUploadFile()->GetUpPriority() == PR_POWERSHARE) {
566 vips++;
569 // allow if VIP uploads occupy at most half of the possible upload slots
570 if (vips <= GetMaxSlots() / 2) {
571 return false;
573 // Otherwise normal rules apply.
576 // Ordinary slots
577 // "Transfer full chunks": drop client after 10 MB upload, or after an hour.
578 // (so average UL speed should at least be 2.84 kB/s)
579 // We don't track what he is downloading, but if it's all from one chunk he gets it.
580 if (client->GetUpStartTimeDelay() > 3600000 // time: 1h
581 || client->GetSessionUp() > 10485760) { // data: 10MB
582 m_allowKicking = false; // kick max one client per cycle
583 return true;
586 return false;
591 * This function removes a file indicated by filehash from suspended_uploads_list.
593 void CUploadQueue::ResumeUpload( const CMD4Hash& filehash )
595 suspendedUploadsSet.erase(filehash);
596 AddLogLineN(CFormat( _("Resuming uploads of file: %s" ) )
597 % filehash.Encode() );
601 * This function stops upload of a file indicated by filehash.
603 * a) teminate == false:
604 * File is suspended while a download completes. Then it is resumed after completion,
605 * so it makes sense to keep the client. Such files are kept in suspendedUploadsSet.
606 * b) teminate == true:
607 * File is deleted. Then the client is not added to the waiting list.
608 * Waiting clients are swept out with next run of AddUpNextClient,
609 * because their file is not shared anymore.
611 uint16 CUploadQueue::SuspendUpload(const CMD4Hash& filehash, bool terminate)
613 AddLogLineN(CFormat( _("Suspending upload of file: %s" ) )
614 % filehash.Encode() );
615 uint16 removed = 0;
617 //Append the filehash to the list.
618 if (!terminate) {
619 suspendedUploadsSet.insert(filehash);
622 CClientRefList::iterator it = m_uploadinglist.begin();
623 while (it != m_uploadinglist.end()) {
624 CUpDownClient *potential = it++->GetClient();
625 //check if the client is uploading the file we need to suspend
626 if (potential->GetUploadFileID() == filehash) {
627 // remove the unlucky client from the upload queue
628 RemoveFromUploadQueue(potential);
629 // if suspend isn't permanent add it to the waiting queue
630 if (terminate) {
631 potential->SetUploadState(US_NONE);
632 } else {
633 m_waitinglist.push_back(CCLIENTREF(potential, wxT("CUploadQueue::SuspendUpload")));
634 theStats::AddWaitingClient();
635 potential->SetUploadState(US_ONUPLOADQUEUE);
636 potential->SendRankingInfo();
637 Notify_SharedCtrlRefreshClient(potential->ECID(), AVAILABLE_SOURCE);
639 removed++;
642 return removed;
645 bool CUploadQueue::RemoveFromWaitingQueue(CUpDownClient* client)
647 CClientRefList::iterator it = m_waitinglist.begin();
649 uint16 rank = 1;
650 while (it != m_waitinglist.end()) {
651 CClientRefList::iterator it1 = it++;
652 if (it1->GetClient() == client) {
653 RemoveFromWaitingQueue(it1);
654 // update ranks of remaining queue
655 while (it != m_waitinglist.end()) {
656 it->GetClient()->SetUploadQueueWaitingPosition(rank++);
657 ++it;
659 return true;
661 rank++;
663 return false;
667 void CUploadQueue::RemoveFromWaitingQueue(CClientRefList::iterator pos)
669 CUpDownClient* todelete = pos->GetClient();
670 m_waitinglist.erase(pos);
671 theStats::RemoveWaitingClient();
672 if( todelete->IsBanned() ) {
673 todelete->UnBan();
675 //Notify_QlistRemoveClient(todelete);
676 todelete->SetUploadState(US_NONE);
677 todelete->ClearScore();
678 todelete->SetUploadQueueWaitingPosition(0);
681 #if EXTENDED_UPLOADQUEUE
683 int CUploadQueue::PopulatePossiblyWaitingList()
685 static uint32 lastPopulate = 0;
686 uint32 tick = GetTickCount();
687 int ret = m_possiblyWaitingList.size();
688 if (tick - lastPopulate > MIN2MS(15)) {
689 // repopulate in any case after this time
690 m_possiblyWaitingList.clear();
691 } else if (ret || tick - lastPopulate < SEC2MS(30)) {
692 // don't retry too fast if list is empty
693 return ret;
695 lastPopulate = tick;
697 // Get our downloads
698 int nrDownloads = theApp->downloadqueue->GetFileCount();
699 for (int idownload = 0; idownload < nrDownloads; idownload++) {
700 CPartFile * download = theApp->downloadqueue->GetFileByIndex(idownload);
701 if (!download || download->GetAvailablePartCount() == 0) {
702 continue;
704 const CKnownFile::SourceSet& sources = download->GetSourceList();
705 if (sources.empty()) {
706 continue;
708 // Make a table which parts are available. No need to notify a client
709 // which needs nothing we have.
710 uint16 parts = download->GetPartCount();
711 std::vector<bool> partsAvailable;
712 partsAvailable.resize(parts);
713 for (uint32 i = parts; i--;) {
714 partsAvailable[i] = download->IsComplete(i);
716 for (CKnownFile::SourceSet::const_iterator it = sources.begin(); it != sources.end(); it++) {
717 // Iterate over our sources, find those where download == upload
718 CUpDownClient * client = it->GetClient();
719 if (!client || client->GetUploadFile() != download || client->HasLowID()) {
720 continue;
722 const BitVector& partStatus = client->GetPartStatus();
723 if (partStatus.size() != parts) {
724 continue;
726 // Do we have something for him?
727 bool haveSomething = false;
728 for (uint32 i = parts; i--;) {
729 if (partsAvailable[i] && !partStatus.get(i)) {
730 haveSomething = true;
731 break;
734 if (haveSomething) {
735 m_possiblyWaitingList.push_back(*it);
739 ret = m_possiblyWaitingList.size();
740 AddDebugLogLineN(logLocalClient, CFormat(wxT("Populated PossiblyWaitingList: %d")) % ret);
741 return ret;
744 #endif // EXTENDED_UPLOADQUEUE
746 // File_checked_for_headers