SQLite Forum

[PATCH] sqlite3 abuses usleep for >=1sec sleeps
Login
In src/os_unix.c, `unixSleep` is defined in terms of `usleep` on platforms that support it, by passing the number of microseconds through verbatim.  This is used to implement `sqlite3_sleep` on Unix, via the VFS `xSleep` method.

There is no limit on duration in the [`sqlite3_sleep` documentation](https://sqlite.org/c3ref/sleep.html), other than the limit of `INT_MAX/1000` milliseconds implied by the argument type and the `1000*ms` logic in `sqlite3_sleep`.  In practice, `fossil backoffice` sometimes uses `sqlite3_sleep` in an attempt to sleep for up to a minute.

But in the [specification of `usleep`](https://pubs.opengroup.org/onlinepubs/009695399/functions/usleep.html), POSIX says that the number of microseconds shall be less than one million, and that `usleep` may fail with `EINVAL` otherwise.  If this happens, `fossil backoffice` spins in a busy-wait rather than actually sleeping, with the side effect that the Fossil sqlite3 database is constantly locked, touched, fsynced, and unlocked by the backoffice process, starving out other writers.

The attached patch fixes `unixSleep` by dividing the number of microseconds into a number of seconds, passed to `sleep`, and a remainder of microseconds, passed to `usleep`.  The patch also fixes the one other call to `usleep` with more than one million microseconds by using `sleep` instead.

```
Index: src/os_unix.c
==================================================================
--- src/os_unix.c
+++ src/os_unix.c
@@ -6574,11 +6574,14 @@
   sp.tv_nsec = (microseconds % 1000000) * 1000;
   nanosleep(&sp, NULL);
   UNUSED_PARAMETER(NotUsed);
   return microseconds;
 #elif defined(HAVE_USLEEP) && HAVE_USLEEP
-  usleep(microseconds);
+  if( microseconds >= 1000000 ){
+    sleep(microseconds / 1000000);
+  }
+  usleep(microseconds % 1000000);
   UNUSED_PARAMETER(NotUsed);
   return microseconds;
 #else
   int seconds = (microseconds+999999)/1000000;
   sleep(seconds);
@@ -7173,11 +7176,11 @@
           }
         }else{
           /* don't break the lock on short read or a version mismatch */
           return SQLITE_BUSY;
         }
-        usleep(10000000); /* wait 10 sec and try the lock again */
+        sleep(10); /* wait 10 sec and try the lock again */
         continue; 
       }
       
       assert( nTries==3 );
       if( 0==proxyBreakConchLock(pFile, myHostID) ){

```