├── .gitattributes ├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── package.json ├── src └── lock.js └── test └── all.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /lib/ 3 | /doc/ 4 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | 5 | clean: { 6 | dist: { 7 | files: { 8 | src: [ 9 | 'lib', 10 | 'doc' 11 | ] 12 | } 13 | } 14 | }, 15 | 16 | jshint: { 17 | options: { 18 | camelcase: true, 19 | curly: true, 20 | forin: true, 21 | freeze: true, 22 | immed: true, 23 | latedef: true, 24 | newcap: true, 25 | noarg: true, 26 | quotmark: 'single', 27 | undef: true, 28 | unused: true, 29 | strict: true, 30 | trailing: true, 31 | boss: true, 32 | expr: true, 33 | multistr: true, 34 | smarttabs: true, 35 | shadow: true, 36 | node: true 37 | }, 38 | dist: { 39 | files: { 40 | src: [ 41 | 'src/lock.js' 42 | ] 43 | } 44 | } 45 | }, 46 | 47 | uglify: { 48 | options: { 49 | banner: '/*! ReadWriteLock - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + 50 | ' * Author: Alberto La Rocca (https://github.com/71104)\n' + 51 | ' * Released under the MIT license\n' + 52 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> Alberto La Rocca */\n' 53 | }, 54 | dist: { 55 | files: { 56 | 'lib/lock.js': [ 57 | 'src/lock.js' 58 | ] 59 | } 60 | } 61 | }, 62 | 63 | nodeunit: { 64 | dist: { 65 | files: { 66 | src: [ 67 | 'test/all.js' 68 | ] 69 | } 70 | } 71 | }, 72 | 73 | yuidoc: { 74 | compile: { 75 | name: 'ReadWriteLock', 76 | description: '<%= pkg.description %>', 77 | version: '<%= pkg.version %>', 78 | url: '<%= pkg.homepage %>', 79 | options: { 80 | paths: 'src/', 81 | outdir: 'doc/', 82 | linkNatives: 'true' 83 | } 84 | } 85 | } 86 | }); 87 | 88 | grunt.loadNpmTasks('grunt-contrib-clean'); 89 | grunt.loadNpmTasks('grunt-contrib-jshint'); 90 | grunt.loadNpmTasks('grunt-contrib-uglify'); 91 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 92 | grunt.loadNpmTasks('grunt-contrib-yuidoc'); 93 | 94 | grunt.registerTask('default', ['jshint', 'uglify']); 95 | grunt.registerTask('test', ['jshint', 'uglify', 'nodeunit']); 96 | grunt.registerTask('all', ['clean', 'jshint', 'uglify', 'nodeunit', 'yuidoc']); 97 | }; 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Alberto La Rocca 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rwlock 2 | ====== 3 | 4 | Asynchronous read/write lock implementation for Node.js. 5 | 6 | Main rules: 7 | - there may be zero or more readers at a time, 8 | - there may be only one writer at a time, 9 | - there may be no writer if there are one or more readers already. 10 | 11 | Installation 12 | ------------ 13 | 14 | It's on [npmjs](https://npmjs.org/package/rwlock): 15 | 16 | ```bash 17 | $ npm install rwlock 18 | ``` 19 | 20 | Basic usage 21 | ----------- 22 | 23 | Requiring the package, creating an instance: 24 | 25 | ```javascript 26 | var ReadWriteLock = require('rwlock'); 27 | 28 | var lock = new ReadWriteLock(); 29 | ``` 30 | 31 | Acquiring a read lock: 32 | 33 | ```javascript 34 | lock.readLock(function (release) { 35 | // do stuff 36 | 37 | release(); 38 | }); 39 | ``` 40 | 41 | Acquiring a write lock: 42 | 43 | ```javascript 44 | lock.writeLock(function (release) { 45 | // do stuff 46 | 47 | release(); 48 | }); 49 | ``` 50 | 51 | Locks can be released later: 52 | 53 | ```javascript 54 | lock.readLock(function (release) { 55 | // not ready to release yet 56 | 57 | setTimeout(function () { 58 | // ok, now I'm ready 59 | release(); 60 | }, 1000); 61 | }); 62 | ``` 63 | 64 | Upgrading to a write lock 65 | ------------------------- 66 | 67 | ReadWriteLock does not explicitly support upgrading but you can take advantage of the asynchronous-ness: 68 | 69 | ```javascript 70 | lock.readLock(function (release) { 71 | // read stuff here 72 | 73 | // ok, I now realize I need to write 74 | 75 | // this will be queued 76 | lock.writeLock(function (release) { 77 | // you can write here 78 | 79 | release(); 80 | 81 | // everything is now released. 82 | }); 83 | 84 | // release the read lock, this will activate the writer 85 | release(); 86 | }); 87 | ``` 88 | 89 | Downgrading to a read lock 90 | -------------------------- 91 | 92 | Similar to upgrading: 93 | 94 | ```javascript 95 | lock.writeLock(function (release) { 96 | lock.readLock(function (release) { 97 | // ... 98 | release(); 99 | }); 100 | release(); 101 | }); 102 | ``` 103 | 104 | Keys 105 | ---- 106 | 107 | Every ReadWriteLock instance allows you to work on a virtually unlimited number of completely independent read/write locks. 108 | 109 | Locks are identified by names called "keys". Every exposed method has an optional "key" first argument indicating the lock to work on; if you don't specify a key, the default lock is used. 110 | 111 | Example: 112 | 113 | ```javascript 114 | lock.writeLock('lock1', function (release) { 115 | console.log('writing 1...'); 116 | lock.writeLock('lock2', function (release) { 117 | console.log('writing 2...'); 118 | release(); 119 | console.log('done 2.'); 120 | }); 121 | release(); 122 | console.log('done 1.'); 123 | }); 124 | ``` 125 | 126 | The previous example logs: 127 | 128 | ``` 129 | writing 1... 130 | writing 2... 131 | done 2. 132 | done 1. 133 | ``` 134 | 135 | [async](https://npmjs.org/package/async) compatibility 136 | ------------------------------------------------------ 137 | 138 | The ReadWriteLock class does not return errors to your callbacks, but many APIs in Node do. The `async` module uses that as a convention: callbacks usually receive two arguments, a possibly `null` error object and the actual result in case there is no error. 139 | 140 | To aid `async` compatibility, ReadWriteLock sends `null` errors if you specify the `async` flag like in the following example: 141 | 142 | ```javascript 143 | lock.async.readLock(function (error, release) { 144 | // no need to check on error, it will always be null 145 | 146 | // do stuff here 147 | 148 | release(); 149 | }); 150 | ``` 151 | 152 | You can use `rwlock` and `async` together like in this example: 153 | 154 | ```javascript 155 | var releaseLock = null; 156 | 157 | async.waterfall([function (next) { 158 | lock.async.writeLock(next); 159 | }, function (release, next) { 160 | releaseLock = release; 161 | fs.writeFile('file', 'content', next); 162 | }, function (next) { 163 | releaseLock(); 164 | next(null); 165 | }], function (error) { 166 | if (error) { 167 | if (releaseLock) { 168 | releaseLock(); 169 | } 170 | console.dir(error); 171 | } else { 172 | console.log('done.'); 173 | } 174 | }); 175 | ``` 176 | 177 | Building from source and testing 178 | -------------------------------- 179 | 180 | You don't need this, but in case you want: 181 | 182 | ```bash 183 | $ sudo npm install -g grunt-cli 184 | $ cd 185 | $ git clone https://github.com/71104/rwlock.git 186 | $ cd rwlock 187 | $ npm install 188 | $ grunt all 189 | ``` 190 | 191 | The following folders will be generated: 192 | - **lib**, containing the minified ReadWriteLock class to `require` in Node.js; 193 | - **doc**, containing the API reference documentation in HTML format. 194 | 195 | License 196 | ------- 197 | 198 | MIT. Copyright 2015 Alberto La Rocca 199 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rwlock", 3 | "version": "5.0.0", 4 | "description": "A read/write lock implementation for Node.", 5 | "homepage": "http://71104.github.io/rwlock", 6 | "bugs": "https://github.com/71104/rwlock/issues", 7 | "license": "MIT", 8 | "author": "Alberto La Rocca ", 9 | "contributors": [ 10 | "Alberto La Rocca ", 11 | "Christian Lerrahn " 12 | ], 13 | "files": [ 14 | "lib", 15 | "LICENSE", 16 | "README.md" 17 | ], 18 | "main": "lib/lock.js", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/71104/rwlock.git" 22 | }, 23 | "devDependencies": { 24 | "grunt": "~0.4.2", 25 | "grunt-contrib-clean": "~0.5.0", 26 | "grunt-contrib-jshint": "~0.7.2", 27 | "grunt-contrib-uglify": "~0.2.7", 28 | "grunt-contrib-yuidoc": "~0.5.0", 29 | "grunt-contrib-nodeunit": "~0.2.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronous read/write lock implementation for Node. 3 | * 4 | * The rules: 5 | * - there may be zero or more readers at the same time, 6 | * - there may be only one writer at a time, 7 | * - if there is a writer there may be no readers. 8 | * 9 | * ReadWriteLock also supports multiple independent locks identified by custom 10 | * user-defined strings called "keys". 11 | * 12 | * @class ReadWriteLock 13 | * @constructor 14 | */ 15 | module.exports = function () { 16 | 'use strict'; 17 | 18 | function Lock() { 19 | this.readers = 0; 20 | this.queue = []; 21 | } 22 | 23 | var defaultLock = new Lock(); 24 | var table = {}; 25 | 26 | /** 27 | * Acquires a read lock and invokes a user-defined callback as soon as it is 28 | * acquired. 29 | * 30 | * The operation might require some time as there may be a writer. You can 31 | * optionally specify a timeout in milliseconds: if it expires before a read 32 | * lock can be acquired, this request is canceled and no lock will be 33 | * acquired. 34 | * 35 | * The `key` argument allows you to work on a specific lock; omitting it 36 | * will request the default lock. 37 | * 38 | * @method readLock 39 | * @param [key] {String} The name of the lock to read-acquire. The default 40 | * lock will be requested if no key is specified. 41 | * @param callback {Function} A user-defined function invoked as soon as a 42 | * read lock is acquired. 43 | * @param callback.release {Function} A function that releases the lock. 44 | * 45 | * This must be called by the ReadWriteLock user at some point, otherwise 46 | * the read lock will remain and prevent any writers from operating. Anyway 47 | * you do not necessarily need to call it inside the `callback` function: 48 | * you can save a reference to the `release` function and call it later. 49 | * @param [options] {Object} Further optional settings. 50 | * @param [options.scope] {Object} An optional object to use as `this` when 51 | * calling the `callback` function. 52 | * @param [options.timeout] {Number} A timeout in milliseconds within which 53 | * the lock must be acquired; if a writer is still operating and the timeout 54 | * expires the request is canceled and no lock is acquired. 55 | * @param [options.timeoutCallback] {Function} An optional user-defined 56 | * callback function that gets invokes in case the timeout expires before 57 | * the lock can be acquired. 58 | */ 59 | function readLock(key, callback, options) { 60 | var lock; 61 | if (typeof key !== 'function') { 62 | if (!table.hasOwnProperty(key)) { 63 | table[key] = new Lock(); 64 | } 65 | lock = table[key]; 66 | } else { 67 | options = callback; 68 | callback = key; 69 | lock = defaultLock; 70 | } 71 | if (!options) { 72 | options = {}; 73 | } 74 | var scope = null; 75 | if (options.hasOwnProperty('scope')) { 76 | scope = options.scope; 77 | } 78 | var release = (function () { 79 | var released = false; 80 | return function () { 81 | if (!released) { 82 | released = true; 83 | lock.readers--; 84 | if (lock.queue.length) { 85 | lock.queue[0](); 86 | } 87 | } 88 | }; 89 | }()); 90 | if ((lock.readers < 0) || lock.queue.length) { 91 | var terminated = false; 92 | lock.queue.push(function () { 93 | if (!terminated && (lock.readers >= 0)) { 94 | terminated = true; 95 | lock.queue.shift(); 96 | lock.readers++; 97 | callback.call(scope, release); 98 | if (lock.queue.length) { 99 | lock.queue[0](); 100 | } 101 | } 102 | }); 103 | if (options.hasOwnProperty('timeout')) { 104 | var timeoutCallback = null; 105 | if (options.hasOwnProperty('timeoutCallback')) { 106 | timeoutCallback = options.timeoutCallback; 107 | } 108 | setTimeout(function () { 109 | if (!terminated) { 110 | terminated = true; 111 | lock.queue.shift(); 112 | if (timeoutCallback) { 113 | timeoutCallback.call(options.scope); 114 | } 115 | } 116 | }, options.timeout); 117 | } 118 | } else { 119 | lock.readers++; 120 | callback.call(options.scope, release); 121 | } 122 | } 123 | 124 | /** 125 | * Acquires a write lock and invokes a user-defined callback as soon as it 126 | * is acquired. 127 | * 128 | * The operation might require some time as there may be one or more 129 | * readers. You can optionally specify a timeout in milliseconds: if it 130 | * expires before a read lock can be acquired, this request is canceled and 131 | * no lock will be acquired. 132 | * 133 | * The `key` argument allows you to work on a specific lock; omitting it 134 | * will request the default lock. 135 | * 136 | * @method writeLock 137 | * @param [key] {String} The name of the lock to write-acquire. The default 138 | * lock will be requested if no key is specified. 139 | * @param callback {Function} A user-defined function invoked as soon as a 140 | * write lock is acquired. 141 | * @param callback.release {Function} A function that releases the lock. 142 | * 143 | * This must be called by the ReadWriteLock user at some point, otherwise 144 | * the write lock will remain and prevent future readers from operating. 145 | * Anyway you do not necessarily need to call it inside the `callback` 146 | * function: you can save a reference to the `release` function and call it 147 | * later. 148 | * @param [options] {Object} Further optional settings. 149 | * @param [options.scope] {Object} An optional object to use as `this` when 150 | * calling the `callback` function. 151 | * @param [options.timeout] {Number} A timeout in milliseconds within which 152 | * the lock must be acquired; if one or more readers are still operating 153 | * and the timeout expires the request is canceled and no lock is acquired. 154 | * @param [options.timeoutCallback] {Function} An optional user-defined 155 | * callback function that gets invokes in case the timeout expires before 156 | * the lock can be acquired. 157 | */ 158 | function writeLock(key, callback, options) { 159 | var lock; 160 | if (typeof key !== 'function') { 161 | if (!table.hasOwnProperty(key)) { 162 | table[key] = new Lock(); 163 | } 164 | lock = table[key]; 165 | } else { 166 | options = callback; 167 | callback = key; 168 | lock = defaultLock; 169 | } 170 | if (!options) { 171 | options = {}; 172 | } 173 | var scope = null; 174 | if (options.hasOwnProperty('scope')) { 175 | scope = options.scope; 176 | } 177 | var release = (function () { 178 | var released = false; 179 | return function () { 180 | if (!released) { 181 | released = true; 182 | lock.readers = 0; 183 | if (lock.queue.length) { 184 | lock.queue[0](); 185 | } 186 | } 187 | }; 188 | }()); 189 | if (lock.readers || lock.queue.length) { 190 | var terminated = false; 191 | lock.queue.push(function () { 192 | if (!terminated && !lock.readers) { 193 | terminated = true; 194 | lock.queue.shift(); 195 | lock.readers = -1; 196 | callback.call(options.scope, release); 197 | } 198 | }); 199 | if (options.hasOwnProperty('timeout')) { 200 | var timeoutCallback = null; 201 | if (options.hasOwnProperty('timeoutCallback')) { 202 | timeoutCallback = options.timeoutCallback; 203 | } 204 | setTimeout(function () { 205 | if (!terminated) { 206 | terminated = true; 207 | lock.queue.shift(); 208 | if (timeoutCallback) { 209 | timeoutCallback.call(scope); 210 | } 211 | } 212 | }, options.timeout); 213 | } 214 | } else { 215 | lock.readers = -1; 216 | callback.call(options.scope, release); 217 | } 218 | } 219 | 220 | this.readLock = readLock; 221 | this.writeLock = writeLock; 222 | 223 | this.async = { 224 | /** 225 | * TODO 226 | * 227 | * @method async.readLock 228 | * @param [key] {String} The name of the lock to read-acquire. The 229 | * default lock will be requested if no key is specified. 230 | * @param callback {Function} A user-defined function invoked as soon as 231 | * a read lock is acquired. 232 | * @param callback.release {Function} A function that releases the lock. 233 | * 234 | * This must be called by the ReadWriteLock user at some point, 235 | * otherwise the read lock will remain and prevent any writers from 236 | * operating. Anyway you do not necessarily need to call it inside the 237 | * `callback` function: you can save a reference to the `release` 238 | * function and call it later. 239 | * @param [options] {Object} Further optional settings. 240 | * @param [options.scope] {Object} An optional object to use as `this` 241 | * when calling the `callback` function. 242 | * @param [options.timeout] {Number} A timeout in milliseconds within 243 | * which the lock must be acquired; if a writer is still operating and 244 | * the timeout expires the request is canceled and no lock is acquired. 245 | * @param [options.timeoutCallback] {Function} An optional user-defined 246 | * callback function that gets invokes in case the timeout expires 247 | * before the lock can be acquired. 248 | */ 249 | readLock: function (key, callback, options) { 250 | if (typeof key !== 'function') { 251 | readLock(key, function (release) { 252 | callback.call(this, null, release); 253 | }, options); 254 | } else { 255 | callback = key; 256 | options = callback; 257 | readLock(function (release) { 258 | callback.call(this, null, release); 259 | }, options); 260 | } 261 | }, 262 | 263 | /** 264 | * TODO 265 | * 266 | * @method async.writeLock 267 | * @param [key] {String} The name of the lock to write-acquire. The 268 | * default lock will be requested if no key is specified. 269 | * @param callback {Function} A user-defined function invoked as soon as 270 | * a write lock is acquired. 271 | * @param callback.release {Function} A function that releases the lock. 272 | * 273 | * This must be called by the ReadWriteLock user at some point, 274 | * otherwise the write lock will remain and prevent future readers from 275 | * operating. Anyway you do not necessarily need to call it inside the 276 | * `callback` function: you can save a reference to the `release` 277 | * function and call it later. 278 | * @param [options] {Object} Further optional settings. 279 | * @param [options.scope] {Object} An optional object to use as `this` 280 | * when calling the `callback` function. 281 | * @param [options.timeout] {Number} A timeout in milliseconds within 282 | * which the lock must be acquired; if one or more readers are still 283 | * operating and the timeout expires the request is canceled and no lock 284 | * is acquired. 285 | * @param [options.timeoutCallback] {Function} An optional user-defined 286 | * callback function that gets invokes in case the timeout expires 287 | * before the lock can be acquired. 288 | */ 289 | writeLock: function (key, callback, options) { 290 | if (typeof key !== 'function') { 291 | writeLock(key, function (release) { 292 | callback.call(this, null, release); 293 | }, options); 294 | } else { 295 | callback = key; 296 | options = callback; 297 | writeLock(function (release) { 298 | callback.call(this, null, release); 299 | }, options); 300 | } 301 | } 302 | }; 303 | }; 304 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | var ReadWriteLock = require('../lib/lock.js'); 2 | 3 | module.exports.oneReaderOperates = function (test) { 4 | var lock = new ReadWriteLock(); 5 | lock.readLock(function (release) { 6 | release(); 7 | test.ok(true); 8 | test.done(); 9 | }); 10 | }; 11 | 12 | module.exports.twoReadersOperate = function (test) { 13 | var lock = new ReadWriteLock(); 14 | var count = 0; 15 | lock.readLock(function (release) { 16 | test.ok(true); 17 | release(); 18 | if (++count > 1) { 19 | test.done(); 20 | } 21 | }); 22 | lock.readLock(function (release) { 23 | test.ok(true); 24 | release(); 25 | if (++count > 1) { 26 | test.done(); 27 | } 28 | }); 29 | }; 30 | 31 | module.exports.oneWriterOperates = function (test) { 32 | var lock = new ReadWriteLock(); 33 | lock.writeLock(function (release) { 34 | release(); 35 | test.ok(true); 36 | test.done(); 37 | }); 38 | }; 39 | 40 | module.exports.twoWritersOperate = function (test) { 41 | var lock = new ReadWriteLock(); 42 | var count = 0; 43 | lock.writeLock(function (release) { 44 | test.ok(true); 45 | release(); 46 | if (++count > 1) { 47 | test.done(); 48 | } 49 | }); 50 | lock.writeLock(function (release) { 51 | test.ok(true); 52 | release(); 53 | if (++count > 1) { 54 | test.done(); 55 | } 56 | }); 57 | }; 58 | 59 | module.exports.twoReaders = function (test) { 60 | var lock = new ReadWriteLock(); 61 | lock.readLock(function (release1) { 62 | var released = false; 63 | lock.readLock(function (release2) { 64 | test.ok(!released); 65 | release2(); 66 | test.done(); 67 | }); 68 | released = true; 69 | release1(); 70 | }); 71 | }; 72 | 73 | module.exports.twoWriters = function (test) { 74 | var lock = new ReadWriteLock(); 75 | lock.writeLock(function (release1) { 76 | var released = false; 77 | lock.writeLock(function (release2) { 78 | test.ok(released); 79 | release2(); 80 | test.done(); 81 | }); 82 | released = true; 83 | release1(); 84 | }); 85 | }; 86 | 87 | module.exports.oneReaderOneWriter = function (test) { 88 | var lock = new ReadWriteLock(); 89 | lock.readLock(function (release1) { 90 | var released = false; 91 | lock.writeLock(function (release2) { 92 | test.ok(released); 93 | release2(); 94 | test.done(); 95 | }); 96 | released = true; 97 | release1(); 98 | }); 99 | }; 100 | 101 | module.exports.oneWriterOneReader = function (test) { 102 | var lock = new ReadWriteLock(); 103 | lock.writeLock(function (release1) { 104 | var released = false; 105 | lock.readLock(function (release2) { 106 | test.ok(released); 107 | release2(); 108 | test.done(); 109 | }); 110 | released = true; 111 | release1(); 112 | }); 113 | }; 114 | 115 | module.exports.twoReadersOneWriter = function (test) { 116 | var lock = new ReadWriteLock(); 117 | lock.readLock(function (release1) { 118 | var released = 0; 119 | lock.readLock(function (release2) { 120 | lock.writeLock(function (release3) { 121 | test.equal(released, 2); 122 | release3(); 123 | test.done(); 124 | }); 125 | released++; 126 | release2(); 127 | }); 128 | released++; 129 | release1(); 130 | }); 131 | }; 132 | 133 | module.exports.lateReaderRelease = function (test) { 134 | var lock = new ReadWriteLock(); 135 | lock.readLock(function (release) { 136 | var released = false; 137 | lock.writeLock(function (release) { 138 | test.ok(released); 139 | release(); 140 | test.done(); 141 | }); 142 | setTimeout(function () { 143 | released = true; 144 | release(); 145 | }, 0); 146 | }); 147 | }; 148 | 149 | module.exports.lateWriterRelease = function (test) { 150 | var lock = new ReadWriteLock(); 151 | lock.writeLock(function (release) { 152 | var released = false; 153 | lock.readLock(function (release) { 154 | test.ok(released); 155 | release(); 156 | test.done(); 157 | }); 158 | setTimeout(function () { 159 | released = true; 160 | release(); 161 | }, 0); 162 | }); 163 | }; 164 | 165 | module.exports.lateWriterReleaseAgainstAnotherWriter = function (test) { 166 | var lock = new ReadWriteLock(); 167 | lock.writeLock(function (release) { 168 | var released = false; 169 | lock.writeLock(function (release) { 170 | test.ok(released); 171 | release(); 172 | test.done(); 173 | }); 174 | setTimeout(function () { 175 | released = true; 176 | release(); 177 | }, 0); 178 | }); 179 | }; 180 | 181 | module.exports.noReaderTimeout = function (test) { 182 | var lock = new ReadWriteLock(); 183 | lock.readLock(function (release) { 184 | lock.readLock(function (release) { 185 | test.ok(true); 186 | release(); 187 | test.done(); 188 | }, { 189 | timeout: 0, 190 | timeoutCallback: function () { 191 | test.ok(false); 192 | test.done(); 193 | } 194 | }); 195 | setTimeout(release, 10); 196 | }); 197 | }; 198 | 199 | module.exports.writerTimeout = function (test) { 200 | var lock = new ReadWriteLock(); 201 | lock.readLock(function (release) { 202 | lock.writeLock(function (release) { 203 | test.ok(false); 204 | test.done(); 205 | }, { 206 | timeout: 0, 207 | timeoutCallback: function () { 208 | test.ok(true); 209 | test.done(); 210 | } 211 | }); 212 | setTimeout(release, 10); 213 | }); 214 | }; 215 | 216 | module.exports.readerTimeout = function (test) { 217 | var lock = new ReadWriteLock(); 218 | lock.writeLock(function (release) { 219 | lock.readLock(function (release) { 220 | test.ok(false); 221 | test.done(); 222 | }, { 223 | timeout: 0, 224 | timeoutCallback: function () { 225 | test.ok(true); 226 | test.done(); 227 | } 228 | }); 229 | setTimeout(release, 10); 230 | }); 231 | }; 232 | 233 | module.exports.readerTimeout = function (test) { 234 | var lock = new ReadWriteLock(); 235 | lock.writeLock(function (release) { 236 | lock.readLock(function (release) { 237 | test.ok(false); 238 | test.done(); 239 | }, { 240 | timeout: 0 241 | }); 242 | lock.readLock(function (release) { 243 | test.ok(true); 244 | test.done(); 245 | }, { 246 | timeout: 20, 247 | timeoutCallback: function () { 248 | test.ok(false); 249 | test.done(); 250 | } 251 | }); 252 | setTimeout(release, 10); 253 | }); 254 | }; 255 | 256 | module.exports.writerTimeoutAgainstAnotherWriter = function (test) { 257 | var lock = new ReadWriteLock(); 258 | lock.writeLock(function (release) { 259 | lock.writeLock(function (release) { 260 | test.ok(false); 261 | test.done(); 262 | }, { 263 | timeout: 0, 264 | timeoutCallback: function () { 265 | test.ok(true); 266 | test.done(); 267 | } 268 | }); 269 | setTimeout(release, 10); 270 | }); 271 | }; 272 | 273 | module.exports.writerTimeoutAgainstAnotherWriterNewWriter = function (test) { 274 | var lock = new ReadWriteLock(); 275 | lock.writeLock(function (release) { 276 | lock.writeLock(function (release) { 277 | test.ok(false); 278 | test.done(); 279 | }, { 280 | timeout: 0 281 | }); 282 | lock.writeLock(function (release) { 283 | test.ok(true); 284 | test.done(); 285 | }, { 286 | timeout: 20, 287 | timeoutCallback: function() { 288 | test.ok(false); 289 | test.done(); 290 | } 291 | }); 292 | setTimeout(release, 10); 293 | }); 294 | }; 295 | 296 | module.exports.avoidWriterTimeout = function (test) { 297 | var lock = new ReadWriteLock(); 298 | lock.readLock(function (release) { 299 | lock.writeLock(function (release) { 300 | test.ok(true); 301 | release(); 302 | test.done(); 303 | }, { 304 | timeout: 10, 305 | timeoutCallback: function () { 306 | test.ok(false); 307 | test.done(); 308 | } 309 | }); 310 | setTimeout(release, 0); 311 | }); 312 | }; 313 | 314 | module.exports.avoidReaderTimeout = function (test) { 315 | var lock = new ReadWriteLock(); 316 | lock.writeLock(function (release) { 317 | lock.readLock(function (release) { 318 | test.ok(true); 319 | release(); 320 | test.done(); 321 | }, { 322 | timeout: 10, 323 | timeoutCallback: function () { 324 | test.ok(false); 325 | test.done(); 326 | } 327 | }); 328 | setTimeout(release, 0); 329 | }); 330 | }; 331 | 332 | module.exports.avoidWriterTimeoutAgainstAnotherWriter = function (test) { 333 | var lock = new ReadWriteLock(); 334 | lock.writeLock(function (release) { 335 | lock.writeLock(function (release) { 336 | test.ok(true); 337 | release(); 338 | test.done(); 339 | }, { 340 | timeout: 10, 341 | timeoutCallback: function () { 342 | test.ok(false); 343 | test.done(); 344 | } 345 | }); 346 | setTimeout(release, 0); 347 | }); 348 | }; 349 | --------------------------------------------------------------------------------