Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Doc updates in JS code. No functional changes. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
587ed3a5d283898ad0e67ccee86a0a4c |
User & Date: | stephan 2024-06-12 12:17:03 |
Context
2024-06-12
| ||
12:36 | Fix a potential db corruption case triggered by the OPFS VFS's xCheckReservedLock() implementation, as discussed in forum thread a2f573b00cda1372. (check-in: c298b8ba user: stephan tags: trunk) | |
12:17 | Doc updates in JS code. No functional changes. (check-in: 587ed3a5 user: stephan tags: trunk) | |
11:39 | Slight API doc tweak for xCheckReservedLock(), based on forum feedback. No code changes. (check-in: 2af7a96f user: stephan tags: trunk) | |
Changes
Changes to ext/wasm/api/sqlite3-opfs-async-proxy.js.
︙ | ︙ | |||
169 170 171 172 173 174 175 | } } return [dh, filename]; }; /** If the given file-holding object has a sync handle attached to it, | | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | } } return [dh, filename]; }; /** If the given file-holding object has a sync handle attached to it, that handle is removed 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); |
︙ | ︙ | |||
289 290 291 292 293 294 295 296 297 298 299 300 301 302 | In order to help alleviate cross-tab contention for a dabase, if an exception is thrown while acquiring the handle, this routine will wait briefly and try again, up to some fixed number of times. If acquisition still fails at that point it will give up and propagate the exception. Client-level code will see that as an I/O error. */ const getSyncHandle = async (fh,opName)=>{ if(!fh.syncHandle){ const t = performance.now(); log("Acquiring sync handle for",fh.filenameAbs); const maxTries = 6, msBase = state.asyncIdleWaitTime * 2; | > > > > > > > > > > > > > > | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | In order to help alleviate cross-tab contention for a dabase, if an exception is thrown while acquiring the handle, this routine will wait briefly and try again, up to some fixed number of times. If acquisition still fails at that point it will give up and propagate the exception. Client-level code will see that as an I/O error. 2024-06-12: there is a rare race condition here which has been reported a single time: https://sqlite.org/forum/forumpost/9ee7f5340802d600 What appears to be happening is that file we're waiting for a lock on is deleted while we wait. What currently happens here is that a locking exception is thrown but the exception type is NotFoundError. In such cases, we very probably should attempt to re-open/re-create the file an obtain the lock on it (noting that there's another race condition there). That's easy to say but creating a viable test for that condition has proven challenging so far. */ const getSyncHandle = async (fh,opName)=>{ if(!fh.syncHandle){ const t = performance.now(); log("Acquiring sync handle for",fh.filenameAbs); const maxTries = 6, msBase = state.asyncIdleWaitTime * 2; |
︙ | ︙ | |||
670 671 672 673 674 675 676 | mTimeEnd(); }, xUnlock: async function(fid/*sqlite3_file pointer*/, lockType/*SQLITE_LOCK_...*/){ mTimeStart('xUnlock'); let rc = 0; const fh = __openFiles[fid]; | > | < > > | 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 | mTimeEnd(); }, xUnlock: async function(fid/*sqlite3_file pointer*/, lockType/*SQLITE_LOCK_...*/){ mTimeStart('xUnlock'); let rc = 0; const fh = __openFiles[fid]; if( fh.syncHandle && state.sq3Codes.SQLITE_LOCK_NONE===lockType /* Note that we do not differentiate between lock types in this VFS. We're either locked or unlocked. */ ){ wTimeStart('xUnlock'); try { await closeSyncHandle(fh) } catch(e){ state.s11n.storeException(1,e); rc = state.sq3Codes.SQLITE_IOERR_UNLOCK; } wTimeEnd(); |
︙ | ︙ |
Changes to ext/wasm/common/whwasmutil.js.
︙ | ︙ | |||
152 153 154 155 156 157 158 | memory and returning its pointer. In Emscripten this is conventionally made available via `Module['_malloc']`. This API requires that the alloc routine throw on allocation error, as opposed to returning null or 0. - 'dealloc()` must behave like C's `free()`, accepting either a pointer returned from its allocation counterpart or the values | | < | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | memory and returning its pointer. In Emscripten this is conventionally made available via `Module['_malloc']`. This API requires that the alloc routine throw on allocation error, as opposed to returning null or 0. - 'dealloc()` must behave like C's `free()`, accepting either a pointer returned from its allocation counterpart or the values null/0 (for which it must be a no-op). In Emscripten this is conventionally made available via `Module['_free']`. APIs which require allocation routines are explicitly documented as such and/or have "alloc" in their names. This code is developed and maintained in conjunction with the Jaccwabyt project: |
︙ | ︙ | |||
726 727 728 729 730 731 732 | } if(list) list.push(rc); }while(list && arguments[0].length); return list || rc; }; /** | | | | | | | | | > | | | | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 | } if(list) list.push(rc); }while(list && arguments[0].length); return list || rc; }; /** The counterpart of peek(), this sets a numeric value at the given WASM heap address, using the 3rd argument to define how many bytes are written. Throws if given an invalid type. See peek() for details about the `type` argument. If the 3rd argument ends with `*` then it is treated as a pointer type and this function behaves as if the 3rd argument were `i32`. If the first argument is an array, it is treated like a list of pointers and the given value is written to each one. Returns `this`. (Prior to 2022-12-09 it returned this function.) ACHTUNG: calling this often, e.g. in a loop to populate a large chunk of memory, can have a noticably painful impact on performance. Rather than doing so, use heapForSize() to fetch the heap object and assign directly to it or use the heap's set() method. */ target.poke = function(ptr, value, type='i8'){ if (type.endsWith('*')) type = ptrIR; const c = (cache.memory && cache.heapSize === cache.memory.buffer.byteLength) ? cache : heapWrappers(); for(const p of (Array.isArray(ptr) ? ptr : [ptr])){ switch (type) { |
︙ | ︙ |