ydn.db
Version:
Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.
330 lines (267 loc) • 7.43 kB
JavaScript
// Copyright 2012 YDN Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Transaction mutex.
*
* Record transaction mutex and execution status.
*/
goog.provide('ydn.db.tr.Mutex');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('ydn.db.InvalidStateError');
/**
* Create transaction mutex.
* @param {number} tr_no track number.
* @constructor
* @const
*/
ydn.db.tr.Mutex = function(tr_no) {
this.tr_no = tr_no;
this.tx_ = null;
/**
* Transaction counter.
* @type {number}
* @private
*/
this.tx_count_ = 0;
};
/**
* @type {ydn.db.base.Transaction}
* @private
*/
ydn.db.tr.Mutex.prototype.tx_;
/**
* @protected
* @type {goog.debug.Logger} logger.
*/
ydn.db.tr.Mutex.prototype.logger =
goog.log.getLogger('ydn.db.tr.Mutex');
/**
* @const
* @type {boolean}
*/
ydn.db.tr.Mutex.DEBUG = false;
/**
* Newly created transaction it push to mutex and lock.
* @final
* @param {ydn.db.base.Transaction} tx the transaction object.
* @param {Array.<string>} store_names scope store name.
* @param {ydn.db.base.TransactionMode} mode tx mode.
*/
ydn.db.tr.Mutex.prototype.up = function(tx, store_names, mode) {
if (ydn.db.tr.Mutex.DEBUG) {
goog.global.console.log(this + ': up');
}
// In compiled code, it is permissible to overlap transaction,
// rather than cause error.
goog.asserts.assert(!this.tx_, this + ': transaction overlap');
this.tx_ = tx;
this.is_locked_ = false;
/**
*
* @type {boolean}
* @private
*/
this.out_of_scope_ = false;
this.store_names = store_names;
this.mode = mode;
this.tx_count_++;
this.oncompleted = null;
};
/**
* @protected
* @type {Array.<string>}
*/
ydn.db.tr.Mutex.prototype.store_names = null;
/**
* @protected
* @type {?ydn.db.base.TransactionMode}
*/
ydn.db.tr.Mutex.prototype.mode;
/**
*
* @param {Array.<string>} store_names
* @param {ydn.db.base.TransactionMode?} mode
* @return {boolean}
*/
ydn.db.tr.Mutex.prototype.sameScope = function(store_names, mode) {
if (!this.store_names || !this.mode) {
return false;
}
if (mode != this.mode) {
return false;
}
if (this.store_names.length != store_names.length) {
return false;
}
for (var i = 0; i < store_names.length; i++) {
if (this.store_names.indexOf(store_names[i]) == -1) {
return false;
}
}
return true;
};
/**
*
* @param {Array.<string>} store_names
* @param {ydn.db.base.TransactionMode} mode
* @return {boolean}
*/
ydn.db.tr.Mutex.prototype.subScope = function(store_names, mode) {
if (!this.store_names || !this.mode) {
return false;
}
if (mode != this.mode) {
if (this.mode != ydn.db.base.TransactionMode.READ_WRITE ||
mode != ydn.db.base.TransactionMode.READ_ONLY) {
return false;
}
}
if (store_names.length > this.store_names.length) {
return false;
}
for (var i = 0; i < store_names.length; i++) {
if (this.store_names.indexOf(store_names[i]) == -1) {
return false;
}
}
return true;
};
/**
* Transaction is released and mutex is unlock.
* @final
* @param {ydn.db.base.TxEventTypes} type event type.
* @param {*} event event.
*/
ydn.db.tr.Mutex.prototype.down = function(type, event) {
//goog.asserts.assertObject(this.tx_, 'mutex already unlocked');
if (this.tx_) {
if (ydn.db.tr.Mutex.DEBUG) {
goog.global.console.log(this + ': down');
}
// down must be call only once by those who up
this.tx_ = null;
this.store_names = null;
this.mode = null;
if (goog.isFunction(this.oncompleted)) {
this.oncompleted(type, event);
}
this.oncompleted = null;
} else {
goog.log.warning(this.logger, this + ' has no TX to be unlocked for ' + type);
}
};
/**
* Transaction callback function is out of scope. We no longer accepting adding
* listeners.
*/
ydn.db.tr.Mutex.prototype.out = function() {
this.out_of_scope_ = true;
// interestingly tx_ can still be use even after out of scope from the
// transaction callback. This is the whole reason we are
// having this class. Otherwise, transaction scope handling
// will be very simple.
};
/**
* True if call while in transaction callback scope. Transaction callback
* is out of scope when a request is returning a result on success or error
* callback.
* @return {boolean} return true if call while in transaction callback scope.
*/
ydn.db.tr.Mutex.prototype.inScope = function() {
return !this.out_of_scope_;
};
/**
* Transaction is explicitly set not to do next transaction.
*/
ydn.db.tr.Mutex.prototype.lock = function() {
if (ydn.db.tr.Mutex.DEBUG) {
goog.global.console.log(this + ': locked');
}
this.is_locked_ = true;
};
/**
* Get number of transaction count.
* @final
* @return {number} transaction count.
*/
ydn.db.tr.Mutex.prototype.getTxCount = function() {
return this.tx_count_;
};
/**
*
* @return {boolean} get done flag.
*/
ydn.db.tr.Mutex.prototype.isLocked = function() {
return this.is_locked_;
};
/**
* Transaction object is active.
* @final
* @return {boolean} true if transaction is active.
*/
ydn.db.tr.Mutex.prototype.isActive = function() {
return !!this.tx_;
};
/**
* Transaction object is available.
* @final
* @return {boolean} true if the transaction is available.
*/
ydn.db.tr.Mutex.prototype.isAvailable = function() {
return !this.is_locked_;
};
/**
* Transaction object is active and not done.
* @final
* @return {boolean} true if transaction is active and available.
*/
ydn.db.tr.Mutex.prototype.isActiveAndAvailable = function() {
return this.isActive() && this.isAvailable();
};
/**
* Add a transaction complete (also error and abort) event. The listener will
* be invoked after receiving one of these three events and before executing
* next transaction. However, it is recommended that listener is not used
* for transaction logistic tracking, which should, in fact, be tracked request
* level. Use this listener to release resource for robustness. Any error on
* the listener will be swallowed.
* @type {?function(string=, *=)} fn first argument is either 'complete',
* 'error', or 'abort' and second argument is event.
*/
ydn.db.tr.Mutex.prototype.oncompleted = null;
/**
* Return current active transaction if available. Transaction consumer must
* check {@link #isActiveAndAvailable} if this transaction object
* should be used.
* @return {ydn.db.base.Transaction} transaction
* object.
*/
ydn.db.tr.Mutex.prototype.getTx = function() {
return this.tx_;
};
/**
*
* @return {string} label.
*/
ydn.db.tr.Mutex.prototype.getLabel = function() {
return 'B' + this.tr_no + 'T' + this.tx_count_;
};
if (goog.DEBUG) {
/** @override */
ydn.db.tr.Mutex.prototype.toString = function() {
var s = !!this.tx_ ? '*' : '';
return 'Mutex:' + this.getLabel() + s;
};
}