[TEST] sqlite3-3.7.17-1 (Cygwin 1.7.19 locking feature)

Corinna Vinschen corinna-cygwin@cygwin.com
Thu Jun 6 17:22:00 GMT 2013

On Jun  5 14:06, Warren Young wrote:
> On 6/4/2013 03:37, Corinna Vinschen wrote:
> >
> >It would be nice to have a simple testcase (plain C, only Cygwin
> >POSIX calls, self-contained, yada yada) to see what sqlite expects in
> >POSIX lock mode.
> SQLite locking is a hairball.  It is spread over a range of about
> 2.5 kLOC within the 140 kLOC sqlite3.c file, and those routines are
> behind about three layers of indirection from the mainline SQLite
> code.
> Mind, I'm not talking about the *callers* of this code here, just
> the locking routines themselves.  Add in all the callers and *their*
> expectations and...well...<shudder>
> I think we have to define the problem as "SQLite locking requires
> what SQLite requires today."
> Luckily, while creating the -2 packages, I stumbled across a STC in
> SQL that replicates the reported problem:
> $ ./sqlite3 foo.db 'create table fred(id integer, barney text)'
> $ CYGWIN_SQLITE_LOCKING=posixmand ./sqlite3 foo.db \
>   'insert into fred(barney) values("wilma")'

So, here's what happens.  The below list contains the lock requests
of sqlite up to the point where it fails.  "r" is a read lock request,
"w" a write lock request, "u" an unlock request.  The next two values
are the byte offset and byte length of the lock, the last is the
status code after calling the Windows Lock/Unlock function:

  A.  r 1073741824 1    STATUS_SUCCESS
  B.  r 1073741826 510  STATUS_SUCCESS
  C.  u 1073741824 1    STATUS_SUCCESS
  E.  r 1073741824 1    STATUS_SUCCESS
  F.  r 1073741826 510  STATUS_SUCCESS
  G.  u 1073741824 1    STATUS_SUCCESS
  H.  w 1073741825 1    STATUS_SUCCESS
  I.  w 1073741824 1    STATUS_SUCCESS
  J.  w 1073741826 510  STATUS_LOCK_NOT_GRANTED
  K.  r 1073741826 510  STATUS_SUCCESS

There are two problems:

The lazy unlock request D tells the system to unlock all locks on the
entire file.  This works fine with POSIX locks, but it does not work
with Windows locks.  These require to unlock a lock exactly as it has
been created.  This means, after this unlock, the "r 1073741826 510"
lock is still present.  Since a failed unlock practically doesn't exist
so fcntl returns 0, success.  Same for L and M, with L failing
because the call tries to use a single unlock on a range holding two
locks.  The locks would have to be unlocked each by itself.

Ultimately the write lock request J fails, because there are already
the read locks B and F present.  POSIX and BSD locks can simply
replace another lock in the same spot if the existing lock is hold
by the same process (POSIX) or file object (BSD).  Windows locks
add up, and require to unlock all locks in the same area before
allowing another lock type to be placed.  No atomic replacement of an
existing read lock with a write lock or vice versa.

In theory this scenario could be worked around in Cygwin by bookkeeping
the present locks, plus a piece of code which unlocks all existing locks
in the given range when a lock or unlock request is coming in.  However,
the really dismal fact is, that an unlock before a lock would never be
atomic.  If the F_SETLK request unlocks an existing lock and then
another process gets a lock in the requested range, the first process
ends up with a failed fcntl call and no lock at all.


Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

More information about the Cygwin mailing list