X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/8b89e57fa2ae7d34b055b8f804cee0c2c194043b..b98f285980c3c2701539e196993ead268141aaeb:/apt-pkg/acquire.cc diff --git a/apt-pkg/acquire.cc b/apt-pkg/acquire.cc index 4ed21831d..ccc28a3f7 100644 --- a/apt-pkg/acquire.cc +++ b/apt-pkg/acquire.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: acquire.cc,v 1.5 1998/10/26 07:11:47 jgg Exp $ +// $Id: acquire.cc,v 1.11 1998/11/11 06:54:16 jgg Exp $ /* ###################################################################### Acquire - File Acquiration @@ -22,12 +22,14 @@ #include #include #include + +#include /*}}}*/ // Acquire::pkgAcquire - Constructor /*{{{*/ // --------------------------------------------------------------------- -/* */ -pkgAcquire::pkgAcquire() +/* We grab some runtime state from the configuration space */ +pkgAcquire::pkgAcquire(pkgAcquireStatus *Log) : Log(Log) { Queues = 0; Configs = 0; @@ -46,7 +48,7 @@ pkgAcquire::pkgAcquire() /*}}}*/ // Acquire::~pkgAcquire - Destructor /*{{{*/ // --------------------------------------------------------------------- -/* Free our memory */ +/* Free our memory, clean up the queues (destroy the workers) */ pkgAcquire::~pkgAcquire() { while (Items.size() != 0) @@ -69,7 +71,8 @@ pkgAcquire::~pkgAcquire() /*}}}*/ // Acquire::Add - Add a new item /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* This puts an item on the acquire list. This list is mainly for tracking + item status */ void pkgAcquire::Add(Item *Itm) { Items.push_back(Itm); @@ -77,19 +80,20 @@ void pkgAcquire::Add(Item *Itm) /*}}}*/ // Acquire::Remove - Remove a item /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* Remove an item from the acquire list. This is usually not used.. */ void pkgAcquire::Remove(Item *Itm) { for (vector::iterator I = Items.begin(); I < Items.end(); I++) { if (*I == Itm) Items.erase(I); - } + } } /*}}}*/ // Acquire::Add - Add a worker /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* A list of workers is kept so that the select loop can direct their FD + usage. */ void pkgAcquire::Add(Worker *Work) { Work->NextAcquire = Workers; @@ -98,9 +102,14 @@ void pkgAcquire::Add(Worker *Work) /*}}}*/ // Acquire::Remove - Remove a worker /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* A worker has died. This can not be done while the select loop is running + as it would require that RunFds could handling a changing list state and + it cant.. */ void pkgAcquire::Remove(Worker *Work) { + if (Running == true) + abort(); + Worker **I = &Workers; for (; *I != 0;) { @@ -113,11 +122,14 @@ void pkgAcquire::Remove(Worker *Work) /*}}}*/ // Acquire::Enqueue - Queue an URI for fetching /*{{{*/ // --------------------------------------------------------------------- -/* */ -void pkgAcquire::Enqueue(Item *Itm,string URI,string Description) +/* This is the entry point for an item. An item calls this function when + it is construction which creates a queue (based on the current queue + mode) and puts the item in that queue. If the system is running then + the queue might be started. */ +void pkgAcquire::Enqueue(ItemDesc &Item) { // Determine which queue to put the item in - string Name = QueueName(URI); + string Name = QueueName(Item.URI); if (Name.empty() == true) return; @@ -129,33 +141,41 @@ void pkgAcquire::Enqueue(Item *Itm,string URI,string Description) I = new Queue(Name,this); I->Next = Queues; Queues = I; + + if (Running == true) + I->Startup(); } + + Item.Owner->Status = Item::StatIdle; // Queue it into the named queue - I->Enqueue(Itm,URI,Description); + I->Enqueue(Item); ToFetch++; - - if (Running == true) - I->Startup(); - + // Some trace stuff if (Debug == true) { - clog << "Fetching " << URI << endl; - clog << " to " << Itm->DestFile << endl; - clog << " Queue is: " << QueueName(URI) << endl; + clog << "Fetching " << Item.URI << endl; + clog << " to " << Item.Owner->DestFile << endl; + clog << " Queue is: " << QueueName(Item.URI) << endl; } } /*}}}*/ // Acquire::Dequeue - Remove an item from all queues /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* This is called when an item is finished being fetched. It removes it + from all the queues */ void pkgAcquire::Dequeue(Item *Itm) { Queue *I = Queues; + bool Res = false; for (; I != 0; I = I->Next) - I->Dequeue(Itm); - ToFetch--; + Res |= I->Dequeue(Itm); + + if (Debug == true) + clog << "Dequeuing " << Itm->DestFile << endl; + if (Res == true) + ToFetch--; } /*}}}*/ // Acquire::QueueName - Return the name of the queue for this URI /*{{{*/ @@ -163,24 +183,20 @@ void pkgAcquire::Dequeue(Item *Itm) /* The string returned depends on the configuration settings and the method parameters. Given something like http://foo.org/bar it can return http://foo.org or http */ -string pkgAcquire::QueueName(string URI) +string pkgAcquire::QueueName(string Uri) { - const MethodConfig *Config = GetConfig(URIAccess(URI)); + URI U(Uri); + + const MethodConfig *Config = GetConfig(U.Access); if (Config == 0) return string(); /* Single-Instance methods get exactly one queue per URI. This is also used for the Access queue method */ if (Config->SingleInstance == true || QueueMode == QueueAccess) - return URIAccess(URI); - - // Host based queue - string::iterator I = URI.begin(); - for (; I < URI.end() && *I != ':'; I++); - for (; I < URI.end() && (*I == '/' || *I == ':'); I++); - for (; I < URI.end() && *I != '/'; I++); - - return string(URI,0,I - URI.begin()); + return U.Access; + + return U.Access + ':' + U.Host; } /*}}}*/ // Acquire::GetConfig - Fetch the configuration information /*{{{*/ @@ -234,7 +250,9 @@ void pkgAcquire::SetFds(int &Fd,fd_set *RSet,fd_set *WSet) /*}}}*/ // Acquire::RunFds - Deal with active FDs /*{{{*/ // --------------------------------------------------------------------- -/* Dispatch active FDs over to the proper workers */ +/* Dispatch active FDs over to the proper workers. It is very important + that a worker never be erased while this is running! The queue class + should never erase a worker except during shutdown processing. */ void pkgAcquire::RunFds(fd_set *RSet,fd_set *WSet) { for (Worker *I = Workers; I != 0; I = I->NextAcquire) @@ -258,7 +276,13 @@ bool pkgAcquire::Run() for (Queue *I = Queues; I != 0; I = I->Next) I->Startup(); + if (Log != 0) + Log->Start(); + // Run till all things have been acquired + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 500000; while (ToFetch > 0) { fd_set RFds; @@ -268,22 +292,57 @@ bool pkgAcquire::Run() FD_ZERO(&WFds); SetFds(Highest,&RFds,&WFds); - if (select(Highest+1,&RFds,&WFds,0,0) <= 0) + int Res = select(Highest+1,&RFds,&WFds,0,&tv); + if (Res < 0) { - Running = false; - return _error->Errno("select","Select has failed"); + _error->Errno("select","Select has failed"); + break; } - + RunFds(&RFds,&WFds); + if (_error->PendingError() == true) + break; + + // Timeout, notify the log class + if (Res == 0 || (Log != 0 && Log->Update == true)) + { + tv.tv_usec = 500000; + for (Worker *I = Workers; I != 0; I = I->NextAcquire) + I->Pulse(); + if (Log != 0) + Log->Pulse(this); + } } + + if (Log != 0) + Log->Stop(); + // Shut down the acquire bits + Running = false; for (Queue *I = Queues; I != 0; I = I->Next) I->Shutdown(); - Running = false; - return true; + return _error->PendingError(); +} + /*}}}*/ +// Acquire::Bump - Called when an item is dequeued /*{{{*/ +// --------------------------------------------------------------------- +/* This routine bumps idle queues in hopes that they will be able to fetch + the dequeued item */ +void pkgAcquire::Bump() +{ + for (Queue *I = Queues; I != 0; I = I->Next) + I->Bump(); } /*}}}*/ +// Acquire::WorkerStep - Step to the next worker /*{{{*/ +// --------------------------------------------------------------------- +/* Not inlined to advoid including acquire-worker.h */ +pkgAcquire::Worker *pkgAcquire::WorkerStep(Worker *I) +{ + return I->NextAcquire; +}; + /*}}}*/ // Acquire::MethodConfig::MethodConfig - Constructor /*{{{*/ // --------------------------------------------------------------------- @@ -327,25 +386,26 @@ pkgAcquire::Queue::~Queue() // Queue::Enqueue - Queue an item to the queue /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgAcquire::Queue::Enqueue(Item *Owner,string URI,string Description) +void pkgAcquire::Queue::Enqueue(ItemDesc &Item) { // Create a new item - QItem *I = new QItem; + QItem *I = new QItem; I->Next = Items; Items = I; + *I = Item; - // Fill it in - Items->Owner = Owner; - Items->URI = URI; - Items->Description = Description; - Owner->QueueCounter++; + Item.Owner->QueueCounter++; + if (Items->Next == 0) + Cycle(); } /*}}}*/ // Queue::Dequeue - Remove an item from the queue /*{{{*/ // --------------------------------------------------------------------- -/* */ -void pkgAcquire::Queue::Dequeue(Item *Owner) +/* We return true if we hit something*/ +bool pkgAcquire::Queue::Dequeue(Item *Owner) { + bool Res = false; + QItem **I = &Items; for (; *I != 0;) { @@ -355,10 +415,13 @@ void pkgAcquire::Queue::Dequeue(Item *Owner) *I = (*I)->Next; Owner->QueueCounter--; delete Jnk; + Res = true; } else I = &(*I)->Next; } + + return Res; } /*}}}*/ // Queue::Startup - Start the worker processes /*{{{*/ @@ -368,19 +431,17 @@ bool pkgAcquire::Queue::Startup() { Shutdown(); - pkgAcquire::MethodConfig *Cnf = Owner->GetConfig(URIAccess(Name)); + URI U(Name); + pkgAcquire::MethodConfig *Cnf = Owner->GetConfig(U.Access); if (Cnf == 0) return false; - Workers = new Worker(this,Cnf); + Workers = new Worker(this,Cnf,Owner->Log); Owner->Add(Workers); if (Workers->Start() == false) return false; - - Items->Worker = Workers; - Workers->QueueItem(Items); - return true; + return Cycle(); } /*}}}*/ // Queue::Shutdown - Shutdown the worker processes /*{{{*/ @@ -414,16 +475,168 @@ pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Wor // Queue::ItemDone - Item has been completed /*{{{*/ // --------------------------------------------------------------------- /* The worker signals this which causes the item to be removed from the - queue. */ + queue. If this is the last queue instance then it is removed from the + main queue too.*/ bool pkgAcquire::Queue::ItemDone(QItem *Itm) { - Dequeue(Itm->Owner); + if (Itm->Owner->QueueCounter <= 1) + Owner->Dequeue(Itm->Owner); + else + { + Dequeue(Itm->Owner); + Owner->Bump(); + } - if (Items == 0) + return Cycle(); +} + /*}}}*/ +// Queue::Cycle - Queue new items into the method /*{{{*/ +// --------------------------------------------------------------------- +/* This locates a new idle item and sends it to the worker */ +bool pkgAcquire::Queue::Cycle() +{ + if (Items == 0 || Workers == 0) + return true; + + // Look for a queable item + QItem *I = Items; + for (; I != 0; I = I->Next) + if (I->Owner->Status == pkgAcquire::Item::StatIdle) + break; + + // Nothing to do, queue is idle. + if (I == 0) return true; + + I->Worker = Workers; + I->Owner->Status = pkgAcquire::Item::StatFetching; + return Workers->QueueItem(I); +} + /*}}}*/ +// Queue::Bump - Fetch any pending objects if we are idle /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgAcquire::Queue::Bump() +{ +} + /*}}}*/ - Items->Worker = Workers; - Items->Owner->Status = pkgAcquire::Item::StatFetching; - return Workers->QueueItem(Items); +// AcquireStatus::pkgAcquireStatus - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgAcquireStatus::pkgAcquireStatus() +{ + Start(); +} + /*}}}*/ +// AcquireStatus::Pulse - Called periodically /*{{{*/ +// --------------------------------------------------------------------- +/* This computes some internal state variables for the derived classes to + use. It generates the current downloaded bytes and total bytes to download + as well as the current CPS estimate. */ +void pkgAcquireStatus::Pulse(pkgAcquire *Owner) +{ + TotalBytes = 0; + CurrentBytes = 0; + + // Compute the total number of bytes to fetch + unsigned int Unknown = 0; + unsigned int Count = 0; + for (pkgAcquire::Item **I = Owner->ItemsBegin(); I != Owner->ItemsEnd(); + I++, Count++) + { + TotalBytes += (*I)->FileSize; + if ((*I)->Complete == true) + CurrentBytes += (*I)->FileSize; + if ((*I)->FileSize == 0 && (*I)->Complete == false) + Unknown++; + } + + // Compute the current completion + for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0; + I = Owner->WorkerStep(I)) + if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false) + CurrentBytes += I->CurrentSize; + + // Normalize the figures and account for unknown size downloads + if (TotalBytes <= 0) + TotalBytes = 1; + if (Unknown == Count) + TotalBytes = Unknown; + else + TotalBytes += TotalBytes/(Count - Unknown)*Unknown; + + // Compute the CPS + struct timeval NewTime; + gettimeofday(&NewTime,0); + if (NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec || + NewTime.tv_sec - Time.tv_sec > 6) + { + // Compute the delta time with full accuracy + long usdiff = NewTime.tv_usec - Time.tv_usec; + long sdiff = NewTime.tv_sec - Time.tv_sec; + + // Borrow + if (usdiff < 0) + { + usdiff += 1000000; + sdiff--; + } + + // Compute the CPS value + CurrentCPS = (CurrentBytes - LastBytes)/(sdiff + usdiff/1000000.0); + LastBytes = CurrentBytes; + ElapsedTime = NewTime.tv_sec - StartTime.tv_sec; + Time = NewTime; + } +} + /*}}}*/ +// AcquireStatus::Start - Called when the download is started /*{{{*/ +// --------------------------------------------------------------------- +/* We just reset the counters */ +void pkgAcquireStatus::Start() +{ + gettimeofday(&Time,0); + gettimeofday(&StartTime,0); + LastBytes = 0; + CurrentCPS = 0; + CurrentBytes = 0; + TotalBytes = 0; + FetchedBytes = 0; + ElapsedTime = 0; +} + /*}}}*/ +// pkgAcquireStatus::Stop - Finished downloading /*{{{*/ +// --------------------------------------------------------------------- +/* This accurately computes the elapsed time and the total overall CPS. */ +void pkgAcquireStatus::Stop() +{ + // Compute the CPS and elapsed time + struct timeval NewTime; + gettimeofday(&NewTime,0); + + // Compute the delta time with full accuracy + long usdiff = NewTime.tv_usec - StartTime.tv_usec; + long sdiff = NewTime.tv_sec - StartTime.tv_sec; + + // Borrow + if (usdiff < 0) + { + usdiff += 1000000; + sdiff--; + } + + // Compute the CPS value + CurrentCPS = FetchedBytes/(sdiff + usdiff/1000000.0); + LastBytes = CurrentBytes; + ElapsedTime = sdiff; +} + /*}}}*/ +// AcquireStatus::Fetched - Called when a byte set has been fetched /*{{{*/ +// --------------------------------------------------------------------- +/* This is used to get accurate final transfer rate reporting. */ +void pkgAcquireStatus::Fetched(unsigned long Size,unsigned long Resume) +{ + FetchedBytes += Size - Resume; } /*}}}*/