SQLite Forum

[PATCH] sqlite3 abuses usleep for >=1sec sleeps
Login

[PATCH] sqlite3 abuses usleep for >=1sec sleeps

(1) By anonymous on 2020-09-15 07:04:57 [source]

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, 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, 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) ){