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