Logo Search packages:      
Sourcecode: qgit version File versions  Download package

dataloader.cpp

/*
      Description: async stream reader, used to load repository data on startup

      Author: Marco Costalba (C) 2005-2006

      Copyright: See COPYING file that comes with this distribution

*/
#include "git.h"
#include "dataloader.h"

#define  GUI_UPDATE_INTERVAL 500
#define  BUF_RING_SIZE       50000

DataLoader::DataLoader(Git* g, FileHistory* f)
           : QObject((QObject*)g), git(g), fh(f) {

      canceling = false;

      buffersRing.setAutoDelete(false); // manual memory management
      buffersRing.resize(BUF_RING_SIZE);

      connect(git, SIGNAL(cancelAllProcesses()), this, SLOT(on_cancel()));
      connect(&proc, SIGNAL(readyReadStdout()), this, SLOT(on_readyReadStdout()));
      connect(&proc, SIGNAL(processExited()), this, SLOT(on_processExited()));
      connect(&guiUpdateTimer, SIGNAL(timeout()), this, SLOT(on_timeout()));
}

DataLoader::~DataLoader() {

      git->decRunningProcesses();
}

void DataLoader::on_cancel() {

      canceling = true;
      proc.tryTerminate();
}

void DataLoader::on_cancel(const FileHistory* f) {

      if (f == fh)
            on_cancel();
}

bool DataLoader::start(SCRef cmd, SCRef wd) {

      buffersRingHead = buffersRingTail = grandTot = 0;
      isProcExited = parsing = false;
      halfChunks = "";

      proc.setArguments(QStringList::split(' ', cmd));
      proc.setWorkingDirectory(wd);

      loadTime.start();

      if (!proc.start()) {
            deleteLater();
            return false;
      }
      git->incRunningProcesses();
      guiUpdateTimer.start(GUI_UPDATE_INTERVAL, true);
      return true;
}

void DataLoader::on_readyReadStdout() {

      if (isProcExited)
            dbs("ASSERT in DataLoader: readFromStdout() called after exit");

      // we use a circular buffer to store data chunks from loading process
      QByteArray* b = new QByteArray(proc.readStdout()); // copy c'tor uses shallow copy
      buffersRing.insert(buffersRingHead, b);

      if (++buffersRingHead == BUF_RING_SIZE)
            buffersRingHead = 0;
}

void DataLoader::on_processExited() {

      isProcExited = true;

      if (parsing && guiUpdateTimer.isActive())
            dbs("ASSERT in DataLoader: timer active while parsing");

      if (parsing == guiUpdateTimer.isActive())
            dbs("ASSERT in DataLoader: inconsistent timer");

      if (guiUpdateTimer.isActive()) // no need to wait anymore
            guiUpdateTimer.start(1, true);
}

void DataLoader::on_timeout() {

      parsing = true;
      // process could exit while we are processing so save the flag now
      bool lastBuffer = isProcExited;

      while (buffersRingTail != buffersRingHead) {

            const QByteArray* ba(buffersRing.at(buffersRingTail));
            parseSingleBuffer(*ba); // about 25% of total time
            grandTot += ba->size();
            delete ba;

            if (++buffersRingTail == BUF_RING_SIZE)
                  buffersRingTail = 0;
      }
      if (!canceling)
            emit newDataReady(fh); // inserting in list view is about 5% of total time

      if (lastBuffer) {
            const QString ds(proc.readStderr());
            const QString cmd(proc.arguments().join(" "));
            if (!canceling)
                  emit loaded(fh, grandTot, loadTime.elapsed(),
                              proc.normalExit(), cmd, ds);
            deleteLater();
      } else
            guiUpdateTimer.start(GUI_UPDATE_INTERVAL, true);

      parsing = false;
}

void DataLoader::updateIndex(int* idx, int* prevIdx, const QByteArray& ba) {

      *prevIdx = *idx + 1;
      *idx = (*prevIdx < (int)ba.size()) ? ba.find(0, *prevIdx) : -1;
}

void DataLoader::parseSingleBuffer(const QByteArray& ba) {

      if (ba.size() == 0 || canceling)
            return;

      int prevIdx, idx = -1;
      updateIndex(&idx, &prevIdx, ba);

      while (idx != -1) {

            // deep copy here by fromAscii()
            const QString& chunk(QString::fromAscii(&(ba[prevIdx]), idx - prevIdx));

            if (halfChunks.isEmpty())
                  git->addChunk(fh, chunk); // about 20% of total time
            else {
                  // unlikely path (10% of cases), we can accept a detach
                  git->addChunk(fh, halfChunks.append(chunk));
                  halfChunks = "";
            }
            updateIndex(&idx, &prevIdx, ba);
      }
      // save any remaining half chunk
      if (prevIdx < (int)ba.size())
            halfChunks.append(QString::fromAscii(&(ba[prevIdx]),  ba.size() - prevIdx));
}

Generated by  Doxygen 1.6.0   Back to index