Skip to content

Commit

Permalink
Fix network file saving hanging after disconnection
Browse files Browse the repository at this point in the history
  • Loading branch information
donho committed Oct 5, 2024
1 parent a3535f3 commit 0625cd4
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
57 changes: 57 additions & 0 deletions PowerEditor/src/MISC/Common/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1940,3 +1940,60 @@ DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_D

return data._result;
}


//----------------------------------------------------

struct CreateFileParamResult
{
wstring _filePath;
HANDLE _hFile = INVALID_HANDLE_VALUE;
DWORD _accessParam = GENERIC_READ | GENERIC_WRITE;
DWORD _shareParam = FILE_SHARE_READ | FILE_SHARE_WRITE;
DWORD _dispParam = CREATE_ALWAYS;
DWORD _attribParam = FILE_ATTRIBUTE_NORMAL;
bool _isNetworkFailure = true;
CreateFileParamResult(wstring filePath, DWORD accessParam, DWORD shareParam, DWORD dispParam, DWORD attribParam) :
_filePath(filePath), _accessParam(accessParam), _shareParam(shareParam), _dispParam(dispParam), _attribParam(attribParam) {};
};

DWORD WINAPI createFileWorker(void* data)
{
CreateFileParamResult* inAndOut = static_cast<CreateFileParamResult*>(data);
inAndOut->_hFile = ::CreateFileW(inAndOut->_filePath.c_str(), inAndOut->_accessParam, inAndOut->_shareParam, NULL, inAndOut->_dispParam, inAndOut->_attribParam, NULL);
inAndOut->_isNetworkFailure = false;
return ERROR_SUCCESS;
};

HANDLE createFileWaitSec(const wchar_t* filePath, DWORD accessParam, DWORD shareParam, DWORD dispParam, DWORD attribParam, DWORD milliSec2wait, bool* isNetWorkProblem)
{
CreateFileParamResult data(filePath, accessParam, shareParam, dispParam, attribParam);

HANDLE hThread = ::CreateThread(NULL, 0, createFileWorker, &data, 0, NULL);
if (!hThread)
{
return FALSE;
}

// wait for our worker thread to complete or terminate it when the required timeout has elapsed
DWORD dwWaitStatus = ::WaitForSingleObject(hThread, milliSec2wait == 0 ? DEFAULT_MILLISEC : milliSec2wait);
switch (dwWaitStatus)
{
case WAIT_OBJECT_0: // Ok, the state of our worker thread is signaled, so it finished itself in the timeout given
// - nothing else to do here, except the thread handle closing later
break;

case WAIT_TIMEOUT: // the timeout interval elapsed, but the worker's state is still non-signaled
default: // any other dwWaitStatus is a BAD one here
// WAIT_FAILED or WAIT_ABANDONED
::TerminateThread(hThread, dwWaitStatus);
break;
}
CloseHandle(hThread);

if (isNetWorkProblem != nullptr)
*isNetWorkProblem = data._isNetworkFailure;

return data._hFile;
}

1 change: 1 addition & 0 deletions PowerEditor/src/MISC/Common/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,4 @@ bool doesPathExist(const wchar_t* path, DWORD milliSec2wait = 0, bool* isNetWork

DWORD getDiskFreeSpaceWaitSec(const wchar_t* dirPath, ULARGE_INTEGER* freeBytesForUser, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr);
DWORD getFileAttributesExWaitSec(const wchar_t* filePath, WIN32_FILE_ATTRIBUTE_DATA* fileAttr, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr);
HANDLE createFileWaitSec(const wchar_t* filePath, DWORD accessParam, DWORD shareParam, DWORD dispParam, DWORD attribParam, DWORD milliSec2wait = 0, bool* isNetWorkProblem = nullptr);
23 changes: 20 additions & 3 deletions PowerEditor/src/MISC/Common/FileInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,41 @@ Win32_IO_File::Win32_IO_File(const wchar_t *fname)
FindClose(hFind);
}
}

wstring fnW = fname;
string fnA = wstring2string(fnW, CP_ACP);
string msg = "BEFORE createFileWaitSec : 1 ";
msg += fnA;
writeLog(L"c:\\tmp\\AAAAAAA", msg.c_str());
_hFile = ::CreateFileW(fname, _accessParam, _shareParam, NULL, dispParam, _attribParam, NULL); // No thread (CreateFileWaitSec) due to the lock guard in the caller which leads crash

//_hFile = createFileWaitSec(fname, _accessParam, _shareParam, dispParam, _attribParam); // No thread (CreateFileWaitSec) due to the lock guard in the caller which leads crash
msg = "AFTER createFileWaitSec : 1 ";
msg += fnA;
writeLog(L"c:\\tmp\\AAAAAAA", msg.c_str());
// Race condition management:
// If file didn't exist while calling PathFileExistsW, but before calling CreateFileW, file is created: use CREATE_ALWAYS is OK
// If file did exist while calling PathFileExistsW, but before calling CreateFileW, file is deleted: use TRUNCATE_EXISTING will cause the error
if (dispParam == TRUNCATE_EXISTING && _hFile == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_FILE_NOT_FOUND)
{
msg = "BEFORE createFileWaitSec : 2 ";
msg += fnA;
writeLog(L"c:\\tmp\\AAAAAAA", msg.c_str());
dispParam = CREATE_ALWAYS;
_hFile = ::CreateFileW(fname, _accessParam, _shareParam, NULL, dispParam, _attribParam, NULL);
//_hFile = createFileWaitSec(fname, _accessParam, _shareParam, dispParam, _attribParam);
msg = "AFTER createFileWaitSec : 2 ";
msg += fnA;
writeLog(L"c:\\tmp\\AAAAAAA", msg.c_str());
}

writeLog(L"c:\\tmp\\AAAAAAA", "3");
if (fileExists && (dispParam == CREATE_ALWAYS) && (_hFile != INVALID_HANDLE_VALUE))
{
// restore back the original creation date & attributes
::SetFileTime(_hFile, &(attributes_original.ftCreationTime), NULL, NULL);
::SetFileAttributesW(fname, (_attribParam | attributes_original.dwFileAttributes));
writeLog(L"c:\\tmp\\AAAAAAA", "4");
}

writeLog(L"c:\\tmp\\AAAAAAA", "5");
NppParameters& nppParam = NppParameters::getInstance();
if (nppParam.isEndSessionStarted() && nppParam.doNppLogNulContentCorruptionIssue())
{
Expand All @@ -90,6 +106,7 @@ Win32_IO_File::Win32_IO_File(const wchar_t *fname)
}
writeLog(nppIssueLog.c_str(), msg.c_str());
}
writeLog(L"c:\\tmp\\AAAAAAA", "6");
}
}

Expand Down

0 comments on commit 0625cd4

Please sign in to comment.