|
|
// ---------------------------------------------------------------------------
//
// Message Queue functions
//
// ---------------------------------------------------------------------------
/* Message Queue: Allow 2 script threads to send async messages to each other and receive async replies.
First thread creates the Q, second thread obtains the Q via GetMsgQueue()
Creator thread gets the "left" Q, and other thread gets "right" Q Internally it uses associative arrays as msg queues.
Methods: WaitForMsgAndDispatch(strOtherWaitEvents, fnMsgProc, nTimeout) You should call this function instead of calling WaitForSync() or WaitForMultipleSyncs(). It waits for messages from this Queue.
strOtherWaitEvents: Syncs you want to wait on. fnMsgProc: Message dispatch function. The prototype is: fnMsgProc(queue, msg); Where "queue" is the queue which received the msg, and msg is a copy of the array of arguments sent via SendMessage(). (The array is copied, not the args).
nTimeout: Same as WaitForSync(). 0 for INFINITE.
Return value is the same as WaitForMultipleSyncs().
SendMessage(strCmd, ...) Send a message to the other thread. "strCmd" and all other arguments are passed as is to the other thread.
Returns a reference to the message. Save this if you would like to wait for the message to be processed.
WaitForMsg(msg, nTimeout) Wait patiently until after the message has been processed.
msg: The msg as returned by SendMessage() nTimeout: Same as WaitForSync(). 0 for INFINITE.
*/
function MsgQueue(strName) { { this.WaitForMsgAndDispatch = QueueWaitForMsgAndDispatch; this.SendMessage = QueueSendMessage; this.GetMessage = QueueGetMessage; this.Dispatch = QueueDispatch; this.WaitForMsg = QueueWaitForMsg; this.ReplyMessage = QueueReplyMessage; this.SignalThisThread = QueueSignalThreadSync; }
//$ BUGBUG can contructors return failed? exception?
if (strName == '') return false;
this.strName = strName; this.nHighIndex = 0; this.nLowIndex = 0; this.aMsgs = new Array(); this.strReplySignal = strName.split(',')[0] + 'Reply';
if (arguments.length == 2) // Creating right-side Q for "other" side
{ this.otherQ = arguments[1]; arguments[1].otherQ = this; this.strSignalName = strName.split(',')[0] + "Right"; this.strSignalNameWait = strName.split(',')[0] + "Left";
// now, exchange signalling functions
this.SignalOtherThread = this.otherQ.SignalThisThread; this.otherQ.SignalOtherThread = this.SignalThisThread; } else { // Left Q specific initialization
this.strSignalName = strName.split(',')[0] + "Left"; this.strSignalNameWait = strName.split(',')[0] + "Right"; } return this; }
function GetMsgQueue(queue) { // sic: Add '' to the name to force a local copy of the string
var newq = new MsgQueue(queue.strName + '', queue); LogMsg('GetMsgQueue ' + newq.strName); return newq; }
function MsgPacket(nMsgIndex, aArgs) { this.nIndex = nMsgIndex; this.aArgs = new Array(); this.nReplied = false; this.vReplyValue = 'ok';
// Copy just the array elements of aArgs -- avoiding any other properties.
for(var i = 0; i < aArgs.length; ++i) this.aArgs[i] = aArgs[i]; }
// MsgQueue Member functions
function WaitForMultipleQueues(aQueues, strOtherWaitEvents, fnMsgProc, nTimeout) { var index; var strMyEvents = ''; var nEvent = 0; var msg; var SignaledQueue;
for(index = 0; index < aQueues.length; ++index) { if (strMyEvents == '') strMyEvents = aQueues[index].strSignalNameWait; else strMyEvents += ',' + aQueues[index].strSignalNameWait; }
if (strOtherWaitEvents != '') strMyEvents += ',' + strOtherWaitEvents;
do { nEvent = WaitForMultipleSyncs(strMyEvents, false, nTimeout); if (nEvent > aQueues.length) { return nEvent - aQueues.length; } if (nEvent > 0) // && nEvent <= aQueues.length)
{ SignaledQueue = aQueues[nEvent - 1]; ResetSync(SignaledQueue.strSignalNameWait); while ( (msg = SignaledQueue.GetMessage()) != null) { SignaledQueue.Dispatch(msg, fnMsgProc); msg = null; } } } while(nEvent != 0);
return nEvent; // 0 -- timeout
}
function QueueWaitForMsgAndDispatch(strOtherWaitEvents, fnMsgProc, nTimeout) { var strMyEvents = this.strSignalNameWait; if (strOtherWaitEvents != '') strMyEvents += ',' + strOtherWaitEvents;
var nEvent = 0; var msg; do { var nEvent = WaitForMultipleSyncs(strMyEvents, false, nTimeout); if (nEvent == 1) { ResetSync(this.strSignalNameWait); while ( (msg = this.GetMessage()) != null) { this.Dispatch(msg, fnMsgProc); msg = null; } } } while(nEvent == 1);
if (nEvent > 1) // adjust the event number to indicate which of their events happened
--nEvent;
return nEvent; }
// Send a message to the "other" thread
function QueueSendMessage(strCmd) { var msg = null; var n;
LogMsg(this.strName + ': Sending message ' + strCmd); try { msg = new MsgPacket(this.nHighIndex, arguments); n = this.nHighIndex++; this.aMsgs[ n ] = msg; this.SignalOtherThread(this.strSignalName); } catch(ex) { LogMsg("QueueSendMessage(" + this.strName + ") failed: " + ex); }
return msg; }
// Retrieve message sent by "other" thread
function QueueGetMessage() { var msg = null; try { LogMsg('getting message');
if (this.otherQ.nHighIndex > this.otherQ.nLowIndex) { var n = this.otherQ.nLowIndex++; msg = this.otherQ.aMsgs[ n ]; delete this.otherQ.aMsgs[ n ]; } } catch(ex) { LogMsg("QueueGetMessage(" + this.strName + " failed: " + ex); } return msg; }
function QueueDispatch(msg, fnMsgProc) { try { msg.vReplyValue = fnMsgProc(this, msg); } catch(ex) { LogMsg("Possible BUG: MessageQueue('" + this.strName + "') dispatch function threw " + ex); JAssert(g_fAssertOnDispatchException == false, "Possible BUG: MessageQueue('" + this.strName + "') dispatch function threw an exception"); msg.vReplyValue = ex; } this.ReplyMessage(msg); }
// Wait at least "nTimeout" miliseconds for a reply on the given msg.
// Returns true if the message was replied to.
function QueueWaitForMsg(msg, nTimeout) { while (!msg.nReplied) { WaitForSync(this.strReplySignal, nTimeout); ResetSync(this.strReplySignal); } return msg.nReplied; }
function QueueReplyMessage(msg) { try { msg.nReplied = true; this.SignalOtherThread(this.strReplySignal); } catch(ex) { LogMsg("(QueueReplyMessage) Oher side of Queue('" + this.strName + "') has been destroyed: " + ex); } }
// Simple wrapper function to allow any remote thread to signal
// this thread. Necessary for cross machine signalling.
function QueueSignalThreadSync(Name) { SignalThreadSync(Name); }
|