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

WriteThread.cpp

/***************************************************************************
                          WriteThread.cpp
                        -------------------

     Begin        : Web May 6 2009 19:11 alpha_one_x86
     Project      : Ultracopier
     Email        : ultracopier@first-world.info
     Note         : See README for copyright and developer
     Target       : Define the class of the writeThread

****************************************************************************/

#include "WriteThread.h"

WriteThread::WriteThread(QObject * parent) :
      QThread(parent)
{
      //initialise the variable
      theCurrentStat    = WriteThread::Stopped;
      stopIt            = false;
      endOfSource = false;
      theItem.id  = 0;
      movingMode  = false;
      sizeList    = 0;
      CurentCopiedSize= 0;
      copyHadBegin      = false;
      preallocation     = false;
      freeBlock   = new QSemaphore(LISTBLOCKSIZE);
      errorStringDef    = "Unknown error";
      #if (DEBUG_ULTRACOPIER>0)
      debugRcount = 0;
      debugWcount = 0;
      haveBeenStarted   = false;
      #endif
      stopThreadWhenFinish=false;
      //load the translation
      translationErrorResize=tr("The file cannot be resized")+": ";
      translationErrorWriting=tr("Error in writing destination")+": ";
      translationErrorDate=tr("Date cannot be modified!");
      translationErrorRemove=tr("Unable to remove the source file in moving mode")+": ";
}

WriteThread::~WriteThread()
{
      DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::~WriteThread","start");
      stop();
      DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::~WriteThread","check if is running");
      if(isRunning())
      {
            DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::~WriteThread","seam runing, wait");
            wait(15000);
      }
      DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::~WriteThread","stop");
      delete freeBlock;
}

QString WriteThread::errorString()
{
      return errorStringDef;
}

#if (DEBUG_ULTRACOPIER>0)
void WriteThread::setId(int id)
{
      //only in debug mode, set thread id
      this->id          = id;
}
#endif

void WriteThread::setFiles(QString source,QString destination,copyItemInternal theItem)
{
      DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::setFiles","source: "+source+", destination: "+destination);
      //set the copy variable
      this->source.setFileName(source);
      this->destination.setFileName(destination);
      this->theItem           = theItem;
}

bool WriteThread::openDestination()
{
        DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::openDestination","start");
      if(destination.isOpen())
            destination.close();
      //try open file destination
      bool resultToReturn=destination.open(QIODevice::WriteOnly);
      if(resultToReturn)
      {
            //set the status to running, because the thread will be started just after
            theCurrentStat    = WriteThread::Running;
            #if (DEBUG_ULTRACOPIER>0)
            if(!destination.isOpen())
                  DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::openDestination","destination.open("+destination.fileName()+") return true but the file is not open");
            #endif
            //set destination possition
            destination.seek(0);
            waitOneFile.release();
      }
      else
      {
            DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::openDestination","destination.open("+destination.fileName()+") return false");
            errorStringDef=destination.errorString();
      }
      DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::openDestination","destination: \""+destination.fileName()+"\", destination.exists(): "+QString::number(destination.exists())+", destination.isOpen(): "+QString::number(destination.isOpen()));
      needSkipTheCurrentFile=false;
      return resultToReturn;
}

//stop it only in urgence, with cancel
00109 void WriteThread::stop()
{
      /// \note never do the unlock() of mutex here, cause crash because it need by close by the opener thread
      DEBUGCONSOLE(30,"id: "+QString::number(id)+", "+"WriteThread::stop","started!!!");
      //for prevent double call which crash ultracopier, discovered in version 0.2.0.11
      if(stopIt)
            return;
      stopIt                  = true;
      //close directly the source
      if(destination.isOpen())
      {
            destination.close();
            if(destination.exists())
            {
                  destination.remove();
                  DEBUGCONSOLE(50,"WriteThread::stop","try remove destination: "+destination.fileName());
            }
      }
      //release the stream buffer
      freeBlock->release();
      usedBlock.release();
      DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::stop","release wait semaphore");
      //release the mutex for prevent infinity loop by blocking
      while(waitOneAction.available()<=0)
            waitOneAction.release();
      waitOneAction.acquire();
      while(waitOneFile.available()<=0)
            waitOneFile.release();
      waitOneFile.acquire();
}

void WriteThread::addNewBlock(QByteArray newBlock)
{
      //mutex for stream this data
      freeBlock->acquire();
      {
            QMutexLocker lock_mutex(&accessList);
            #if (DEBUG_ULTRACOPIER>0)
            debugRcount+=newBlock.size();
            #endif
            //add this block to list
            theBlockList.append(newBlock);
            CurentCopiedSize+=newBlock.size();
            sizeList++;
      }
      usedBlock.release();
}

