| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 12391c7 commit c7c420e
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -855,6 +855,15 @@ An unknown Diffie-Hellman group name was given. See | |||
| 855 | 855 | ||
| 856 | 856 | The [`fs.Dir`][] was previously closed. | |
| 857 | 857 | ||
| 858 | + <a id="ERR_DIR_CONCURRENT_OPERATION"></a> | ||
| 859 | + ### `ERR_DIR_CONCURRENT_OPERATION` | ||
| 860 | + <!-- YAML | ||
| 861 | + added: REPLACEME | ||
| 862 | + --> | ||
| 863 | + | ||
| 864 | + A synchronous read or close call was attempted on an [`fs.Dir`][] which has | ||
| 865 | + ongoing asynchronous operations. | ||
| 866 | + | ||
| 858 | 867 | <a id="ERR_DNS_SET_SERVERS_FAILED"></a> | |
| 859 | 868 | ### `ERR_DNS_SET_SERVERS_FAILED` | |
| 860 | 869 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -788,6 +788,9 @@ E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign', Error); | |||
| 788 | 788 | E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH', | |
| 789 | 789 | 'Input buffers must have the same byte length', RangeError); | |
| 790 | 790 | E('ERR_DIR_CLOSED', 'Directory handle was closed', Error); | |
| 791 | + E('ERR_DIR_CONCURRENT_OPERATION', | ||
| 792 | + 'Cannot do synchronous work on directory handle with concurrent ' + | ||
| 793 | + 'asynchronous operations', Error); | ||
| 791 | 794 | E('ERR_DNS_SET_SERVERS_FAILED', 'c-ares failed to set servers: "%s" [%s]', | |
| 792 | 795 | Error); | |
| 793 | 796 | E('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ const dirBinding = internalBinding('fs_dir'); | |||
| 12 | 12 | const { | |
| 13 | 13 | codes: { | |
| 14 | 14 | ERR_DIR_CLOSED, | |
| 15 | + ERR_DIR_CONCURRENT_OPERATION, | ||
| 15 | 16 | ERR_INVALID_CALLBACK, | |
| 16 | 17 | ERR_MISSING_ARGS | |
| 17 | 18 | } | |
@@ -37,6 +38,7 @@ const kDirOptions = Symbol('kDirOptions'); | |||
| 37 | 38 | const kDirReadImpl = Symbol('kDirReadImpl'); | |
| 38 | 39 | const kDirReadPromisified = Symbol('kDirReadPromisified'); | |
| 39 | 40 | const kDirClosePromisified = Symbol('kDirClosePromisified'); | |
| 41 | + const kDirOperationQueue = Symbol('kDirOperationQueue'); | ||
| 40 | 42 | ||
| 41 | 43 | class Dir { | |
| 42 | 44 | constructor(handle, path, options) { | |
@@ -46,6 +48,10 @@ class Dir { | |||
| 46 | 48 | this[kDirPath] = path; | |
| 47 | 49 | this[kDirClosed] = false; | |
| 48 | 50 | ||
| 51 | + // Either `null` or an Array of pending operations (= functions to be called | ||
| 52 | + // once the current operation is done). | ||
| 53 | + this[kDirOperationQueue] = null; | ||
| 54 | + | ||
| 49 | 55 | this[kDirOptions] = { | |
| 50 | 56 | bufferSize: 32, | |
| 51 | 57 | ...getOptions(options, { | |
@@ -79,6 +85,13 @@ class Dir { | |||
| 79 | 85 | throw new ERR_INVALID_CALLBACK(callback); | |
| 80 | 86 | } | |
| 81 | 87 | ||
| 88 | + if (this[kDirOperationQueue] !== null) { | ||
| 89 | + this[kDirOperationQueue].push(() => { | ||
| 90 | + this[kDirReadImpl](maybeSync, callback); | ||
| 91 | + }); | ||
| 92 | + return; | ||
| 93 | + } | ||
| 94 | + | ||
| 82 | 95 | if (this[kDirBufferedEntries].length > 0) { | |
| 83 | 96 | const [ name, type ] = this[kDirBufferedEntries].splice(0, 2); | |
| 84 | 97 | if (maybeSync) | |
@@ -90,6 +103,12 @@ class Dir { | |||
| 90 | 103 | ||
| 91 | 104 | const req = new FSReqCallback(); | |
| 92 | 105 | req.oncomplete = (err, result) => { | |
| 106 | + process.nextTick(() => { | ||
| 107 | + const queue = this[kDirOperationQueue]; | ||
| 108 | + this[kDirOperationQueue] = null; | ||
| 109 | + for (const op of queue) op(); | ||
| 110 | + }); | ||
| 111 | + | ||
| 93 | 112 | if (err || result === null) { | |
| 94 | 113 | return callback(err, result); | |
| 95 | 114 | } | |
@@ -98,6 +117,7 @@ class Dir { | |||
| 98 | 117 | getDirent(this[kDirPath], result[0], result[1], callback); | |
| 99 | 118 | }; | |
| 100 | 119 | ||
| 120 | + this[kDirOperationQueue] = []; | ||
| 101 | 121 | this[kDirHandle].read( | |
| 102 | 122 | this[kDirOptions].encoding, | |
| 103 | 123 | this[kDirOptions].bufferSize, | |
@@ -110,6 +130,10 @@ class Dir { | |||
| 110 | 130 | throw new ERR_DIR_CLOSED(); | |
| 111 | 131 | } | |
| 112 | 132 | ||
| 133 | + if (this[kDirOperationQueue] !== null) { | ||
| 134 | + throw new ERR_DIR_CONCURRENT_OPERATION(); | ||
| 135 | + } | ||
| 136 | + | ||
| 113 | 137 | if (this[kDirBufferedEntries].length > 0) { | |
| 114 | 138 | const [ name, type ] = this[kDirBufferedEntries].splice(0, 2); | |
| 115 | 139 | return getDirent(this[kDirPath], name, type); | |
@@ -143,6 +167,13 @@ class Dir { | |||
| 143 | 167 | throw new ERR_INVALID_CALLBACK(callback); | |
| 144 | 168 | } | |
| 145 | 169 | ||
| 170 | + if (this[kDirOperationQueue] !== null) { | ||
| 171 | + this[kDirOperationQueue].push(() => { | ||
| 172 | + this.close(callback); | ||
| 173 | + }); | ||
| 174 | + return; | ||
| 175 | + } | ||
| 176 | + | ||
| 146 | 177 | this[kDirClosed] = true; | |
| 147 | 178 | const req = new FSReqCallback(); | |
| 148 | 179 | req.oncomplete = callback; | |
@@ -154,6 +185,10 @@ class Dir { | |||
| 154 | 185 | throw new ERR_DIR_CLOSED(); | |
| 155 | 186 | } | |
| 156 | 187 | ||
| 188 | + if (this[kDirOperationQueue] !== null) { | ||
| 189 | + throw new ERR_DIR_CONCURRENT_OPERATION(); | ||
| 190 | + } | ||
| 191 | + | ||
| 157 | 192 | this[kDirClosed] = true; | |
| 158 | 193 | const ctx = { path: this[kDirPath] }; | |
| 159 | 194 | const result = this[kDirHandle].close(undefined, ctx); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,6 +33,10 @@ const dirclosedError = { | |||
| 33 | 33 | code: 'ERR_DIR_CLOSED' | |
| 34 | 34 | }; | |
| 35 | 35 | ||
| 36 | + const dirconcurrentError = { | ||
| 37 | + code: 'ERR_DIR_CONCURRENT_OPERATION' | ||
| 38 | + }; | ||
| 39 | + | ||
| 36 | 40 | const invalidCallbackObj = { | |
| 37 | 41 | code: 'ERR_INVALID_CALLBACK', | |
| 38 | 42 | name: 'TypeError' | |
@@ -230,3 +234,39 @@ async function doAsyncIterDirClosedTest() { | |||
| 230 | 234 | assert.throws(() => dir.close(), dirclosedError); | |
| 231 | 235 | } | |
| 232 | 236 | doAsyncIterDirClosedTest().then(common.mustCall()); | |
| 237 | + | ||
| 238 | + // Check that readSync() and closeSync() during read() throw exceptions | ||
| 239 | + async function doConcurrentAsyncAndSyncOps() { | ||
| 240 | + const dir = await fs.promises.opendir(testDir); | ||
| 241 | + const promise = dir.read(); | ||
| 242 | + | ||
| 243 | + assert.throws(() => dir.closeSync(), dirconcurrentError); | ||
| 244 | + assert.throws(() => dir.readSync(), dirconcurrentError); | ||
| 245 | + | ||
| 246 | + await promise; | ||
| 247 | + dir.closeSync(); | ||
| 248 | + } | ||
| 249 | + doConcurrentAsyncAndSyncOps().then(common.mustCall()); | ||
| 250 | + | ||
| 251 | + // Check that concurrent read() operations don't do weird things. | ||
| 252 | + async function doConcurrentAsyncOps() { | ||
| 253 | + const dir = await fs.promises.opendir(testDir); | ||
| 254 | + const promise1 = dir.read(); | ||
| 255 | + const promise2 = dir.read(); | ||
| 256 | + | ||
| 257 | + assertDirent(await promise1); | ||
| 258 | + assertDirent(await promise2); | ||
| 259 | + dir.closeSync(); | ||
| 260 | + } | ||
| 261 | + doConcurrentAsyncOps().then(common.mustCall()); | ||
| 262 | + | ||
| 263 | + // Check that concurrent read() + close() operations don't do weird things. | ||
| 264 | + async function doConcurrentAsyncMixedOps() { | ||
| 265 | + const dir = await fs.promises.opendir(testDir); | ||
| 266 | + const promise1 = dir.read(); | ||
| 267 | + const promise2 = dir.close(); | ||
| 268 | + | ||
| 269 | + assertDirent(await promise1); | ||
| 270 | + await promise2; | ||
| 271 | + } | ||
| 272 | + doConcurrentAsyncMixedOps().then(common.mustCall()); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments