You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.0 KiB
273 lines
8.0 KiB
// ---------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
}
|
|
|