/// \brief end of source detected
00158 void WriteThread::endOfSourceDetected()
{
      DEBUGCONSOLE(70,"id: "+QString::number(id)+", "+"WriteThread::endOfSourceDetected","start");
      //set that's end of source is detected, and add empty block for prevent that's thread wait more data
      endOfSource=true;
      addNewBlock(QByteArray());
      DEBUGCONSOLE(70,"id: "+QString::number(id)+", "+"WriteThread::endOfSourceDetected","stop");
}

/// \brief set action on error
00168 void WriteThread::errorAction(int action)
{
      DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","waitOneAction.available(): "+QString::number(waitOneAction.available()));
      DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::fileExistsAction","action: "+QString::number(action));
      actionAfterUnlock=action;
      if(action==ERRORACTION_CLOSE)
            stop();
      else
            waitOneAction.release();
}

/// \brief return the statut
00180 WriteThread::currentStat WriteThread::getTheCurrentStat()
{
      return theCurrentStat;
}

void WriteThread::setKeepDate(bool keepDate)
{
      this->keepDate=keepDate;
}

00190 void WriteThread::setMovingMode(bool movingMode)
{
      this->movingMode=movingMode;
}

void WriteThread::setPreallocation(bool preallocation)
{
      this->preallocation=preallocation;
}

