Attached is a pseudo-locking mechanism I wrote, intending to use it for
Windows CE.  The design goal for the code was to allow the existing os_win.c
locking code to remain intact with zero replacement coding involved.

With that in mind, I basically implemented LockFile(), LockFileEx() and
UnlockFile() using a mutex and some shared global memory.  It is similar in
nature to Nuno Lucas's "idea" behind locking -- however, its completely
re-realized in this code.

The code is designed around SQLite's behavioral use of those API functions
and for testing purposes, does not require Windows CE to run and test.

Please have a look at it and poke some holes in it.

Robert
#define _CRT_SECURE_NO_DEPRECATE

#include <windows.h>

// The following code is copied over from SQLITE so this app can test itself
#ifndef SQLITE_TEST
#define PENDING_BYTE      0x40000000  /* First byte past the 1GB boundary */
#else
extern unsigned int sqlite3_pending_byte;
#define PENDING_BYTE sqlite3_pending_byte
#endif

#define RESERVED_BYTE     (PENDING_BYTE+1)
#define SHARED_FIRST      (PENDING_BYTE+2)
#define SHARED_SIZE       510

typedef struct _LOCKDATA
{
  long nReaders;
  long nPending;
  long nReserved;
  long nExclusive;
} LOCKDATA;

typedef struct _LOCKSTRUCT
{
  HANDLE hFile;
  HANDLE hMutex;
  HANDLE hShared;
  LOCKDATA local;
  LOCKDATA *shared;
} LOCKSTRUCT;

// In live mode, the "x" prefix will be removed for Windows CE, and these 
macros will allow the main os_win.c code to implement locking
// without any modification
#define xLockFile(a, b, c, d, e) pseudoLockFile(&a, b, c, d, e)
#define xUnlockFile(a, b, c, d, e) pseudoUnlockFile(&a, b, c, d, e)
#define xLockFileEx(a, b, c, d, e, f) pseudoLockFileEx(&a, b, c, d, e, f)

#define HANDLE_TO_LOCKSTRUCT(a) (LOCKSTRUCT 
*)&((LPBYTE)a)[-PtrToInt((&((LOCKSTRUCT *)0)->hFile))]

#define MUTEX_ACQUIRE(h) { DWORD dwErr; do { dwErr = WaitForSingleObject(h, 
INFINITE); } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); }
#define MUTEX_RELEASE(h) ReleaseMutex(h)

BOOL CreateLockStruct(WCHAR *pszFilename, LOCKSTRUCT *pLocks)
{
  WCHAR szName[MAX_PATH];
  WCHAR *pszTok;
  BOOL bInit = FALSE;

  // Initialize the local lockdata
  ZeroMemory(&pLocks->local, sizeof(LOCKDATA));

  // Create a unique global name for the mutex and subsequently the shared 
memory
  wcscpy(szName, L"sqliteL");
  wcscat(szName, pszFilename);
  while (pszTok = wcschr(szName, '\\'))
  {
    *pszTok = '_';
  }

  // Create/open the named mutex
  pLocks->hMutex = CreateMutexW(NULL, FALSE, szName);
  if (!pLocks->hMutex) return FALSE;

  // Acquire the mutex before continuing
  MUTEX_ACQUIRE(pLocks->hMutex);
  
  // Create/open the shared memory
  wcscat(szName, L"MEM");
  pLocks->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, 
PAGE_READWRITE, 0, sizeof(LOCKDATA), szName);
  
  // Set a flag that indicates we're the first to create the memory so it must 
be zero-initialized
  if (GetLastError() == ERROR_ALREADY_EXISTS)
  {
    bInit = TRUE;
  }

  // If we succeeded in making the shared memory handle, map it.
  if (pLocks->hShared)
  {
    pLocks->shared = (LOCKDATA *)MapViewOfFile(pLocks->hShared, 
FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(LOCKDATA));
    // If mapping failed, close the shared memory handle and erase it
    if (!pLocks->shared)
    {
      CloseHandle(pLocks->hShared);
      pLocks->hShared = NULL;
    }
  }

  // If shared memory could not be created, then close the mutex and fail
  if (pLocks->hShared == NULL)
  {
    MUTEX_RELEASE(pLocks->hMutex);
    CloseHandle(pLocks->hMutex);
    pLocks->hMutex = NULL;
    return FALSE;
  }
  
  // Initialize the shared memory if we're supposed to
  if (bInit)
  {
    ZeroMemory(pLocks->shared, sizeof(LOCKDATA));
  }

  MUTEX_RELEASE(pLocks->hMutex);

  return TRUE;
}

void DestroyLockStruct(LOCKSTRUCT *pLocks)
{
  if (pLocks->hMutex)
  {
    // Acquire the mutex
    MUTEX_ACQUIRE(pLocks->hMutex);

    // The following blocks should probably assert in debug mode, but they
    // are to cleanup in case any locks remained open
    if (pLocks->local.nReaders)
      pLocks->shared->nReaders --;

    if (pLocks->local.nReserved)
    {
      pLocks->shared->nReserved = 0;
    }

    if (pLocks->local.nPending)
    {
      pLocks->shared->nPending = 0;
    }

    if (pLocks->local.nExclusive)
    {
      pLocks->shared->nExclusive = 0;
    }

    // De-reference and close our copy of the shared memory handle
    UnmapViewOfFile(pLocks->shared);
    CloseHandle(pLocks->hShared);

    // Done with the mutex
    MUTEX_RELEASE(pLocks->hMutex);    
    CloseHandle(pLocks->hMutex);
    pLocks->hMutex = NULL;
  }
}

