1 /* 2 * swarmmanager.cpp 3 * 4 * Created by Thomas Schaap 5 * Copyright 2009-2016 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved. 6 * 7 */ 8 9 10 #include <string.h> 11 #include <time.h> 12 13 #define SWARMMANAGER_ASSERT_INVARIANTS 1 14 15 #include "swift.h" 16 #include "swarmmanager.h" 17 18 #define SECONDS_UNTIL_INDEX_REUSE 120 19 #define SECONDS_UNUSED_UNTIL_SWARM_MAY_BE_DEACTIVATED 300 20 21 #ifdef __APPLE__ 22 #define DEFAULT_MAX_ACTIVE_SWARMS 224 // 2 file descriptors per swarm 256 max 23 #else 24 #define DEFAULT_MAX_ACTIVE_SWARMS 480 // 2 file descriptors per swarm 25 #endif 26 27 #if SWARMMANAGER_ASSERT_INVARIANTS 28 #include <assert.h> 29 int levelcount = 0; 30 // Arno: enter/leave prints disabled 31 #define enter( x ) 32 //fprintf( stderr, "[%02d] Entered " x "\n", ++levelcount ); 33 #define exit( x ) 34 //fprintf( stderr, "[%02d] Leaving " x "\n", levelcount-- ); 35 #else 36 #undef assert 37 #define assert( x ) 38 #define invariant() 39 #define enter( x ) 40 #define exit( x ) 41 #endif 42 43 #define manager_debug false 44 45 46 namespace swift 47 { 48 49 // FIXME: Difference between seeds (complete) and downloads; allow setting minimum number of seeds? 50 // -> Currently not supported, but the basis is there. 51 // -> Also needs mechanisms to automatically decide to swap swarms back in, such as a timed check on progress. 52 // FIXME: Build and run assert methods (switch on/off by define) 53 54 SwarmManager SwarmManager::instance_; 55 SwarmData(const std::string filename,const Sha1Hash & rootHash,const std::string trackerurl,bool force_check_diskvshash,popt_cont_int_prot_t cipm,bool zerostate,uint32_t chunk_size,std::string metadir)56 SwarmData::SwarmData(const std::string filename, const Sha1Hash& rootHash, const std::string trackerurl, 57 bool force_check_diskvshash, popt_cont_int_prot_t cipm, bool zerostate, uint32_t chunk_size, std::string metadir) : 58 id_(-1), rootHash_(rootHash), active_(false), latestUse_(0), stateToBeRemoved_(false), contentToBeRemoved_(false), 59 ft_(NULL), 60 filename_(filename), trackerurl_(trackerurl), forceCheckDiskVSHash_(force_check_diskvshash), contIntProtMethod_(cipm), 61 chunkSize_(chunk_size), zerostate_(zerostate), cached_(false), metadir_(metadir) 62 { 63 } 64 SwarmData(const SwarmData & sd)65 SwarmData::SwarmData(const SwarmData& sd) : // Arno, 2012-12-05: Note: latestUse not copied 66 id_(-1), rootHash_(sd.rootHash_), active_(false), latestUse_(0), stateToBeRemoved_(false), contentToBeRemoved_(false), 67 ft_(NULL), 68 filename_(sd.filename_), trackerurl_(sd.trackerurl_), forceCheckDiskVSHash_(sd.forceCheckDiskVSHash_), 69 contIntProtMethod_(sd.contIntProtMethod_), chunkSize_(sd.chunkSize_), zerostate_(sd.zerostate_), cached_(false), 70 metadir_(sd.metadir_) 71 { 72 } 73 ~SwarmData()74 SwarmData::~SwarmData() 75 { 76 if (ft_) 77 delete ft_; 78 } 79 Touch(bool onlyifactive)80 bool SwarmData::Touch(bool onlyifactive) 81 { 82 if (onlyifactive && !active_) 83 return false; 84 latestUse_ = usec_time(); 85 return true; 86 } 87 IsActive()88 bool SwarmData::IsActive() 89 { 90 return active_; 91 } 92 RootHash()93 const Sha1Hash& SwarmData::RootHash() 94 { 95 return rootHash_; 96 } 97 Id()98 int SwarmData::Id() 99 { 100 return id_; 101 } 102 103 104 // Can return NULL GetTransfer(bool touch)105 FileTransfer* SwarmData::GetTransfer(bool touch) 106 { 107 if (touch) { 108 if (!Touch()) 109 return NULL; 110 } else { 111 if (!IsActive()) 112 return NULL; 113 } 114 assert(ft_); 115 return ft_; 116 } 117 Filename()118 std::string& SwarmData::Filename() 119 { 120 return filename_; 121 } 122 Tracker()123 std::string SwarmData::Tracker() 124 { 125 return trackerurl_; 126 } 127 ChunkSize()128 uint32_t SwarmData::ChunkSize() 129 { 130 return chunkSize_; 131 } 132 IsZeroState()133 bool SwarmData::IsZeroState() 134 { 135 return zerostate_; 136 } 137 Metadir()138 std::string SwarmData::Metadir() 139 { 140 return metadir_; 141 } 142 143 SetMaxSpeed(data_direction_t ddir,double speed)144 void SwarmData::SetMaxSpeed(data_direction_t ddir, double speed) 145 { 146 if (speed <= 0) 147 return; 148 if (ft_) { 149 assert(!cached_); 150 // Arno, 2012-05-25: SetMaxSpeed resets the current speed history, so 151 // be careful here. 152 if (ft_->GetMaxSpeed(ddir) != speed) 153 ft_->SetMaxSpeed(ddir, speed); 154 } else if (cached_) 155 cachedMaxSpeeds_[ddir] = speed; 156 } 157 AddProgressCallback(ProgressCallback cb,uint8_t agg)158 void SwarmData::AddProgressCallback(ProgressCallback cb, uint8_t agg) 159 { 160 if (ft_) { 161 assert(!cached_); 162 ft_->AddProgressCallback(cb, agg); 163 } else if (cached_) 164 cachedCallbacks_.push_back(std::pair<ProgressCallback, uint8_t>(cb, agg)); 165 } 166 RemoveProgressCallback(ProgressCallback cb)167 void SwarmData::RemoveProgressCallback(ProgressCallback cb) 168 { 169 if (ft_) { 170 assert(!cached_); 171 ft_->RemoveProgressCallback(cb); 172 } else if (cached_) { 173 for (std::list< std::pair<ProgressCallback, uint8_t> >::iterator iter = cachedCallbacks_.begin(); 174 iter != cachedCallbacks_.end(); iter++) { 175 if ((*iter).first == cb) { 176 cachedCallbacks_.erase(iter); 177 break; 178 } 179 } 180 } 181 } 182 Size()183 uint64_t SwarmData::Size() 184 { 185 186 if (ft_) { 187 assert(!cached_); 188 return ft_->hashtree()->size(); 189 } else if (cached_) 190 return cachedSize_; 191 return 0; 192 } 193 IsComplete()194 bool SwarmData::IsComplete() 195 { 196 197 if (ft_) { 198 assert(!cached_); 199 return ft_->hashtree()->is_complete(); 200 } else if (cached_) { 201 // Arno, 2012-10-16: Handle start-of-download case. 202 assert(cachedSize_ == 0 || ((cachedSize_ == cachedComplete_) == cachedIsComplete_)); 203 return cachedIsComplete_; 204 } 205 return false; 206 } 207 Complete()208 uint64_t SwarmData::Complete() 209 { 210 211 if (ft_) { 212 assert(!cached_); 213 return ft_->hashtree()->complete(); 214 } else if (cached_) 215 return cachedComplete_; 216 return 0; 217 } 218 SeqComplete(int64_t offset)219 uint64_t SwarmData::SeqComplete(int64_t offset) 220 { 221 if (ft_) { 222 assert(!cached_); 223 return ft_->hashtree()->seq_complete(offset); 224 } else if (offset == 0 && cached_) 225 return cachedComplete_; 226 else { 227 // Need to wake the process to answer this 228 SwarmData* swarm = SwarmManager::GetManager().ActivateSwarm(rootHash_); 229 if (swarm) { 230 assert(ft_); 231 return ft_->hashtree()->seq_complete(offset); 232 } 233 } 234 return 0; 235 } 236 237 OSPathName()238 std::string SwarmData::OSPathName() 239 { 240 if (ft_) { 241 assert(!cached_); 242 return ft_->GetStorage()->GetOSPathName(); 243 } else if (cached_) 244 return cachedOSPathName_; 245 return std::string(); 246 } 247 SwarmManager()248 SwarmManager::SwarmManager() : 249 knownSwarms_(64, std::vector<SwarmData*>()), swarmList_(), unusedIndices_(), 250 eventCheckToBeRemoved_(NULL), 251 maxActiveSwarms_(DEFAULT_MAX_ACTIVE_SWARMS), activeSwarmCount_(0), activeSwarms_() 252 { 253 enter("cons"); 254 // Do not call the invariant here, directly or indirectly: screws up event creation 255 exit("cons"); 256 } 257 ~SwarmManager()258 SwarmManager::~SwarmManager() 259 { 260 enter("dest"); 261 std::list<SwarmData*> dellist; 262 for (std::vector<SwarmData*>::iterator iter = swarmList_.begin(); iter != swarmList_.end(); iter++) 263 dellist.push_back(*iter); 264 for (std::list<SwarmData*>::iterator deliter = dellist.begin(); deliter != dellist.end(); deliter++) 265 delete(*deliter); 266 if (eventCheckToBeRemoved_ != NULL) 267 event_free(eventCheckToBeRemoved_); 268 exit("dest"); 269 } 270 271 #define rootHashToList( rootHash ) (knownSwarms_[rootHash.bits[0]&63]) 272 AddSwarm(const std::string filename,const Sha1Hash & hash,const std::string trackerurl,bool force_check_diskvshash,popt_cont_int_prot_t cipm,bool zerostate,bool activate,uint32_t chunk_size,std::string metadir)273 SwarmData* SwarmManager::AddSwarm(const std::string filename, const Sha1Hash& hash, const std::string trackerurl, 274 bool force_check_diskvshash, popt_cont_int_prot_t cipm, bool zerostate, bool activate, uint32_t chunk_size, 275 std::string metadir) 276 { 277 //fprintf(stderr,"sm: AddSwarm %s hash %s track %s cdisk %d cipm %" PRIu32 " zs %d act %d cs %" PRIu32 "\n", filename.c_str(), hash.hex().c_str(), trackerurl.c_str(), force_check_diskvshash, cipm, zerostate, activate, chunk_size ); 278 enter("addswarm( many )"); 279 invariant(); 280 SwarmData sd(filename, hash, trackerurl, force_check_diskvshash, cipm, zerostate, chunk_size, metadir); 281 #if SWARMMANAGER_ASSERT_INVARIANTS 282 SwarmData* res = AddSwarm(sd, activate); 283 assert(hash == Sha1Hash::ZERO || res == FindSwarm(hash)); 284 assert(!res || res == FindSwarm(res->Id())); 285 invariant(); 286 exit("addswarm( many )"); 287 return res; 288 #else 289 return AddSwarm(sd, activate); 290 #endif 291 } 292 293 // Can return NULL. Can also return a non-active swarm, even though it tries to activate by default. AddSwarm(const SwarmData & swarm,bool activate)294 SwarmData* SwarmManager::AddSwarm(const SwarmData& swarm, bool activate) 295 { 296 enter("addswarm( swarm )"); 297 invariant(); 298 299 SwarmData* newSwarm = new SwarmData(swarm); 300 // Arno: create SwarmData from checkpoint 301 if (swarm.rootHash_ == Sha1Hash::ZERO && !activate) { 302 std::string binmap_filename = swarm.filename_; 303 binmap_filename.append(".mbinmap"); 304 305 // Arno, 2012-01-03: Hack to discover root hash of a file on disk, such that 306 // we don't load it twice. 307 MmapHashTree *ht = new MmapHashTree(true,binmap_filename); 308 //fprintf(stderr,"sm: AddSwarm: File %s may have hash %s\n", swarm.filename_.c_str(), ht->root_hash().hex().c_str() ); 309 310 std::string hash_filename = swarm.filename_; 311 hash_filename.append(".mhash"); 312 313 bool mhash_exists=true; 314 int64_t mhash_size = file_size_by_path_utf8(hash_filename); 315 if (mhash_size <= 0) 316 mhash_exists = false; 317 // ARNOTODO: sanity check if mhash = Sha1Hash-in-bytes * size-of-tree(ht) 318 319 int64_t content_size = file_size_by_path_utf8(swarm.filename_); 320 321 if (mhash_exists && content_size >=0 && ht->complete() == content_size) { 322 //fprintf(stderr,"sm: AddSwarm: Swarm good on disk, let sleep %s\n", swarm.filename_.c_str() ); 323 // Swarm is good on disk, create SwarmData without activation 324 newSwarm->cached_ = true; 325 newSwarm->rootHash_ = ht->root_hash(); 326 newSwarm->cachedComplete_ = ht->complete(); 327 newSwarm->cachedSize_ = content_size; 328 newSwarm->cachedIsComplete_ = true; 329 newSwarm->cachedOSPathName_ = swarm.filename_; 330 newSwarm->cachedStorageReady_ = true; 331 332 // ARNOTODO: REGISTER AT TRACKER!!!! 333 } else { 334 //fprintf(stderr,"sm: AddSwarm: Swarm incomplete, mhash %d complete %" PRIu64 " content %" PRIi64 "\n", (int)mhash_exists, ht->complete(), content_size ); 335 // Swarm incomplete, can't let sleep 336 activate = true; 337 } 338 } 339 340 if (newSwarm->rootHash_ == Sha1Hash::ZERO) { 341 // FIXME: Handle a swarm that has no rootHash yet in a better way: queue it and build the rootHash in the background. 342 BuildSwarm(newSwarm); 343 if (!newSwarm->ft_) { 344 delete newSwarm; 345 exit("addswarm( swarm ) (1)"); 346 return NULL; 347 } 348 } 349 350 //Arno: check for duplicates 351 std::vector<SwarmData*>& list = rootHashToList(newSwarm->rootHash_); 352 int loc = GetSwarmLocation(list, newSwarm->rootHash_); 353 if (loc < list.size() && list[loc]->rootHash_ == newSwarm->rootHash_) { 354 Sha1Hash gotroothash = newSwarm->rootHash_; 355 delete newSwarm; 356 // Let's assume here that the rest of the data is, hence, also equal 357 assert(gotroothash != Sha1Hash::ZERO); 358 assert(list[loc] == FindSwarm(gotroothash)); 359 invariant(); 360 exit("addswarm( swarm ) (2)"); 361 return list[loc]; 362 } 363 assert(loc <= list.size()); 364 list.push_back(NULL); 365 for (int i = list.size() - 1; i > loc; i--) 366 list[i] = list[i - 1]; 367 list[loc] = newSwarm; 368 assert(rootHashToList(newSwarm->rootHash_)[loc] == newSwarm); 369 if (unusedIndices_.size() > 0 && unusedIndices_.front().since < (usec_time() - SECONDS_UNTIL_INDEX_REUSE)) { 370 newSwarm->id_ = unusedIndices_.front().index; 371 unusedIndices_.pop_front(); 372 swarmList_[newSwarm->id_] = newSwarm; 373 } else { 374 newSwarm->id_ = swarmList_.size(); 375 swarmList_.push_back(newSwarm); 376 } 377 378 // Arno: transfer id as assigned by SwarmManager not known at constructor time :-( 379 if (newSwarm->ft_) { 380 newSwarm->ft_->SetTD(newSwarm->id_); 381 382 // Arno, 2013-09-11: BuildSwarm could not use ExternalTrackerClient while td was -1 383 if (manager_debug) 384 fprintf(stderr,"swarmmgr: AddSwarm: ConnectToTracker\n"); 385 else 386 dprintf("\tswarmmgr: AddSwarm: ConnectToTracker\n"); 387 newSwarm->ft_->ConnectToTracker(); 388 } 389 390 // Arno 391 if (activate) { 392 if (!ActivateSwarm(newSwarm) && newSwarm->ft_) { 393 delete newSwarm->ft_; 394 newSwarm->ft_ = NULL; 395 } else // Arno, 2012-12-05: Make sure latestUse_ is set. 396 newSwarm->Touch(false); 397 } 398 assert(swarm.rootHash_ == Sha1Hash::ZERO || newSwarm == FindSwarm(swarm.rootHash_)); 399 assert(newSwarm == FindSwarm(newSwarm->Id())); 400 invariant(); 401 exit("addswarm( swarm )"); 402 return newSwarm; 403 } 404 BuildSwarm(SwarmData * swarm)405 void SwarmManager::BuildSwarm(SwarmData* swarm) 406 { 407 enter("buildswarm"); 408 assert(swarm); 409 invariant(); 410 // Refuse to seed a 0-byte file 411 if (swarm->rootHash_ == Sha1Hash::ZERO && file_size_by_path_utf8(swarm->filename_) == 0) 412 return; 413 414 swarm->ft_ = new FileTransfer(swarm->id_, swarm->filename_, swarm->rootHash_, swarm->forceCheckDiskVSHash_, 415 swarm->contIntProtMethod_, swarm->chunkSize_, swarm->zerostate_, swarm->metadir_); 416 if (!swarm->ft_ || !swarm->ft_->IsOperational()) { // Arno, 2012-10-01: Check if operational 417 exit("buildswarm (1)"); 418 return; 419 } 420 if (swarm->rootHash_ == Sha1Hash::ZERO) 421 swarm->rootHash_ = swarm->ft_->swarm_id().roothash(); 422 assert(swarm->RootHash() != Sha1Hash::ZERO); 423 if (swarm->cached_) { 424 swarm->cached_ = false; 425 swarm->SetMaxSpeed(DDIR_DOWNLOAD, swarm->cachedMaxSpeeds_[DDIR_DOWNLOAD]); 426 swarm->SetMaxSpeed(DDIR_UPLOAD, swarm->cachedMaxSpeeds_[DDIR_UPLOAD]); 427 for (std::list< std::pair<ProgressCallback, uint8_t> >::iterator iter = swarm->cachedCallbacks_.begin(); 428 iter != swarm->cachedCallbacks_.end(); iter++) 429 swarm->AddProgressCallback((*iter).first, (*iter).second); 430 swarm->cachedStorageFilenames_.clear(); 431 swarm->cachedCallbacks_.clear(); 432 swarm->cachedOSPathName_ = std::string(); 433 } 434 // Hashes have been checked, don't check again 435 swarm->forceCheckDiskVSHash_ = false; 436 if (swarm->trackerurl_ != "" && swarm->id_ != -1) { 437 // initiate tracker connections 438 // SWIFTPROC 439 swarm->ft_->SetTracker(swarm->trackerurl_); 440 if (manager_debug) 441 fprintf(stderr,"swarmmgr: BuildSwarm: ConnectToTracker\n"); 442 else 443 dprintf("\tswarmmgr: BuildSwarm: ConnectToTracker\n"); 444 swarm->ft_->ConnectToTracker(); 445 } 446 447 // Swarm just became active (because ->ft_), but still needs to be made ->active_, so invariant does not hold 448 exit("buildswarm"); 449 } 450 451 // Arno: Removes the swarm, also if active RemoveSwarm(const Sha1Hash & rootHash,bool removeState,bool removeContent)452 void SwarmManager::RemoveSwarm(const Sha1Hash& rootHash, bool removeState, bool removeContent) 453 { 454 enter("removeswarm"); 455 invariant(); 456 assert(rootHash != Sha1Hash::ZERO); 457 std::vector<SwarmData*>& list = rootHashToList(rootHash); 458 int loc = GetSwarmLocation(list, rootHash); 459 if (loc == list.size()) { 460 exit("removeswarm (1)"); 461 return; 462 } 463 SwarmData* swarm = list[loc]; 464 465 // Arno, 2012-10-16: Remove from active list 466 int activeLoc = -1; 467 for (int i = 0; i < activeSwarms_.size(); i++) { 468 if (activeSwarms_[i] == swarm) { 469 activeLoc = i; 470 break; 471 } 472 } 473 if (activeLoc != -1) { 474 swarm->active_ = false; 475 activeSwarms_[activeLoc] = activeSwarms_[activeSwarms_.size()-1]; 476 activeSwarms_.pop_back(); 477 activeSwarmCount_--; 478 } 479 480 if (swarm->rootHash_ == rootHash) { 481 for (int i = loc; i < list.size() - 1; i++) 482 list[i] = list[i+1]; 483 list.pop_back(); 484 } 485 struct SwarmManager::UnusedIndex ui; 486 ui.index = swarm->id_; 487 ui.since = usec_time(); 488 swarmList_[ui.index] = NULL; 489 unusedIndices_.push_back(ui); 490 invariant(); 491 assert(!FindSwarm(rootHash)); 492 assert(!FindSwarm(swarm->Id())); 493 494 //MULTIFILE 495 // Arno, 2012-05-23: Copy all filename to be deleted to a set. This info is lost after 496 // swift::Close() and we need to call Close() to let the storage layer close the open files. 497 // TODO: remove the dirs we created, if now empty. 498 std::set<std::string> delset; 499 std::string contentfilename; 500 contentfilename = swarm->OSPathName(); 501 502 // Delete content + .mhash from filesystem, if desired 503 if (removeContent) 504 delset.insert(contentfilename); 505 506 if (removeState) { 507 std::string mhashfilename = contentfilename + ".mhash"; 508 delset.insert(mhashfilename); 509 510 // Arno, 2012-01-10: .mbinmap gots to go too. 511 std::string mbinmapfilename = contentfilename + ".mbinmap"; 512 delset.insert(mbinmapfilename); 513 } 514 515 // MULTIFILE 516 bool ready; 517 if (swarm->ft_) 518 ready = swarm->ft_->GetStorage()->IsReady(); 519 else 520 ready = swarm->cachedStorageReady_; 521 if (removeContent && ready) { 522 if (swarm->ft_) { 523 storage_files_t::iterator iter; 524 storage_files_t sfs = swarm->ft_->GetStorage()->GetStorageFiles(); 525 for (iter = sfs.begin(); iter != sfs.end(); iter++) { 526 std::string cfn = ((StorageFile*)*iter)->GetOSPathName(); 527 delset.insert(cfn); 528 } 529 } else { 530 std::list<std::string>::iterator iter; 531 std::list<std::string> filenames = swarm->cachedStorageFilenames_; 532 for (iter = filenames.begin(); iter != filenames.end(); iter++) 533 delset.insert(*iter); 534 } 535 } 536 537 if (swarm->ft_) { 538 // Arno, 2013-09-11: If external tracker, sign off 539 swarm->ft_->ConnectToTracker(true); 540 } 541 542 delete swarm; // Arno, 2012-10-01: calls delete ft_ which causes storage layer to close files 543 544 std::set<std::string>::iterator iter; 545 for (iter=delset.begin(); iter!=delset.end(); iter++) { 546 std::string filename = *iter; 547 int ret = remove_utf8(filename); 548 if (ret < 0) { 549 print_error("Could not remove file"); 550 } 551 } 552 553 invariant(); 554 exit("removeswarm"); 555 } 556 CheckSwarmsToBeRemovedCallback(evutil_socket_t fd,short events,void * arg)557 void SwarmManager::CheckSwarmsToBeRemovedCallback(evutil_socket_t fd, short events, void* arg) 558 { 559 enter("static checkswarms"); 560 ((SwarmManager*)arg)->CheckSwarmsToBeRemoved(); 561 exit("static checkswarms"); 562 } 563 CheckSwarmsToBeRemoved()564 void SwarmManager::CheckSwarmsToBeRemoved() 565 { 566 enter("checkswarms"); 567 invariant(); 568 569 // If we have too much swarms active, aggressively try to remove swarms 570 while (activeSwarmCount_ > maxActiveSwarms_) 571 if (!DeactivateSwarm()) 572 break; 573 574 if (activeSwarmCount_ > maxActiveSwarms_) 575 evtimer_add(eventCheckToBeRemoved_, tint2tv(5*TINT_SEC)); 576 invariant(); 577 exit("checkswarms"); 578 } 579 580 // Called from invariant() FindSwarm(int id)581 SwarmData* SwarmManager::FindSwarm(int id) 582 { 583 //enter( "findswarm( id )" ); 584 if (id < 0 || id >= swarmList_.size()) { 585 exit("findswarm( id ) (1)"); 586 return NULL; 587 } 588 assert(!swarmList_[id] || swarmList_[id]->Id() == id); 589 //exit( "findswarm( id )" ); 590 return swarmList_[id]; 591 } 592 593 // Called from invariant() FindSwarm(const Sha1Hash & rootHash)594 SwarmData* SwarmManager::FindSwarm(const Sha1Hash& rootHash) 595 { 596 //enter( "findswarm( hash )" ); 597 SwarmData* swarm = GetSwarmData(rootHash); 598 if (swarm && swarm->rootHash_ == rootHash) { 599 assert(swarm->RootHash() == rootHash); 600 //exit( "findswarm( hash ) (1)" ); 601 return swarm; 602 } 603 //exit( "findswarm( hash )" ); 604 return NULL; 605 } 606 607 // Returns NULL if !containsSwarm( rootHash ) or too many swarms are already active ActivateSwarm(const Sha1Hash & rootHash)608 SwarmData* SwarmManager::ActivateSwarm(const Sha1Hash& rootHash) 609 { 610 enter("activateswarm( hash )"); 611 assert(rootHash != Sha1Hash::ZERO); 612 invariant(); 613 SwarmData* sd = GetSwarmData(rootHash); 614 if (!sd || !(sd->rootHash_ == rootHash)) { 615 exit("activateswarm( hash ) (1)"); 616 return NULL; 617 } 618 #if SWARMMANAGER_ASSERT_INVARIANTS 619 SwarmData* res = ActivateSwarm(sd); 620 assert(!res || res->IsActive()); 621 invariant(); 622 exit("activateswarm( hash )"); 623 return res; 624 #else 625 return ActivateSwarm(sd); 626 #endif 627 } 628 ActivateSwarm(SwarmData * sd)629 SwarmData* SwarmManager::ActivateSwarm(SwarmData* sd) 630 { 631 enter("activateswarm( swarm )"); 632 assert(sd); 633 assert(FindSwarm(sd->Id()) == sd); 634 // invariant doesn't necessarily hold for sd, here (might have ft_ and !active_) 635 636 if (sd->active_) { 637 exit("activateswarm( swarm ) (1)"); 638 return sd; 639 } 640 641 if (activeSwarmCount_ >= maxActiveSwarms_) { 642 if (!DeactivateSwarm()) { 643 if (sd->ft_) { 644 delete sd->ft_; 645 sd->ft_ = NULL; // Arno, 2012-10-16: clear var 646 } 647 invariant(); 648 exit("activateswarm( swarm ) (2)"); 649 return NULL; 650 } 651 } 652 653 if (!sd->ft_ || !sd->ft_->IsOperational()) { 654 if (sd->ft_) { 655 delete sd->ft_; 656 sd->ft_ = NULL; // Arno, 2012-10-16: clear var 657 } 658 BuildSwarm(sd); 659 660 if (!sd->ft_ || !sd->ft_->IsOperational()) { 661 if (sd->ft_) { 662 delete sd->ft_; 663 sd->ft_ = NULL; // Arno, 2012-10-16: clear var 664 } 665 invariant(); 666 exit("activateswarm( swarm ) (3)"); 667 return NULL; 668 } 669 } 670 671 activeSwarmCount_++; 672 673 sd->active_ = true; 674 sd->latestUse_ = 0; 675 activeSwarms_.push_back(sd); 676 677 invariant(); 678 exit("activateswarm( swarm )"); 679 return sd; 680 } 681 DeactivateSwarm(SwarmData * swarm,int activeLoc)682 void SwarmManager::DeactivateSwarm(SwarmData* swarm, int activeLoc) 683 { 684 enter("deactivateswarm(swarm,loc)"); 685 assert(swarm); 686 assert(activeSwarms_[activeLoc] == swarm); 687 688 // Checkpoint before deactivating 689 if (Checkpoint(swarm->Id()) == -1 && !swarm->zerostate_) { 690 // Checkpoint failed and it's not due to not being needed in zerostate; better check the hashes next timey 691 swarm->forceCheckDiskVSHash_ = true; 692 } 693 694 swarm->active_ = false; 695 activeSwarms_[activeLoc] = activeSwarms_[activeSwarms_.size()-1]; 696 activeSwarms_.pop_back(); 697 activeSwarmCount_--; 698 699 if (swarm->ft_) { 700 swarm->cachedMaxSpeeds_[DDIR_DOWNLOAD] = swarm->ft_->GetMaxSpeed(DDIR_DOWNLOAD); 701 swarm->cachedMaxSpeeds_[DDIR_UPLOAD] = swarm->ft_->GetMaxSpeed(DDIR_UPLOAD); 702 swarm->cachedStorageReady_ = swarm->ft_->GetStorage()->IsReady(); 703 if (swarm->cachedStorageReady_) { 704 storage_files_t sfs = swarm->ft_->GetStorage()->GetStorageFiles(); 705 for (storage_files_t::iterator iter = sfs.begin(); iter != sfs.end(); iter++) 706 swarm->cachedStorageFilenames_.push_back(((StorageFile*)*iter)->GetOSPathName()); 707 } 708 swarm->cachedSize_ = swarm->Size(); 709 swarm->cachedIsComplete_ = swarm->IsComplete(); 710 swarm->cachedComplete_ = swarm->Complete(); 711 swarm->cachedSeqComplete_ = swarm->SeqComplete(); 712 swarm->cachedOSPathName_ = swarm->OSPathName(); 713 progcallbackregs_t pcs = swarm->ft_->GetProgressCallbackRegistrations(); 714 progcallbackregs_t::iterator iter; 715 for (iter = pcs.begin(); iter != pcs.end(); iter++) 716 swarm->cachedCallbacks_.push_back(progcallbackreg_t((*iter).first,(*iter).second)); 717 swarm->cached_ = true; 718 delete swarm->ft_; 719 swarm->ft_ = NULL; 720 } 721 722 exit("deactivateswarm(swarm,loc)"); 723 } 724 DeactivateSwarm(const Sha1Hash & rootHash)725 void SwarmManager::DeactivateSwarm(const Sha1Hash& rootHash) 726 { 727 enter("deactivateswarm(hash)"); 728 invariant(); 729 SwarmData* swarm = FindSwarm(rootHash); 730 if (!swarm) { 731 exit("deactivateswarm(hash) (1)"); 732 return; 733 } 734 735 for (int i = 0; i < activeSwarms_.size(); i++) { 736 if (activeSwarms_[i] == swarm) { 737 DeactivateSwarm(swarm, i); 738 invariant(); 739 exit("deactivateswarm(hash) (2)"); 740 return; 741 } 742 } 743 744 invariant(); 745 exit("deactivateswarm(hash)"); 746 } 747 DeactivateSwarm()748 bool SwarmManager::DeactivateSwarm() 749 { 750 // This can be called from ActivateSwarm(swarm), where the invariant need not hold 751 enter("deactivateswarm"); 752 753 // Arno, 2012-10-01: This is just a LRU policy, not even looking at #conns :-( 754 755 tint old = usec_time() - SECONDS_UNUSED_UNTIL_SWARM_MAY_BE_DEACTIVATED*TINT_SEC; 756 SwarmData* oldest = NULL; 757 int oldestloc = 0; 758 for (int i = 0; i < activeSwarms_.size(); i++) { 759 if (activeSwarms_[i]->latestUse_ < old && (!oldest || (oldest->latestUse_ > activeSwarms_[i]->latestUse_))) { 760 oldest = activeSwarms_[i]; 761 oldestloc = i; 762 } 763 } 764 if (!oldest) { 765 exit("deactivateswarm (1)"); 766 return false; 767 } 768 769 DeactivateSwarm(oldest, oldestloc); 770 771 exit("deactivateswarm"); 772 return true; 773 } 774 775 776 // Arno: Called from ContentTransfer::GlobalCleanCallback DeactivateIdleSwarms()777 void SwarmManager::DeactivateIdleSwarms() 778 { 779 enter("deactivateidleswarms"); 780 while (DeactivateSwarm()) 781 ; 782 exit("deactivateidleswarms"); 783 } 784 785 GetMaximumActiveSwarms()786 int SwarmManager::GetMaximumActiveSwarms() 787 { 788 return maxActiveSwarms_; 789 } 790 SetMaximumActiveSwarms(int newMaxSwarms)791 void SwarmManager::SetMaximumActiveSwarms(int newMaxSwarms) 792 { 793 enter("setmaximumactiveswarms"); 794 invariant(); 795 if (newMaxSwarms <= 0) { 796 exit("setmaximumactiveswarms (1)"); 797 return; 798 } 799 while (newMaxSwarms < activeSwarmCount_) 800 if (!DeactivateSwarm()) 801 break; 802 maxActiveSwarms_ = newMaxSwarms; 803 if (maxActiveSwarms_ < activeSwarmCount_ && !evtimer_pending(eventCheckToBeRemoved_, NULL)) 804 evtimer_add(eventCheckToBeRemoved_, tint2tv(5*TINT_SEC)); 805 invariant(); 806 exit("setmaximumativeswarms"); 807 } 808 809 // Called from invariant() GetSwarmData(const Sha1Hash & rootHash)810 SwarmData* SwarmManager::GetSwarmData(const Sha1Hash& rootHash) 811 { 812 //enter( "getswarmdata" ); 813 std::vector<SwarmData*>& list = rootHashToList(rootHash); 814 int loc = GetSwarmLocation(list, rootHash); 815 if (loc >= list.size()) { 816 //exit( "getswarmdata (1)" ); 817 return NULL; 818 } 819 //exit( "getswarmdata" ); 820 return list[loc]; 821 } 822 823 // Called from invariant() GetSwarmLocation(const std::vector<SwarmData * > & list,const Sha1Hash & rootHash)824 int SwarmManager::GetSwarmLocation(const std::vector<SwarmData*>& list, const Sha1Hash& rootHash) 825 { 826 //enter( "getswarmlocation" ); 827 int low = 0; 828 int high = list.size(); 829 int mid, c, res; 830 uint8_t* bits; 831 const uint8_t* bitsTarget = rootHash.bits; 832 while (low < high) { 833 mid = (low + high) / 2; 834 bits = list[mid]->rootHash_.bits; 835 res = memcmp(bits, bitsTarget, Sha1Hash::SIZE); 836 if (res < 0) 837 low = mid + 1; 838 else if (res > 0) 839 high = mid; 840 else { 841 assert(mid >= 0 && mid < list.size()); 842 //exit( "getswarmlocation (1)" ); 843 return mid; 844 } 845 } 846 assert(low >= 0 && low <= list.size()); 847 #if SWARMMANAGER_ASSERT_INVARIANTS 848 if (low == list.size()) { 849 for (int i = 0; i < list.size(); i++) 850 assert(list[i]->rootHash_ != rootHash); 851 } 852 //exit( "getswarmlocation" ); 853 #endif 854 return low; 855 } 856 GetManager()857 SwarmManager& SwarmManager::GetManager() 858 { 859 // Deferred, since Channel::evbase is created later 860 if (!instance_.eventCheckToBeRemoved_) { 861 //ARNOTODO: timer only runs when GetManager gets called regularly... 862 instance_.eventCheckToBeRemoved_ = evtimer_new(Channel::evbase, CheckSwarmsToBeRemovedCallback, &instance_); 863 } 864 return instance_; 865 } 866 867 868 //Arno GetTransferDescriptors()869 tdlist_t SwarmManager::GetTransferDescriptors() 870 { 871 tdlist_t tdl; 872 for (int i=0; i<swarmList_.size(); i++) { 873 if (swarmList_[i] != NULL) 874 tdl.push_back(i); 875 } 876 return tdl; 877 } 878 879 880 Iterator()881 SwarmManager::Iterator::Iterator() 882 { 883 transfer_ = -1; 884 (void)operator++(); 885 } Iterator(int transfer)886 SwarmManager::Iterator::Iterator(int transfer) : transfer_(transfer) {} Iterator(const Iterator & other)887 SwarmManager::Iterator::Iterator(const Iterator& other) : transfer_(other.transfer_) {} operator ++()888 SwarmManager::Iterator& SwarmManager::Iterator::operator++() 889 { 890 transfer_++; 891 for (; transfer_ < SwarmManager::GetManager().swarmList_.size(); transfer_++) { 892 if (SwarmManager::GetManager().swarmList_[transfer_]) 893 break; 894 } 895 return *this; 896 } operator ++(int)897 SwarmManager::Iterator SwarmManager::Iterator::operator++(int) 898 { 899 SwarmManager::Iterator tmp(*this); 900 (void)operator++(); 901 return tmp; 902 } operator ==(const SwarmManager::Iterator & other)903 bool SwarmManager::Iterator::operator==(const SwarmManager::Iterator& other) 904 { 905 return transfer_ == other.transfer_; 906 } operator !=(const SwarmManager::Iterator & other)907 bool SwarmManager::Iterator::operator!=(const SwarmManager::Iterator& other) 908 { 909 return transfer_ != other.transfer_; 910 } operator *()911 SwarmData* SwarmManager::Iterator::operator*() 912 { 913 if (transfer_ < SwarmManager::GetManager().swarmList_.size()) 914 return SwarmManager::GetManager().swarmList_[transfer_]; 915 return NULL; 916 } begin()917 SwarmManager::Iterator SwarmManager::begin() 918 { 919 return SwarmManager::Iterator(); 920 } end()921 SwarmManager::Iterator SwarmManager::end() 922 { 923 return SwarmManager::Iterator(swarmList_.size()); 924 } 925 926 #if SWARMMANAGER_ASSERT_INVARIANTS invariant()927 void SwarmManager::invariant() 928 { 929 enter("inv"); 930 int i, j; 931 bool f; 932 int c1, c2, c3; 933 c1 = 0; 934 c3 = 0; 935 tint t; 936 for (i = 0; i < 64; i++) { 937 std::vector<SwarmData*> l = knownSwarms_[i]; 938 for (std::vector<SwarmData*>::iterator iter = l.begin(); iter != l.end(); iter++) { 939 assert((*iter)); 940 assert((*iter)->RootHash() != Sha1Hash::ZERO); 941 assert(((*iter)->RootHash().bits[0] & 63) == i); 942 f = false; 943 for (std::vector<SwarmData*>::iterator iter2 = swarmList_.begin(); iter2 != swarmList_.end(); iter2++) { 944 if ((*iter) == (*iter2)) { 945 f = true; 946 break; 947 } 948 } 949 assert(f); 950 c1++; 951 } 952 for (j = 1; j < l.size(); j++) 953 assert(memcmp(l[j-1]->RootHash().bits, l[j]->RootHash().bits, Sha1Hash::SIZE) < 0); 954 for (j = 0; j < l.size(); j++) 955 assert(GetSwarmLocation(l, l[j]->RootHash()) == j); 956 } 957 c2 = 0; 958 for (std::vector<SwarmData*>::iterator iter = swarmList_.begin(); iter != swarmList_.end(); iter++) { 959 if (!(*iter)) { 960 c3++; 961 continue; 962 } 963 if ((*iter)->RootHash() != Sha1Hash::ZERO) { 964 assert(GetSwarmData((*iter)->RootHash()) == (*iter)); 965 c2++; 966 } 967 assert((((bool)(*iter)->ft_) ^ (!(*iter)->IsActive()))); 968 } 969 assert(!FindSwarm(-1)); 970 assert(!FindSwarm(Sha1Hash::ZERO)); 971 for (i = 0; i < swarmList_.size(); i++) { 972 assert((!swarmList_[i]) || (swarmList_[i]->Id() == i)); 973 if (swarmList_[i]) { 974 assert(swarmList_[i] == FindSwarm(i)); 975 assert((swarmList_[i]->RootHash() == Sha1Hash::ZERO) || (swarmList_[i] == FindSwarm(swarmList_[i]->RootHash()))); 976 } else 977 assert(!FindSwarm(i)); 978 } 979 assert(!FindSwarm(swarmList_.size())); 980 t = 0; 981 for (std::list<UnusedIndex>::iterator iter = unusedIndices_.begin(); iter != unusedIndices_.end(); iter++) { 982 assert((*iter).index >= 0); 983 assert((*iter).index < swarmList_.size()); 984 assert(!swarmList_[(*iter).index]); 985 assert((*iter).since > t); 986 t = (*iter).since; 987 } 988 assert(c1 == c2); 989 assert(c3 == unusedIndices_.size()); 990 c1 = 0; 991 for (Iterator iter = begin(); iter != end(); iter++) { 992 assert(*iter); 993 assert((*iter)->Id() >= 0); 994 assert((*iter)->Id() < swarmList_.size()); 995 assert(swarmList_[(*iter)->Id()] == (*iter)); 996 c1++; 997 } 998 assert(c1 == (swarmList_.size() - c3)); 999 1000 c1 = 0; 1001 for (std::vector<SwarmData*>::iterator iter = swarmList_.begin(); iter != swarmList_.end(); iter++) { 1002 if ((*iter) && (*iter)->IsActive()) 1003 c1++; 1004 } 1005 for (std::vector<SwarmData*>::iterator iter = activeSwarms_.begin(); iter != activeSwarms_.end(); iter++) { 1006 assert((*iter)); 1007 assert((*iter)->IsActive()); 1008 assert((*iter)->Id() >= 0); 1009 assert((*iter)->Id() < swarmList_.size()); 1010 assert(swarmList_[(*iter)->Id()] == (*iter)); 1011 } 1012 assert(c1 <= maxActiveSwarms_ || evtimer_pending(eventCheckToBeRemoved_, NULL)); 1013 assert(c1 == activeSwarmCount_); 1014 assert(activeSwarmCount_ == activeSwarms_.size()); 1015 exit("inv"); 1016 } 1017 #endif 1018 } 1019