00200 void WriteThread::run()
{
      DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","start");
      QByteArray blockArray;
      //For this file only reset this variable
      qint64 positionInDestination=0,bytesWriten=0;
      do
      {
            DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","enter in wait mode");
            //block in the stopped mode
            theCurrentStat    = WriteThread::Stopped;
                DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","waitOneFile");
            waitOneFile.acquire();
                #if (DEBUG_ULTRACOPIER>0)
            haveBeenStarted=true;
                if(stopIt)
                    DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","stopIt=true");
                else if(stopThreadWhenFinish)
                    DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","stopThreadWhenFinish=true");
                #endif
            //stop this thread for urgence or end of copy
            if(stopIt || stopThreadWhenFinish)
                  break;
            DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","leave wait mode");
            //the destination should be open here
            if(!destination.isOpen() || theItem.id==0)
            {
                  DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::run","bug, destination should by open here");
                  stopIt=true;
                  break;
            }
            //start write the data stream
            theCurrentStat    = WriteThread::Running;
            if(!stopIt && !stopThreadWhenFinish)
            {
                  copyHadBegin      = true;
                  errorStringDef    = "Unknown error";
                  //never uncomment it, it product bug in this case (with semaphore should not have this bug):
                  //if source have finish to read and endOfSourceDetected() is call before this thread have leave wait mode do few on top by controlMutexF.wait(&mutexWaitControlF);
                  //endOfSource     = false;
                  if(preallocation)
                  {
                        DEBUGCONSOLE(90,"copyThread::run","resize destination to "+QString::number(source.size())+"...");
                        retryResizeFirst:
                        if(!destination.resize(source.size()))
                        {
                              actionAfterUnlock=-1;
                                        DEBUGCONSOLE(90,"copyThread::run",destination.fileName()+", cannot be resized: "+destination.errorString()+" at: "+QString::number(source.size()));
                              errorOnFile(ERROR_DEF_ALL,destination.fileName(),translationErrorResize+destination.errorString(),theItem.id);
                              if(stopIt || actionAfterUnlock==ERRORACTION_CLOSE)
                                    goto SkipFile;
                              if(actionAfterUnlock==ERRORACTION_RETRY)
                                    goto retryResizeFirst;
                              else if(actionAfterUnlock==ERRORACTION_SKIP || actionAfterUnlock==ERRORACTION_ENDOF)
                                    goto SkipFile;
                              else
                              {
                                    DEBUGCONSOLE(10,"copyThread::run","Unknow action at set right ("+QString::number(actionAfterUnlock)+")");
                                    goto SkipFile;
                              }
                        }
                        DEBUGCONSOLE(90,"copyThread::run","resize done");
                  }

                  DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","start loop");
                  do
                  {
                        retryUsedBlock:
                        usedBlock.acquire();
                        //read one block
                        if(!theBlockList.size())
                              goto retryUsedBlock;
                        else
                        {
                              QMutexLocker lock_mutex(&accessList);
                              blockArray=theBlockList.first();
                              theBlockList.removeFirst();
                              sizeList--;
                              #if (DEBUG_ULTRACOPIER>0)
                              debugWcount+=blockArray.size();
                              #endif
                        }
                        //write one block
                        freeBlock->release();
                        if(!stopIt && !blockArray.isEmpty())
                        {
                              positionInDestination=destination.pos();
                              retryWriteThisBlock:
                              bytesWriten=destination.write(blockArray);
                              if((destination.error()!=QFile::NoError || bytesWriten!=blockArray.size()) && !stopIt)
                              {
                                    retrySeekDestination:
                                    actionAfterUnlock=-1;
                                    DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","Error in writing: "+destination.errorString()+" and destination->error(): "+QString::number((int)destination.error()));
                                    DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","bytesWriten("+QString::number(bytesWriten)+")!=blockArray.size("+QString::number(blockArray.size())+")");
                                    errorOnFile(ERROR_DEF_ALL,destination.fileName(),translationErrorWriting+destination.errorString(),theItem.id);
                                    if(stopIt)
                                          goto SkipFile;
                                    if(actionAfterUnlock==ERRORACTION_RETRY)
                                    {
                                          if(!destination.seek(positionInDestination))
                                                goto retrySeekDestination;
                                          goto retryWriteThisBlock;
                                    }
                                    else if(actionAfterUnlock==ERRORACTION_SKIP || actionAfterUnlock==ERRORACTION_ENDOF)
                                          goto SkipFile;
                                    else if(actionAfterUnlock==ERRORACTION_CLOSE)
                                          goto SkipFile;
                                    else
                                    {
                                          DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::run","Unknow action at destination write error ("+QString::number(actionAfterUnlock)+")");
                                          goto SkipFile;
                                    }
                              }
                        }
                  }
                  while(!blockArray.isEmpty() && bytesWriten==blockArray.size() && !stopIt && (sizeList || !endOfSource));
                  DEBUGCONSOLE(70,"id: "+QString::number(id)+", "+"WriteThread::run","blockArray.isEmpty(): "+QString::number(blockArray.isEmpty()));
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","bytesWriten: "+QString::number(bytesWriten));
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","blockArray.size(): "+QString::number(blockArray.size()));
                  DEBUGCONSOLE(70,"id: "+QString::number(id)+", "+"WriteThread::run","stopIt: "+QString::number(stopIt));
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","theBlockList.size(): "+QString::number(theBlockList.size()));
                  DEBUGCONSOLE(70,"id: "+QString::number(id)+", "+"WriteThread::run","endOfSource: "+QString::number(endOfSource));
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","debugRcount: "+QString::number(debugRcount));
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","debugWcount: "+QString::number(debugWcount));
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","CurentCopiedSize: "+QString::number(CurentCopiedSize));

                  if(!stopIt)
                  {
                        #if (DEBUG_ULTRACOPIER>0)
                        if(endOfSource)
                              DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","End of source detected: usedBlock->acquire()");
                        else
                              DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::run","End of source not detected and no stopIt but finish!");
                        #endif
                        DEBUGCONSOLE(90,"copyThread::run","resize destination to "+QString::number(source.size())+"...");
                        retryResizeLast:
                        if(!destination.resize(CurentCopiedSize))
                        {
                              actionAfterUnlock=-1;
                                        DEBUGCONSOLE(90,"copyThread::run","destination file exist: "+QString::number(destination.exists())+", is writable: "+QString::number(destination.isWritable())+", is open: "+QString::number(destination.isOpen()));
                                        DEBUGCONSOLE(90,"copyThread::run",destination.fileName()+", cannot be resized: "+destination.errorString()+" at: "+QString::number(CurentCopiedSize));
                              errorOnFile(ERROR_DEF_ALL,destination.fileName(),translationErrorResize+destination.errorString(),theItem.id);
                              if(stopIt || actionAfterUnlock==ERRORACTION_CLOSE)
                                    goto SkipFile;
                              if(actionAfterUnlock==ERRORACTION_RETRY)
                                    goto retryResizeLast;
                              else if(actionAfterUnlock==ERRORACTION_SKIP || actionAfterUnlock==ERRORACTION_ENDOF)
                                    goto SkipFile;
                              else
                              {
                                    DEBUGCONSOLE(10,"copyThread::run","Unknow action at set right ("+QString::number(actionAfterUnlock)+")");
                                    goto SkipFile;
                              }
                        }
                        DEBUGCONSOLE(90,"copyThread::run","resizing done");
                  }

                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","closing source done");
                  /// \warning crash here, because the var is destructed by other thread
                  if(destination.isOpen())
                        destination.close();
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","closing destination done");
      
                  //set the time if no write thread used
                  if(keepDate)
                  {
                        retrySetDate:
                        DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","set date...");
                        if(!changeFileDateTime(destination.fileName(),source.fileName()))
                        {
                              actionAfterUnlock=-1;
                              DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","Date cannot be modified!");
                              errorOnFile(ERROR_DEF_ALL,source.fileName(),translationErrorDate,theItem.id);
                              if(stopIt)
                                    goto SkipFile;
                              if(actionAfterUnlock==ERRORACTION_RETRY)
                                    goto retrySetDate;
                              else if(actionAfterUnlock==ERRORACTION_SKIP || actionAfterUnlock==ERRORACTION_ENDOF)
                                    goto SkipFile;
                              else if(actionAfterUnlock==ERRORACTION_CLOSE)
                                    goto SkipFile;
                              else
                              {
                                    DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::run","Unknow action at change file time ("+QString::number(actionAfterUnlock)+")");
                                    goto SkipFile;
                              }
                        }
                        DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","set date done");
                  }
      
                  //if query to stop, directly go to SkipFile
                  if(stopIt)
                        goto SkipFile;
      
                  //remove source in moving mode
                  if(movingMode)
                  {
                        DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","try remove source...");
                        if(destination.exists())
                        {
                              if(!source.remove())
                              {
                                    actionAfterUnlock=-1;
                                    DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::run","source.errorString():\""+source.errorString()+"\" while removing the source");
                                    errorOnFile(ERROR_DEF_ALL,source.fileName(),translationErrorRemove+source.errorString(),theItem.id);
                                    if(stopIt)
                                          goto SkipFile;
                                    if(actionAfterUnlock==ERRORACTION_RETRY)
                                          goto retrySetDate;
                                    else if(actionAfterUnlock==ERRORACTION_SKIP || actionAfterUnlock==ERRORACTION_ENDOF)
                                          goto SkipFile;
                                    else if(actionAfterUnlock==ERRORACTION_CLOSE)
                                          goto SkipFile;
                                    else
                                    {
                                          DEBUGCONSOLE(10,"id: "+QString::number(id)+", "+"WriteThread::run","Unknow action at remove source file ("+QString::number(actionAfterUnlock)+")");
                                          goto SkipFile;
                                    }
                              }
                        }
                        else
                        {
                              DEBUGCONSOLE(10,"copyThread::run","try remove source but destination not exists!");
                        }
                        DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","try remove source done");
                  }
      
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","now is in SkipFile point!");
                  SkipFile:

                  //close file
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","closing destination...");
                  if(destination.isOpen())
                        destination.close();
                  DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::run","closing destination done");
      
                  if((actionAfterUnlock==ERRORACTION_SKIP || actionAfterUnlock==ERRORACTION_ENDOF || actionAfterUnlock==ERRORACTION_CLOSE || stopIt) && destination.exists() && source.exists() && copyHadBegin)
                  {
                        DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","try remove destination: "+destination.fileName());
                        destination.remove();
                  }
                  if(needSkipTheCurrentFile)
                        stopIt=false;
                  source.setFileName("");
                  destination.setFileName("");
                  theItem.id  = 0;
                  copyHadBegin      = false;

                        //reset all the var by default stat
                        CurentCopiedSize=0;
                        #if (DEBUG_ULTRACOPIER>0)
                        debugRcount = 0;
                        debugWcount = 0;
                        #endif
                        DEBUGCONSOLE(50,"id: "+QString::number(id)+", "+"WriteThread::run","endOfSource=false");
                        endOfSource = false;
                        theBlockList.clear();
                        usedBlock.acquire(usedBlock.available());
                        while(freeBlock->available()>LISTBLOCKSIZE)
                                freeBlock->acquire();
                        while(freeBlock->available()<LISTBLOCKSIZE)
                                freeBlock->release();

                  emit haveFinishFileOperation();
            }
      } while(!stopIt && !stopThreadWhenFinish);
      theCurrentStat    = WriteThread::Stopped;
      DEBUGCONSOLE(30,"id: "+QString::number(id)+", "+"WriteThread::run","stop the loop! And destroy the object!");
}

