Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add the pause/unpause capability to the opfs-sahpool VFS, as discussed in forum thread fe8cdb8431c. Summary: this gives clients a way to eke some degree of multi-page/tab/Worker concurrency out of this VFS but requires that coordination to be implemented client-side, e.g. via a SharedWorker or WebLocks. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
b5dbd521951e129b4dec69f191a87250 |
User & Date: | stephan 2025-02-20 04:14:26.736 |
Context
2025-02-20
| ||
04:45 | wasm makefile docs: make explicit that the node.js-for-node.js builds (as opposed to the node.js-for-browser builds) are both untested and unsupported. (check-in: e1f184889f user: stephan tags: trunk) | |
04:14 | Add the pause/unpause capability to the opfs-sahpool VFS, as discussed in forum thread fe8cdb8431c. Summary: this gives clients a way to eke some degree of multi-page/tab/Worker concurrency out of this VFS but requires that coordination to be implemented client-side, e.g. via a SharedWorker or WebLocks. (check-in: b5dbd52195 user: stephan tags: trunk) | |
03:27 | configure: when running proj-check-function-in-lib, strip -Werror from CFLAGS for the duration of the test. This enables CFLAGS='-Wall -Werror' and the like to be passed to configure without breaking these configure-time checks. (check-in: 4ae9d6c642 user: stephan tags: trunk) | |
2025-01-31
| ||
17:47 | Minor cleanups in the opfs-sahpool pause/unpause API demo. (Closed-Leaf check-in: e205cdc468 user: stephan tags: opfs-sahpool-pause) | |
Changes
Changes to ext/wasm/GNUmakefile.
︙ | ︙ | |||
99 100 101 102 103 104 105 | $(error Cannot make release-quality binary because wasm-strip is not available.) endif bin.wasm-strip := echo "not wasm-stripping" endif ifeq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS))) $(info ==============================================================) $(info == Development build. Make one of (dist, snapshot) for a) | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | $(error Cannot make release-quality binary because wasm-strip is not available.) endif bin.wasm-strip := echo "not wasm-stripping" endif ifeq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS))) $(info ==============================================================) $(info == Development build. Make one of (dist, snapshot) for a) $(info == smaller and faster release build.) $(info ==============================================================) endif endif # ^^^ end of are-we-MAKING_CLEAN maybe-wasm-strip := $(bin.wasm-strip) ######################################################################## |
︙ | ︙ |
Changes to ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js.
︙ | ︙ | |||
497 498 499 500 501 502 503 | /* Current number of in-use files from pool. */ getFileCount(){return this.#mapFilenameToSAH.size} /* Returns an array of the names of all currently-opened client-specified filenames. */ getFileNames(){ const rc = []; | | < < < < < < < < < < < < | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 | /* Current number of in-use files from pool. */ getFileCount(){return this.#mapFilenameToSAH.size} /* Returns an array of the names of all currently-opened client-specified filenames. */ getFileNames(){ const rc = []; for(const n of this.#mapFilenameToSAH.keys()) rc.push(n); return rc; } /** Adds n files to the pool's capacity. This change is persistent across settings. Returns a Promise which resolves to the new capacity. */ async addCapacity(n){ for(let i = 0; i < n; ++i){ |
︙ | ︙ | |||
553 554 555 556 557 558 559 | this.#availableSAH.delete(ah); ++nRm; } return nRm; } /** | | | > | | | | > | > > > | | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | this.#availableSAH.delete(ah); ++nRm; } return nRm; } /** Releases all currently-opened SAHs. The only legal operation after this is acquireAccessHandles() or (if this is called from pauseVfs()) either of isPaused() or unpauseVfs(). */ releaseAccessHandles(){ for(const ah of this.#mapSAHToName.keys()) ah.close(); this.#mapSAHToName.clear(); this.#mapFilenameToSAH.clear(); this.#availableSAH.clear(); } /** Opens all files under this.vfsDir/this.#dhOpaque and acquires a SAH for each. Returns a Promise which resolves to no value but completes once all SAHs are acquired. If acquiring an SAH throws, this.$error will contain the corresponding Error object. If it throws, it releases any SAHs which it may have acquired before the exception was thrown, leaving the VFS in a well-defined but unusable state. If clearFiles is true, the client-stored state of each file is cleared when its handle is acquired, including its name, flags, and any data stored after the metadata block. */ async acquireAccessHandles(clearFiles=false){ const files = []; for await (const [name,h] of this.#dhOpaque){ if('file'===h.kind){ files.push([name,h]); } } return Promise.all(files.map(async([name,h])=>{ |
︙ | ︙ | |||
828 829 830 831 832 833 834 | return this.#mapFilenameToSAH.get(path); } /** Removes this object's sqlite3_vfs registration and shuts down this object, releasing all handles, mappings, and whatnot, including deleting its data directory. There is currently no | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 | return this.#mapFilenameToSAH.get(path); } /** Removes this object's sqlite3_vfs registration and shuts down this object, releasing all handles, mappings, and whatnot, including deleting its data directory. There is currently no way to "revive" the object and reaquire its resources. Similarly, there is no recovery strategy if removal of any given SAH fails, so such errors are ignored by this function. This function is intended primarily for testing. Resolves to true if it did its job, false if the VFS has already been shut down. @see pauseVfs() @see unpauseVfs() */ async removeVfs(){ if(!this.#cVfs.pointer || !this.#dhOpaque) return false; capi.sqlite3_vfs_unregister(this.#cVfs.pointer); this.#cVfs.dispose(); delete initPromises[this.vfsName]; try{ this.releaseAccessHandles(); await this.#dhVfsRoot.removeEntry(OPAQUE_DIR_NAME, {recursive: true}); this.#dhOpaque = undefined; await this.#dhVfsParent.removeEntry( this.#dhVfsRoot.name, {recursive: true} ); this.#dhVfsRoot = this.#dhVfsParent = undefined; }catch(e){ sqlite3.config.error(this.vfsName,"removeVfs() failed with no recovery strategy:",e); /*otherwise ignored - there is no recovery strategy*/ } return true; } /** "Pauses" this VFS by unregistering it from SQLite and relinquishing all open SAHs, leaving the associated files intact. If this object is already paused, this is a no-op. Returns this object. This function throws if SQLite has any opened file handles hosted by this VFS, as the alternative would be to invoke Undefined Behavior by closing file handles out from under the library. Similarly, automatically closing any database handles opened by this VFS would invoke Undefined Behavior in downstream code which is holding those pointers. If this function throws due to open file handles then it has no side effects. If the OPFS API throws while closing handles then the VFS is left in an undefined state. @see isPaused() @see unpauseVfs() */ pauseVfs(){ if(this.#mapS3FileToOFile_.size>0){ sqlite3.SQLite3Error.toss( capi.SQLITE_MISUSE, "Cannot pause VFS", this.vfsName,"because it has opened files." ); } if(this.#mapSAHToName.size>0){ capi.sqlite3_vfs_unregister(this.vfsName); this.releaseAccessHandles(); } return this; } /** Returns true if this pool is currently paused else false. @see pauseVfs() @see unpauseVfs() */ isPaused(){ return 0===this.#mapSAHToName.size; } /** "Unpauses" this VFS, reacquiring all SAH's and (if successful) re-registering it with SQLite. This is a no-op if the VFS is not currently paused. The returned Promise resolves to this object. See acquireAccessHandles() for how it behaves if it throws due to SAH acquisition failure. @see isPaused() @see pauseVfs() */ async unpauseVfs(){ if(0===this.#mapSAHToName.size){ return this.acquireAccessHandles(false). then(()=>capi.sqlite3_vfs_register(this.#cVfs, 0),this); } return this; } //! Documented elsewhere in this file. exportFile(name){ const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:",name); const n = sah.getSize() - HEADER_OFFSET_DATA; const b = new Uint8Array(n>0 ? n : 0); if(n>0){ |
︙ | ︙ | |||
979 980 981 982 983 984 985 986 987 988 989 990 991 992 | importDb(name, bytes){ return this.#p.importDb(name,bytes) } async wipeFiles(){ return this.#p.reset(true) } unlink(filename){ return this.#p.deletePath(filename) } async removeVfs(){ return this.#p.removeVfs() } }/* class OpfsSAHPoolUtil */; /** Returns a resolved Promise if the current environment has a "fully-sync" SAH impl, else a rejected Promise. */ | > > > > | 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | importDb(name, bytes){ return this.#p.importDb(name,bytes) } async wipeFiles(){ return this.#p.reset(true) } unlink(filename){ return this.#p.deletePath(filename) } async removeVfs(){ return this.#p.removeVfs() } pauseVfs(){ this.#p.pauseVfs(); return this; } async unpauseVfs(){ return this.#p.unpauseVfs().then(()=>this); } isPaused(){ return this.#p.isPaused() } }/* class OpfsSAHPoolUtil */; /** Returns a resolved Promise if the current environment has a "fully-sync" SAH impl, else a rejected Promise. */ |
︙ | ︙ | |||
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 | The SQLite VFS name under which this pool's VFS is registered. - [async] void wipeFiles() Clears all client-defined state of all SAHs and makes all of them available for re-use by the pool. Results are undefined if any such handles are currently in use, e.g. by an sqlite3 db. */ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){ options = Object.assign(Object.create(null), optionDefaults, (options||{})); const vfsName = options.name; if(options.$testThrowPhase1){ throw options.$testThrowPhase1; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 | The SQLite VFS name under which this pool's VFS is registered. - [async] void wipeFiles() Clears all client-defined state of all SAHs and makes all of them available for re-use by the pool. Results are undefined if any such handles are currently in use, e.g. by an sqlite3 db. APIs specific to the "pause" capability (added in version 3.49): Summary: "pausing" the VFS disassociates it from SQLite and relinquishes its SAHs so that they may be opened by another instance of this VFS (running in a separate tab/page or Worker). "Unpausing" it takes back control, if able. - pauseVfs() "Pauses" this VFS by unregistering it from SQLite and relinquishing all open SAHs, leaving the associated files intact. This enables pages/tabs to coordinate semi-concurrent usage of this VFS. If this object is already paused, this is a no-op. Returns this object. Throws if SQLite has any opened file handles hosted by this VFS. If this function throws due to open file handles then it has no side effects. If the OPFS API throws while closing handles then the VFS is left in an undefined state. - isPaused() Returns true if this VFS is paused, else false. - [async] unpauseVfs() Restores the VFS to an active state after having called pauseVfs() on it. This is a no-op if the VFS is not paused. The returned Promise resolves to this object on success. A rejected Promise means there was a problem reacquiring the SAH handles (possibly because they're in use by another instance or have since been removed). Generically speaking, there is no recovery strategy for that type of error, but if the problem is simply that the OPFS files are locked, then a later attempt to unpause it, made after the concurrent instance releases the SAHs, may recover from the situation. */ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){ options = Object.assign(Object.create(null), optionDefaults, (options||{})); const vfsName = options.name; if(options.$testThrowPhase1){ throw options.$testThrowPhase1; } |
︙ | ︙ |
Changes to ext/wasm/api/sqlite3-worker1-promiser.c-pp.js.
︙ | ︙ | |||
331 332 333 334 335 336 337 | original: sqlite3Worker1Promiser }); //#if target=es6-module /** When built as a module, we export sqlite3Worker1Promiser.v2() instead of sqlite3Worker1Promise() because (A) its interface is more | | | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | original: sqlite3Worker1Promiser }); //#if target=es6-module /** When built as a module, we export sqlite3Worker1Promiser.v2() instead of sqlite3Worker1Promise() because (A) its interface is more conventional for ESM usage and (B) the ESM export option for this API did not exist until v2 was created, so there's no backwards incompatibility. */ export default sqlite3Worker1Promiser.v2; //#endif /* target=es6-module */ //#else /* Built with the omit-oo1 flag. */ //#endif ifnot omit-oo1 |
Changes to ext/wasm/index.html.
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 | sqlite3_vfs OPFS proxy using SharedArrayBuffer and the Atomics APIs to regulate communication between the synchronous sqlite3_vfs interface and the async OPFS impl. </li> <li><a href='tests/opfs/concurrency/index.html'>OPFS concurrency</a> tests using multiple workers. </li> </ul> </li> <li><strong>WASMFS</strong>-specific tests which require that the WASMFS build is available on this server (it is not by default) and that this server emits the COOP/COEP headers. <ul> | > > > > | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | sqlite3_vfs OPFS proxy using SharedArrayBuffer and the Atomics APIs to regulate communication between the synchronous sqlite3_vfs interface and the async OPFS impl. </li> <li><a href='tests/opfs/concurrency/index.html'>OPFS concurrency</a> tests using multiple workers. </li> <li><a href='tests/opfs/sahpool/index.html'>OPFS SAHPool cooperative semi-concurrency</a> demonstrates usage of the OPFS SAHPool VFS's "pause" feature to coordinate access to a database. </li> </ul> </li> <li><strong>WASMFS</strong>-specific tests which require that the WASMFS build is available on this server (it is not by default) and that this server emits the COOP/COEP headers. <ul> |
︙ | ︙ |
Changes to ext/wasm/tester1.c-pp.js.
︙ | ︙ | |||
3185 3186 3187 3188 3189 3190 3191 | .assert( 'wal'===db.selectValue('pragma journal_mode') || wasm.compileOptionUsed('OMIT_WAL') ); db.close(); T.assert(1 === u1.getFileCount()); db = new u2.OpfsSAHPoolDb(dbName); | | > > > > > > > > > > > > > > > > > | 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 | .assert( 'wal'===db.selectValue('pragma journal_mode') || wasm.compileOptionUsed('OMIT_WAL') ); db.close(); T.assert(1 === u1.getFileCount()); db = new u2.OpfsSAHPoolDb(dbName); T.assert(1 === u1.getFileCount()) .mustThrowMatching( ()=>u1.pauseVfs(), (err)=>{ return capi.SQLITE_MISUSE===err.resultCode && /^SQLITE_MISUSE: Cannot pause VFS /.test(err.message); }, "Cannot pause VFS with opened db." ); db.close(); T.assert( u2===u2.pauseVfs() ) .assert( u2.isPaused() ) .assert( 0===capi.sqlite3_vfs_find(u2.vfsName) ) .mustThrowMatching(()=>new u2.OpfsSAHPoolDb(dbName), /.+no such vfs: .+/, "VFS is not available") .assert( u2===await u2.unpauseVfs() ) .assert( u2===await u1.unpauseVfs(), "unpause is a no-op if the VFS is not paused" ) .assert( 0!==capi.sqlite3_vfs_find(u2.vfsName) ); const fileNames = u1.getFileNames(); T.assert(1 === fileNames.length) .assert(dbName === fileNames[0]) .assert(1 === u1.getFileCount()) if(1){ // test exportFile() and importDb() const dbytes = u1.exportFile(dbName); |
︙ | ︙ |
Added ext/wasm/tests/opfs/sahpool/index.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <!doctype html> <html lang="en-us"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon"> <link rel="stylesheet" href="../../../common/emscripten.css"/> <link rel="stylesheet" href="../../../common/testing.css"/> <title>sqlite3 tester: OpfsSAHPool Pausing</title> <style></style> </head> <body><h1 id='color-target'></h1> <p> This page provides a <em>very basic</em> demonstration of "pausing" and "unpausing" the OPFS SAHPool VFS such that multiple pages or workers can use it by coordinating which handler may have it open at any given time. </p> <div class='input-wrapper'> <input type='checkbox' id='cb-log-reverse'> <label for='cb-log-reverse'>Reverse log order?</label> </div> <div id='test-output'></div> <script>(function(){ document.querySelector('h1').innerHTML = document.querySelector('title').innerHTML; })();</script> <script src="sahpool-pausing.js"></script> </body> </html> |
Added ext/wasm/tests/opfs/sahpool/sahpool-pausing.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | /* 2025-01-31 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. *********************************************************************** These tests are specific to the opfs-sahpool VFS and are limited to demonstrating its pause/unpause capabilities. Most of this file is infrastructure for displaying results to the user. Search for runTests() to find where the work actually starts. */ 'use strict'; (function(){ let logClass; const mapToString = (v)=>{ switch(typeof v){ case 'number': case 'string': case 'boolean': case 'undefined': case 'bigint': return ''+v; default: break; } if(null===v) return 'null'; if(v instanceof Error){ v = { message: v.message, stack: v.stack, errorClass: v.name }; } return JSON.stringify(v,undefined,2); }; const normalizeArgs = (args)=>args.map(mapToString); const logTarget = document.querySelector('#test-output'); logClass = function(cssClass,...args){ const ln = document.createElement('div'); if(cssClass){ for(const c of (Array.isArray(cssClass) ? cssClass : [cssClass])){ ln.classList.add(c); } } ln.append(document.createTextNode(normalizeArgs(args).join(' '))); logTarget.append(ln); }; const cbReverse = document.querySelector('#cb-log-reverse'); //cbReverse.setAttribute('checked','checked'); const cbReverseKey = 'tester1:cb-log-reverse'; const cbReverseIt = ()=>{ logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse'); //localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0); }; cbReverse.addEventListener('change', cbReverseIt, true); /*if(localStorage.getItem(cbReverseKey)){ cbReverse.checked = !!(+localStorage.getItem(cbReverseKey)); }*/ cbReverseIt(); const log = (...args)=>{ //console.log(...args); logClass('',...args); } const warn = (...args)=>{ console.warn(...args); logClass('warning',...args); } const error = (...args)=>{ console.error(...args); logClass('error',...args); }; const toss = (...args)=>{ error(...args); throw new Error(args.join(' ')); }; const endOfWork = (passed=true)=>{ const eH = document.querySelector('#color-target'); const eT = document.querySelector('title'); if(passed){ log("End of work chain. If you made it this far, you win."); eH.innerText = 'PASS: '+eH.innerText; eH.classList.add('tests-pass'); eT.innerText = 'PASS: '+eT.innerText; }else{ eH.innerText = 'FAIL: '+eH.innerText; eH.classList.add('tests-fail'); eT.innerText = 'FAIL: '+eT.innerText; } }; const nextHandlerQueue = []; const nextHandler = function(workerId,...msg){ log(workerId,...msg); (nextHandlerQueue.shift())(); }; const postThen = function(W, msgType, callback){ nextHandlerQueue.push(callback); W.postMessage({type:msgType}); }; /** Run a series of operations on an sahpool db spanning two workers. This would arguably be more legible with Promises, but creating a Promise-based communication channel for this purpose is left as an exercise for the reader. An example of such a proxy can be found in the SQLite source tree: https://sqlite.org/src/file/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js */ const runPyramidOfDoom = function(W1, W2){ postThen(W1, 'vfs-acquire', function(){ postThen(W1, 'db-init', function(){ postThen(W1, 'db-query', function(){ postThen(W1, 'vfs-pause', function(){ postThen(W2, 'vfs-acquire', function(){ postThen(W2, 'db-query', function(){ postThen(W2, 'vfs-remove', endOfWork); }); }); }); }); }); }); }; const runTests = function(){ log("Running opfs-sahpool pausing tests..."); const wjs = 'sahpool-worker.js?sqlite3.dir=../../../jswasm'; const W1 = new Worker(wjs+'&workerId=w1'), W2 = new Worker(wjs+'&workerId=w2'); W1.workerId = 'w1'; W2.workerId = 'w2'; let initCount = 0; const onmessage = function({data}){ //log("onmessage:",data); switch(data.type){ case 'vfs-acquired': nextHandler(data.workerId, "VFS acquired"); break; case 'vfs-paused': nextHandler(data.workerId, "VFS paused"); break; case 'vfs-unpaused': nextHandler(data.workerId, 'VFS unpaused'); break; case 'vfs-removed': nextHandler(data.workerId, 'VFS removed'); break; case 'db-inited': nextHandler(data.workerId, 'db initialized'); break; case 'query-result': nextHandler(data.workerId, 'query result', data.payload); break; case 'log': log(data.workerId, ':', ...data.payload); break; case 'error': error(data.workerId, ':', ...data.payload); endOfWork(false); break; case 'initialized': log(data.workerId, ': Worker initialized',...data.payload); if( 2===++initCount ){ runPyramidOfDoom(W1, W2); } break; } }; W1.onmessage = W2.onmessage = onmessage; }; runTests(); })(); |
Added ext/wasm/tests/opfs/sahpool/sahpool-worker.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /* 2025-01-31 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. *********************************************************************** This file is part of sahpool-pausing.js's demonstration of the pause/unpause feature of the opfs-sahpool VFS. */ const searchParams = new URL(self.location.href).searchParams; const workerId = searchParams.get('workerId'); const wPost = (type,...args)=>postMessage({type, workerId, payload:args}); const log = (...args)=>wPost('log',...args); let capi, wasm, S, poolUtil; const sahPoolConfig = { name: 'opfs-sahpool-pausable', clearOnInit: false, initialCapacity: 3 }; importScripts(searchParams.get('sqlite3.dir') + '/sqlite3.js'); const sqlExec = function(sql){ const db = new poolUtil.OpfsSAHPoolDb('/my.db'); try{ return db.exec(sql); }finally{ db.close(); } }; const clog = console.log.bind(console); globalThis.onmessage = function({data}){ clog(workerId+": onmessage:",data); switch(data.type){ case 'vfs-acquire': if( poolUtil ){ poolUtil.unpauseVfs().then(()=>wPost('vfs-unpaused')); }else{ S.installOpfsSAHPoolVfs(sahPoolConfig).then(pu=>{ poolUtil = pu; wPost('vfs-acquired'); }); } break; case 'db-init': try{ sqlExec([ "DROP TABLE IF EXISTS mytable;", "CREATE TABLE mytable(a);", "INSERT INTO mytable(a) VALUES(11),(22),(33)" ]); wPost('db-inited'); }catch(e){ wPost('error',e.message); } break; case 'db-query': { const rc = sqlExec({ sql: 'select * from mytable order by a', rowMode: 'array', returnValue: 'resultRows' }); wPost('query-result',rc); break; } case 'vfs-remove': poolUtil.removeVfs().then(()=>wPost('vfs-removed')); break; case 'vfs-pause': poolUtil.pauseVfs(); wPost('vfs-paused'); break; } }; const hasOpfs = ()=>{ return globalThis.FileSystemHandle && globalThis.FileSystemDirectoryHandle && globalThis.FileSystemFileHandle && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle && navigator?.storage?.getDirectory; }; if( !hasOpfs() ){ wPost('error',"OPFS not detected"); }else{ globalThis.sqlite3InitModule().then(async function(sqlite3){ S = sqlite3; capi = S.capi; wasm = S.wasm; log("sqlite3 version:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); //return sqlite3.installOpfsSAHPoolVfs(sahPoolConfig).then(pu=>poolUtil=pu); }).then(()=>{ wPost('initialized'); }); } |