I am multithreading my Qt data backup program with all file manipulation logic offloaded to a worker object that has been moved to a worker thread. I am wondering if it is safe to use CopyFileEx to do the file copying within the worker thread.
The one most relevant detail I'm not sure about is that I pass the CopyFileEx call a callback function as the lpProgressRoutine parameter. This callback function is a static DWORD CALLBACK member of the worker object. Is this safe and avoids race conditions even though the callback is static? The QThread::currentThreadId() call within the callback reports the same thread as the worker thread.
Also I pass the worker object as this to the lpData parameter so I can access worker object member functions from within the callback function which connect to the main gui thread via signals/slots to update the gui on copy progress.
Anything else worth noting? Here is the code:
//code within main gui thread to create worker object and move to worker thread
workerThread = new QThread(this);
workerThread->start();
worker = new Worker;
worker->moveToThread(workerThread);
//here is the CopeFileEx call for copying files in a worker object method
if (!CopyFileEx(sourceFile.fileName().toStdWString().c_str(), targetTempFile.fileName().toStdWString().c_str(), ©Progress, this, NULL, 0))
win32Error(TEXT("CopyFileEx"));
//here is the declaration of the static DWORD CALLBACK function to pass as a callback to CopyFileEx
class Worker : public QObject
{
Q_OBJECT
public:
static DWORD CALLBACK copyProgress(
LARGE_INTEGER totalSize, LARGE_INTEGER totalTransferred,
LARGE_INTEGER streamSize, LARGE_INTEGER streamTransferred,
DWORD streamNo, DWORD callbackReason, HANDLE src, HANDLE dst,
LPVOID data);
//here is the callback function implementation
DWORD Worker::copyProgress(LARGE_INTEGER totalSize, LARGE_INTEGER totalTransferred, LARGE_INTEGER, LARGE_INTEGER, DWORD, DWORD, HANDLE, HANDLE, LPVOID data)
{
qDebug() << "copyprogress" << QThread::currentThreadId() << "transferred" << totalTransferred.QuadPart;
Worker *worker = static_cast<Worker *>(data);
if (worker->getStop())
return PROGRESS_CANCEL;
qint64 readSize = totalTransferred.QuadPart - worker->prevBytesTransferred;
worker->prevBytesTransferred = totalTransferred.QuadPart;
worker->setProgressMeters(readSize, totalSize.QuadPart);
worker->setTimeLabels(readSize);
return PROGRESS_CONTINUE;
}