void WriteThread::stopWhenIsFinish(bool stopThreadWhenFinish)
{
        DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::stopWhenIsFinish","start");
        this->stopThreadWhenFinish=stopThreadWhenFinish;
      addNewBlock(QByteArray());
}

/// \brief error on file or folder, bouton enable, file path, error message
00479 void WriteThread::errorOnFile(int error_code,QString error_file,QString error_string,quint64 error_itemId)
{
      theCurrentError.error_code=error_code;
      theCurrentError.error_file=error_file;
      theCurrentError.error_string=error_string;
      theCurrentError.error_itemId=error_itemId;
      theCurrentStat=WriteThread::PausedInError;
            waitOneAction.acquire();
      theCurrentStat=WriteThread::Running;
}

/// \brief get the current error
00491 writeThreadError WriteThread::getTheCurrentError()
{
      return theCurrentError;
}

void WriteThread::skipTheCurrentFile()
{
        if(theCurrentStat!=WriteThread::Stopped)
        {
                DEBUGCONSOLE(90,"id: "+QString::number(id)+", "+"WriteThread::skipTheCurrentFile","start");
                needSkipTheCurrentFile=true;
                stopIt=true;
        }
}

/// \brief get the current copyItemInternal item
00507 copyItemInternal WriteThread::getCopyItemInternal()
{
      return theItem;
}




Generated by  Doxygen 1.6.0   Back to index