]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Generic minor cleanups and docs in the OPFS async proxy.
authorstephan <stephan@noemail.net>
Fri, 14 Oct 2022 15:52:29 +0000 (15:52 +0000)
committerstephan <stephan@noemail.net>
Fri, 14 Oct 2022 15:52:29 +0000 (15:52 +0000)
FossilOrigin-Name: a4423ca234453c14eb40db7fe5943f63b30fd9dc2207388e8a2966733a004e9d

ext/wasm/sqlite3-opfs-async-proxy.js
manifest
manifest.uuid

index d261dd354c5c52525c55cfe9a09e69ca1da58898..5b29aa3ea50979eccd004ac7e8663d2169c23e3d 100644 (file)
 
   ***********************************************************************
 
-  An INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS: a Worker
-  which manages asynchronous OPFS handles on behalf of a synchronous
-  API which controls it via a combination of Worker messages,
-  SharedArrayBuffer, and Atomics.
+  An Worker which manages asynchronous OPFS handles on behalf of a
+  synchronous API which controls it via a combination of Worker
+  messages, SharedArrayBuffer, and Atomics. It is the asynchronous
+  counterpart of the API defined in sqlite3-api-opfs.js.
 
   Highly indebted to:
 
@@ -102,9 +102,9 @@ const __openFiles = Object.create(null);
 
 /**
    Expects an OPFS file path. It gets resolved, such that ".."
-   components are properly expanded, and returned. If the 2nd
-   are is true, it's returned as an array of path elements,
-   else it's returned as an absolute path string.
+   components are properly expanded, and returned. If the 2nd arg is
+   true, the result is returned as an array of path elements, else an
+   absolute path string is returned.
 */
 const getResolvedPath = function(filename,splitIt){
   const p = new URL(
@@ -115,9 +115,9 @@ const getResolvedPath = function(filename,splitIt){
 
 /**
    Takes the absolute path to a filesystem element. Returns an array
-   of [handleOfContainingDir, filename]. If the 2nd argument is
-   truthy then each directory element leading to the file is created
-   along the way. Throws if any creation or resolution fails.
+   of [handleOfContainingDir, filename]. If the 2nd argument is truthy
+   then each directory element leading to the file is created along
+   the way. Throws if any creation or resolution fails.
 */
 const getDirForFilename = async function f(absFilename, createDirs = false){
   const path = getResolvedPath(absFilename, true);
@@ -171,6 +171,16 @@ const getSyncHandle = async (fh)=>{
   return fh.syncHandle;
 };
 
+/**
+   If the given file-holding object has a sync handle attached to it,
+   that handle is remove and asynchronously closed. Though it may
+   sound sensible to continue work as soon as the close() returns
+   (noting that it's asynchronous), doing so can cause operations
+   performed soon afterwards, e.g. a call to getSyncHandle() to fail
+   because they may happen out of order from the close(). OPFS does
+   not guaranty that the actual order of operations is retained in
+   such cases. i.e.  always "await" on the result of this function.
+*/
 const closeSyncHandle = async (fh)=>{
   if(fh.syncHandle){
     log("Closing sync handle for",fh.filenameAbs);
@@ -197,43 +207,50 @@ const affirmNotRO = function(opName,fh){
   if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs);
 };
 
-
-const opTimer = Object.create(null);
-opTimer.op = undefined;
-opTimer.start = undefined;
+/**
+   We track 2 different timers: the "metrics" timer records how much
+   time we spend performing work. The "wait" timer records how much
+   time we spend waiting on the underlying OPFS timer. See the calls
+   to mTimeStart(), mTimeEnd(), wTimeStart(), and wTimeEnd()
+   throughout this file to see how they're used.
+*/
+const __mTimer = Object.create(null);
+__mTimer.op = undefined;
+__mTimer.start = undefined;
 const mTimeStart = (op)=>{
-  opTimer.start = performance.now();
-  opTimer.op = op;
+  __mTimer.start = performance.now();
+  __mTimer.op = op;
   //metrics[op] || toss("Maintenance required: missing metrics for",op);
   ++metrics[op].count;
 };
 const mTimeEnd = ()=>(
-  metrics[opTimer.op].time += performance.now() - opTimer.start
+  metrics[__mTimer.op].time += performance.now() - __mTimer.start
 );
-const waitTimer = Object.create(null);
-waitTimer.op = undefined;
-waitTimer.start = undefined;
+const __wTimer = Object.create(null);
+__wTimer.op = undefined;
+__wTimer.start = undefined;
 const wTimeStart = (op)=>{
-  waitTimer.start = performance.now();
-  waitTimer.op = op;
+  __wTimer.start = performance.now();
+  __wTimer.op = op;
   //metrics[op] || toss("Maintenance required: missing metrics for",op);
 };
 const wTimeEnd = ()=>(
-  metrics[waitTimer.op].wait += performance.now() - waitTimer.start
+  metrics[__wTimer.op].wait += performance.now() - __wTimer.start
 );
 
 /**
-   Set to true by the 'opfs-async-shutdown' command to quite the wait loop.
-   This is only intended for debugging purposes: we cannot inspect this
-   file's state while the tight waitLoop() is running.
+   Gets set to true by the 'opfs-async-shutdown' command to quit the
+   wait loop. This is only intended for debugging purposes: we cannot
+   inspect this file's state while the tight waitLoop() is running and
+   need a way to stop that loop for introspection purposes.
 */
 let flagAsyncShutdown = false;
 
 
 /**
    Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods
-   methods. Maintenance reminder: members are in alphabetical order
-   to simplify finding them.
+   methods, as well as helpers like mkdir(). Maintenance reminder:
+   members are in alphabetical order to simplify finding them.
 */
 const vfsAsyncImpls = {
   'opfs-async-metrics': async ()=>{
@@ -369,11 +386,13 @@ const vfsAsyncImpls = {
     const fh = __openFiles[fid];
     let rc = 0;
     if( !fh.syncHandle ){
+      wTimeStart('xLock');
       try { await getSyncHandle(fh) }
       catch(e){
         state.s11n.storeException(1,e);
         rc = state.sq3Codes.SQLITE_IOERR;
       }
+      wTimeEnd();
     }
     storeAndNotify('xLock',rc);
     mTimeEnd();
@@ -423,11 +442,11 @@ const vfsAsyncImpls = {
   },
   xRead: async function(fid,n,offset){
     mTimeStart('xRead');
-    let rc = 0;
+    let rc = 0, nRead;
     const fh = __openFiles[fid];
     try{
       wTimeStart('xRead');
-      const nRead = (await getSyncHandle(fh)).read(
+      nRead = (await getSyncHandle(fh)).read(
         fh.sabView.subarray(0, n),
         {at: Number(offset)}
       );
@@ -437,6 +456,7 @@ const vfsAsyncImpls = {
         rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ;
       }
     }catch(e){
+      if(undefined===nRead) wTimeEnd();
       error("xRead() failed",e,fh);
       state.s11n.storeException(1,e);
       rc = state.sq3Codes.SQLITE_IOERR_READ;
@@ -454,9 +474,8 @@ const vfsAsyncImpls = {
         await fh.syncHandle.flush();
       }catch(e){
         state.s11n.storeException(2,e);
-      }finally{
-        wTimeEnd();
       }
+      wTimeEnd();
     }
     storeAndNotify('xSync',rc);
     mTimeEnd();
@@ -484,13 +503,13 @@ const vfsAsyncImpls = {
     const fh = __openFiles[fid];
     if( state.sq3Codes.SQLITE_LOCK_NONE===lockType
         && fh.syncHandle ){
+      wTimeStart('xUnlock');
       try { await closeSyncHandle(fh) }
       catch(e){
         state.s11n.storeException(1,e);
         rc = state.sq3Codes.SQLITE_IOERR;
-        /* Maybe we want to not report this? "Destructors do not
-           throw." */
       }
+      wTimeEnd();
     }
     storeAndNotify('xUnlock',rc);
     mTimeEnd();
@@ -511,9 +530,8 @@ const vfsAsyncImpls = {
       error("xWrite():",e,fh);
       state.s11n.storeException(1,e);
       rc = state.sq3Codes.SQLITE_IOERR_WRITE;
-    }finally{
-      wTimeEnd();
     }
+    wTimeEnd();
     storeAndNotify('xWrite',rc);
     mTimeEnd();
   }
@@ -632,7 +650,7 @@ const waitLoop = async function f(){
     const o = Object.create(null);
     opHandlers[state.opIds[k]] = o;
     o.key = k;
-    o.f = vi || toss("No vfsAsyncImpls[",k,"]");
+    o.f = vi;
   }
   /**
      waitTime is how long (ms) to wait for each Atomics.wait().
@@ -640,8 +658,6 @@ const waitLoop = async function f(){
      to do other things.
   */
   const waitTime = 1000;
-  let lastOpTime = performance.now();
-  let now;
   while(!flagAsyncShutdown){
     try {
       if('timed-out'===Atomics.wait(
@@ -649,7 +665,6 @@ const waitLoop = async function f(){
       )){
         continue;
       }
-      lastOpTime = performance.now();
       const opId = Atomics.load(state.sabOPView, state.opIds.whichOp);
       Atomics.store(state.sabOPView, state.opIds.whichOp, 0);
       const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
@@ -663,7 +678,7 @@ const waitLoop = async function f(){
     }catch(e){
       error('in waitLoop():',e);
     }
-  };
+  }
 };
 
 navigator.storage.getDirectory().then(function(d){
index 5e6a5ac9b8147e17ff6d8202f3da1eb25c396984..259868c45de55574761d8355cd18e33da299b6ae 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\sin\sthe\sLIKE\sand\sGLOB\soperators\sthat\smay\soccur\swhen\sthe\scharacter\simmediately\sfollowing\sa\s"%"\sor\s"*"\swildcard\sis\sU+80.\sReported\sby\s[forum:61bf7ccbdf].
-D 2022-10-14T15:10:36.387
+C Generic\sminor\scleanups\sand\sdocs\sin\sthe\sOPFS\sasync\sproxy.
+D 2022-10-14T15:52:29.231
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -519,7 +519,7 @@ F ext/wasm/speedtest1.html e4cb5d722b494104fc1249e7c008ca018f820a784833c51004c95
 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
 F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
-F ext/wasm/sqlite3-opfs-async-proxy.js e552d9290509644929cd7bfde3381a0eac85d17b4d82dbb11e86fae5336215b6
+F ext/wasm/sqlite3-opfs-async-proxy.js 206ce6bbc3c30ad51a37d9c25e3a2712e70b586e0f9a2cf8cb0b9619017c2671
 F ext/wasm/sqlite3-worker1-promiser.js 307d7837420ca6a9d3780dfc81194f1c0715637e6d9540e935514086b96913d8
 F ext/wasm/sqlite3-worker1.js 466e9bd39409ab03f3e00999887aaffc11e95b416e2689596e3d7f1516673fdf
 F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
@@ -2031,8 +2031,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 32fd4ac382f774189ac34f6fff80e55e6e56dd2aa67b0db88d5a88324f17f6ff
-R ad7e356fc9072ccedd7e8855b564b667
-U dan
-Z 16dbeccb3b1fdf0272ff90de38793516
+P 2da677c45b643482eec39e4db7079c772760bc966dc71bf6c01658cc468f5823
+R bd5efaaf882b3ae0c03e0e5a6693b37e
+U stephan
+Z c8a3d40e488aded9faa8a7e4d2f0c359
 # Remove this line to create a well-formed Fossil manifest.
index 8828e08bae8a919dc00925f38e76bfe4ce914aa8..de485659dc6e15102e64168fdca10fe6917e4bea 100644 (file)
@@ -1 +1 @@
-2da677c45b643482eec39e4db7079c772760bc966dc71bf6c01658cc468f5823
\ No newline at end of file
+a4423ca234453c14eb40db7fe5943f63b30fd9dc2207388e8a2966733a004e9d
\ No newline at end of file