// Custom pseudo file locking support specifically for SQLite
BOOL pseudoLockFile(HANDLE *phFile, DWORD dwFileOffsetLow, DWORD 
dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
{
  LOCKSTRUCT *pls = HANDLE_TO_LOCKSTRUCT(phFile);
  BOOL bReturn = FALSE;

  MUTEX_ACQUIRE(pls->hMutex);

  // Wanting an exclusive lock?
  if (dwFileOffsetLow == SHARED_FIRST && nNumberOfBytesToLockLow == SHARED_SIZE)
  {
    if (pls->shared->nReaders == 0 && pls->shared->nExclusive == 0)
    {
        pls->shared->nExclusive = 1;
        pls->local.nExclusive = 1;
        bReturn = TRUE;
    }
    else if (pls->local.nExclusive) // We already had an exclusive lock
    {
      pls->local.nExclusive ++;
      bReturn = TRUE;
    }
  }
  // Want a read-only lock?
  else if ((dwFileOffsetLow >= SHARED_FIRST && dwFileOffsetLow < SHARED_FIRST + 
SHARED_SIZE) && nNumberOfBytesToLockLow == 1)
  {
    if (pls->shared->nExclusive == 0)
    {
      pls->local.nReaders ++;
      if (pls->local.nReaders == 1)
      {
        pls->shared->nReaders ++;
      }
      bReturn = TRUE;
    }
  }
  // Want a pending lock?
  else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToLockLow == 1)
  {
    // If no pending lock has been acquired, then acquire it
    if (pls->shared->nPending == 0)
    {
      pls->shared->nPending = 1;
      pls->local.nPending = 1;
      bReturn = TRUE;
    }
    else if (pls->local.nPending)
    {
      pls->local.nPending ++;
      bReturn = TRUE; // Already had a pending lock
    }
  }
  // Want a reserved lock?
  else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToLockLow == 1)
  {
    if (pls->shared->nReserved == 0)
    {
      pls->shared->nReserved = 1;
      pls->local.nReserved = 1;
      bReturn = TRUE;
    }
    else if (pls->local.nReserved)
    {
      pls->local.nReserved ++;
      bReturn = TRUE; // Already had a reserved lock
    }
  }

  MUTEX_RELEASE(pls->hMutex);

  return bReturn;
}

BOOL pseudoUnlockFile(HANDLE *phFile, DWORD dwFileOffsetLow, DWORD 
dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD 
nNumberOfBytesToUnlockHigh)
{
  LOCKSTRUCT *pls = HANDLE_TO_LOCKSTRUCT(phFile);
  BOOL bReturn = FALSE;

  MUTEX_ACQUIRE(pls->hMutex);

  // Releasing a reader lock or an exclusive lock
  if (dwFileOffsetLow >= SHARED_FIRST && dwFileOffsetLow < SHARED_FIRST + 
SHARED_SIZE)
  {
    // Did we have an exclusive lock?
    if (pls->local.nExclusive)
    {
      pls->local.nExclusive --;
      if (pls->local.nExclusive == 0)
      {
        pls->shared->nExclusive = 0;
      }
      bReturn = TRUE;
    }
    // Did we just have a reader lock?
    else if (pls->local.nReaders)
    {
      pls->local.nReaders --;
      if (pls->local.nReaders == 0)
      {
        pls->shared->nReaders --;
      }
      bReturn = TRUE;
    }
  }
  // Releasing a pending lock
  else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToUnlockLow == 1)
  {
    if (pls->local.nPending)
    {
      pls->local.nPending --;
      if (pls->local.nPending == 0)
      {
        pls->shared->nPending = 0;
      }
      bReturn = TRUE;
    }
  }
  // Releasing a reserved lock
  else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1)
  {
    if (pls->local.nReserved)
    {
      pls->local.nReserved --;
      if (pls->local.nReserved == 0)
      {
        pls->shared->nReserved = 0;
      }
      bReturn = TRUE;
    }
  }

  MUTEX_RELEASE(pls->hMutex);

  return bReturn;
}

BOOL pseudoLockFileEx(HANDLE *phFile, DWORD dwFlags, DWORD dwReserved, DWORD 
nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED 
lpOverlapped)
{
  // If the caller wants a shared read lock, foward this call to pseudoLockFile
  if (lpOverlapped->Offset == SHARED_FIRST && dwFlags == 1 && 
nNumberOfBytesToLockLow == SHARED_SIZE)
    return pseudoLockFile(phFile, SHARED_FIRST, 0, 1, 0);

  return FALSE;
}

int main()
{
  LOCKSTRUCT ls;
  BOOL res;

  CreateLockStruct(L"foo", &ls);


  res = xLockFile(ls.hFile, SHARED_FIRST, 0, SHARED_SIZE, 0);

  res = xUnlockFile(ls.hFile, SHARED_FIRST, 0, SHARED_SIZE, 0);

  DestroyLockStruct(&ls);

  return 0;
}

Reply via email to