Hi,

I've run into lots SQLITE_IOERR returns from insert/update/select operations
on client machines, and traced it down to the windows vfs. This happens for
all kinds of operations, and instead of fixing it at the application layer,
I'm proposing a fix to sqlite.

The winRead/winWrite functions don't retry their operations when the error
is due to a sharing/lock violation, and fail the first time. Doing something
similiar to what is done in winDelete would alleviate most issues caused by
antivirus software.

Attached is a patch to 3.6.23.1 amalgamation that does this retry logic. The
changes to winDelete are to add some additional error codes when a retry is
required.

I've also attached a simple test tool for windows that will lock and unlock
files in a directory, which other users can use to simulate antivirus
behavior. It shouldn't be difficult to use the tool along with aggressive
sqlite test cases that exercise the functions I've patched.

Please review my patch, and let me know what you think.

- Nasron Cheong
28737a28738,28739
> ** Retry if the file seems locked, since its likely that an antivirus
> ** or other program is temporary locking the region of the file.
28738a28741
> #define MX_READ_ATTEMPTS 5
28750a28754
>   DWORD retry_count = MX_READ_ATTEMPTS;
28760,28762c28764,28779
<   if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){
<     pFile->lastErrno = GetLastError();
<     return SQLITE_IOERR_READ;
---
>   do{
>     if( ReadFile(pFile->h, pBuf, amt, &got, 0) ){
>       break;
>     }
>     else{
>       error = GetLastError();
>       if(retry_count && 
>          (error == ERROR_LOCK_VIOLATION ||
>           error == ERROR_SHARING_VIOLATION)){
>         Sleep(100);  /* Wait a little before trying again */
>       }
>       else{
>         pFile->lastErrno = error;
>         return SQLITE_IOERR_READ;
>       }
>     }
28763a28781
>   while(retry_count--);
28775a28794,28795
> ** Retry if the file seems locked, since its likely that an antivirus
> ** or other program is temporary locking the region of the file.
28776a28797
> #define MX_WRITE_ATTEMPTS 5
28788a28810
>   DWORD retry_count = MX_WRITE_ATTEMPTS;
28800,28806c28822,28837
<   while(
<      amt>0
<      && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0
<      && wrote>0
<   ){
<     amt -= wrote;
<     pBuf = &((char*)pBuf)[wrote];
---
>   do{
>     if((rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0){
>       amt -= wrote;
>       pBuf = &((char*)pBuf)[wrote];
>     }
>     else{
>        error = GetLastError();
>        if((error == ERROR_LOCK_VIOLATION ||
>            error == ERROR_SHARING_VIOLATION) &&
>            retry_count--){
>            Sleep(100);  /* Wait a little before trying again */
>        }
>        else{
>          break;
>        }
>     }
28807a28839
>   while(amt>0);
28809c28841
<     pFile->lastErrno = GetLastError();
---
>     pFile->lastErrno = error;
29559c29591,29593
<                || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
---
>                || (((error = GetLastError()) == ERROR_ACCESS_DENIED) || 
>                     error == ERROR_SHARING_BUFFER_EXCEEDED ||  
>                     error == ERROR_LOCK_VIOLATION ))
29571c29605,29607
<                || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
---
>                || (((error = GetLastError()) == ERROR_ACCESS_DENIED) || 
>                     error == ERROR_SHARING_BUFFER_EXCEEDED ||  
>                     error == ERROR_LOCK_VIOLATION ))
//---------------------------------------------------------------------------
// Author: Nasron Cheong
// Date:   April 2010 
//---------------------------------------------------------------------------
#include <stdio.h>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <vector>
#include <limits.h>

//windows is silly sometimes, not defined on some
//versions of windows
#ifndef LLONG_MAX
#define LLONG_MAX     9223372036854775807i64
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

using namespace std;

void printWin32Error()
{
   LPVOID lpMsgBuf;
   DWORD dw = GetLastError(); 
   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL,
      dw,
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR) &lpMsgBuf,
      0, NULL );
   cout << (const char*)lpMsgBuf << endl;
   ::LocalFree(lpMsgBuf);
}

bool getAllFiles(const string& Path, vector<string>& AllFiles)
{
   WIN32_FIND_DATA FindFileData;
   HANDLE hFind = 0;

   hFind = FindFirstFile((Path + "\\*").c_str(), &FindFileData);
   do
   {
      if (hFind != INVALID_HANDLE_VALUE)
      {
         string ThisFile = FindFileData.cFileName;
         string NextPath = Path + "\\" + FindFileData.cFileName;
         if (ThisFile != "." && ThisFile != "..")
         {
            if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
               if (!getAllFiles(NextPath, AllFiles))
               {
                  ::FindClose(hFind);
                  return false;
               }
            }
            else
            {
               AllFiles.push_back(NextPath);
            }
         }
      }
      else
      {
         return false;
      }
   }
   while (FindNextFile(hFind, &FindFileData) != 0);
   ::FindClose(hFind);
   return true;
}

void closeHandles(vector<HANDLE>& Handles)
{
   for (vector<HANDLE>::iterator Iter = Handles.begin(); Iter != Handles.end(); 
Iter++)
   {
      ::CloseHandle(*Iter);
   }
}

HANDLE lockFile(const char* pFileName)
{
   HANDLE FileHandle = ::CreateFile(pFileName,GENERIC_READ, FILE_SHARE_READ | 
FILE_SHARE_WRITE,0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
   if (FileHandle != INVALID_HANDLE_VALUE)
   { 
      LARGE_INTEGER MaxLargeIndex;
      MaxLargeIndex.QuadPart = LLONG_MAX;
      if (!::LockFile(FileHandle,0, 0, MaxLargeIndex.LowPart, 
MaxLargeIndex.HighPart))
      {
         ::CloseHandle(FileHandle);
         FileHandle = INVALID_HANDLE_VALUE;
      }
   }
   return FileHandle;
}

int main(int argv,const char** argc)
{
   if (argv < 3)
   {
      cout << "Usage: file_locker DelayUntilLockMs HoldLockTimeMs" << endl;
      return -1;
   }

   int DelayUntilLockMs = atoi(argc[1]);
   int HoldLockTimeMs = atoi(argc[2]);
   for(;;)
   {
      cout << endl;
      vector<string> AllFiles;
      if (!getAllFiles(".",AllFiles))
      {
         printWin32Error();
      }
      else
      {
         vector<HANDLE> LockedFiles;
         for (vector<string>::iterator Iter = AllFiles.begin(); Iter != 
AllFiles.end(); Iter++)
         {
            HANDLE FileHandle = lockFile(Iter->c_str());
            bool Locked = false;
            if (FileHandle != INVALID_HANDLE_VALUE)
            {
               Locked = true;
               LockedFiles.push_back(FileHandle);
            }
            cout << *Iter << " locked : " << (Locked ? "yes" : "no") << endl;
         }
         cout << "Holding Files for " << HoldLockTimeMs << ". Press 'ctrl-c' to 
quit...";
         ::Sleep(HoldLockTimeMs);
         closeHandles(LockedFiles);
         cout << "Unlocked." << endl;
      }
      cout << "Waiting for " << DelayUntilLockMs << ". Press 'ctrl-c' to quit." 
<< endl;
      ::Sleep(DelayUntilLockMs);
   }
   return 0;
}
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to