I had previously posted this article with a snippet of example code. That blog entry was accidently deleted so I am reposting an update version of it now.
What is a "Joining Mutex" and why would you need it?
var AfterAllThreadsExecute = function() { alert("Hello World");}
for(var i=0; i < records.length; i++) {
// dispatchAjaxCall(ThreadMutexHandle, recordId)
dispatchAjaxCall(JoiningMutex.contextJoinCreate('TestContext',AfterAllThreadsExecute, records[i].ID);
}
within the dispatchAjaxCall(myMutexHandle) {
// do AJAX stuff here ...
// now signal to the Mutex (via our reference handle) that this thread has finish running
myMutexHandle.ThreadFinished();
}
A listing of the initial code is below. The latest code is used within the i2b2 web client in the i2b2/hive/hive_helpers.js file and can be found at https://github.com/i2b2/i2b2-webclient/blob/master/js-i2b2/hive/hive_helpers.js
// created this object to allow the joining of forked execution paths (Waiting for Multiple AJAX calls)
JoiningMutex = {
_contexts: {},
_contextGenID: 0,
_createContextProxy: function(contextRef) {
// create a proxy object (via closure) to encapsulate data
// and route actions to the JoinMutex singleton
var cl_JoinMutextRef = contextRef;
function JoiningMutexContextProxy() {
this._JoiningMutexContext = cl_JoinMutextRef;
this._alreadyRun = false;
this.name = function() { return this._JoiningMutexContext.name; };
this.openThreads = function() { return this._JoiningMutexContext.openThreads; };
this.executeOnce = function() { return this._JoiningMutexContext.executeOnce; };
this.executionCount = function() { return this._JoiningMutexContext.executionCount; };
this.isActive = function() { return this._JoiningMutexContext.active; };
this.ThreadFinished = function() {
if (!this._JoiningMutexContext.active) {
return {error: true, errorObj: undefined, errorMsg: 'JoiningMutexProxy.ThreadFinished() failed because the giving context is no longer active'};
}
if (this._JoiningMutexContext._alreadyRun) {
return {error: true, errorObj: undefined, errorMsg: 'JoiningMutexProxy.ThreadFinished() failed because the MutexProxy has already been run'};
}
if (this._JoiningMutexContext.openThreads > 0) {
this._JoiningMutexContext.openThreads--;
this._alreadyRun = true;
if (this._JoiningMutexContext.openThreads == 0) {
// all threads finished
if (this._JoiningMutexContext.executeOnce) {
// this is going to be our only run of the callback function
this._JoiningMutexContext.active = false;
}
this._JoiningMutexContext.executionCount++;
this._JoiningMutexContext.callbackFinished();
return true;
} else {
// everything is OK but there are still outstanding threads to finish
return false;
}
} else {
return {error: true, errorObj: undefined, errorMsg: 'JoiningMutexProxy.ThreadFinished() failed because there are no outstanding thread executions'};
}
};
}
return new JoiningMutexContextProxy;
},
contextCreate: function(sContextName, fZeroRunFunction, bSingleRun) {
// make sure context is new
var validName = sContextName;
try {
if (!validName) {
this._contextGenID++;
validName = "AUTOGEN-"+this._contextGenID;
}
if (this._contexts[validName]) {
return {error: true, errorObj: undefined, errorMsg: 'JoiningMutex.contextCreate() failed because the giving context name already exists'};
}
// verify that the name can be used as an object identifier (with throw an error if invalid)
this._contexts[validName] = true;
delete this._contexts[validName];
} catch(e) {
return {error: true, errorObj: e, errorMsg: 'an error occurred within JoiningMutex.contextCreate()'};
}
// create new context object
function JoiningMutexContext(inName, inFinishFunction, inSingleExecution) {
this.name = inName;
this.callbackFinished = inFinishFunction;
this.openThreads = 0;
this.executeOnce = inSingleExecution;
this.executionCount = 0;
this.active = true;
}
var bSingleRun = Boolean.parseTo(bSingleRun);
this._contexts[validName] = new JoiningMutexContext(validName, fZeroRunFunction, bSingleRun);
// add ourselves to the thread count
var cl_JoinMutextRef = this._contexts[validName];
cl_JoinMutextRef.openThreads++;
return this._createContextProxy(cl_JoinMutextRef);
},
contextJoin: function(sContextName) {
// make sure context already exists
var validName = sContextName;
if (!this._contexts[validName]) {
return {error: true, errorObj: undefined, errorMsg: 'JoiningMutex.contextCreate() failed because the context name does not exist'};
}
var cl_JoinMutextRef = this._contexts[validName];
// Add this thread to the count
cl_JoinMutextRef.openThreads++;
return this._createContextProxy(cl_JoinMutextRef);
},
contextJoinCreate: function(sContextName, fZeroRunFunction, bSingleRun) {
// Join context or create it if it exists
var ctx = false;
if (!sContextName || !this._contexts[sContextName]) {
ctx = this.contextCreate.call(this, sContextName, fZeroRunFunction, bSingleRun);
} else {
ctx = this.contextJoin.call(this, sContextName);
}
return ctx;
},
contextDestroy: function(sContextName) {
if (!sContextName || !this._contexts[sContextName]) {
return false;
} else {
// Garbage collection will not execute until all
// the JoiningMutexContextProxy are deleted so
// invalidate the context as well as delete it!
this._contexts[sContextName].active = false;
this._contexts[sContextName].callbackFinished = function() { return null; };
delete this._contexts[sContextName];
return true;
}
}
};
No comments:
Post a Comment