internal void TryMatch(AsyncMessage newMessage) { Chord matchedChord; SyncMessage primaryMessage = null; lock (lookupLock) { newMessage.AsyncMethod.Enqueue(newMessage); matchedChord = AsyncLookup(newMessage, ref primaryMessage); } if (matchedChord == null) { return; } if (matchedChord is SyncChord) { primaryMessage.WakeUp(); } else { (matchedChord as AsyncChord).ExecuteBody(newMessage.Arguments); } }
private Chord AsyncLookup(AsyncMessage newMessage, ref SyncMessage primaryMessage) { //we are inside a lookupLock foreach (Chord chord in asyncMethodToChords[newMessage.AsyncMethod]) { if (chord.Match()) { chord.Dequeue(ref primaryMessage, newMessage); if (chord is SyncChord) { //put some contextual info in the primary message primaryMessage.IsPrimary = true; primaryMessage.MatchingChord = chord as SyncChord; return(chord); } else { (chord as AsyncChord).ExecuteBody(newMessage.CollatedArguments.ToArray()); } } } return(null); }
internal abstract void Dequeue(ref SyncMessage primaryMessage, Message newMessage);
/// <summary> /// Having receveived a sync message we will try to find a match. /// /// If a match has been found and the message isn't the primary one we wait until we get a ReturnTo call /// /// If no match has been found we wait until we are wakened up. It will be either because we are the /// primary message or because we will have received a ReturnTo call. /// /// This is only relevant for sync messages as async ones cannot wait. /// /// </summary> internal object TryMatchAndWait(SyncMessage newMessage, out object[] arguments) { arguments = null; bool matchFound; SyncMessage primaryMessage; lock (lookupLock) { //TODO: the original version says not to queue this message... can't remember why... //if the arrival of this message triggers a match it will be dequeued anyway newMessage.SyncMethod.Enqueue(newMessage); matchFound = SyncLookup(out primaryMessage, newMessage); //TODO: would it be easier to have two lists of sync chords, one for single sync chords and another one for multiple ones? } //we can now leave our lock as the match and the dequeuing have been done if (matchFound) { // this message completes a chord // is newMessage the primary message? if (!newMessage.IsPrimary) { // newMessage is an returned to non-primary message // lets wake up the primary thread to tell it to do its job :) primaryMessage.WakeUp(); //TODO: non primary methods which return void don't have to return via a Return call //TODO: if no Return is called when required we must throw an exception. // and wait until we get a return call //TODO: do we want to timeout here??? newMessage.Wait(); //TODO: should we return now or when the main body has returned???? arguments = primaryMessage.OutArguments; return(newMessage.ReturnValue); } //here match found and we are primary } else { // if we have no match we must wait... // until someone signals a match (if we are primary) or until we time out bool timedOut = newMessage.WaitWithTimeout(); if (timedOut) { //there can be a gap between the timeout and a successful match //we will catch this here lock (lookupLock) { if (!newMessage.IsAwake) { //no match happened in between //remove this message from the queue newMessage.RemoveFromQueue(); //execute the code expected to be executed //TODO: deal with exceptions //TODO; could throw a timeout exception here... well, the action could do that... return(newMessage.OnTimeOut()); } } } // here we have been wakened up either because newMessage is primary // or because newMessage is non-primary and got a return call //TODO: deal with error of non primary not being wakened up by a return call } //got a match, find out whether we are the primary message or not. if (newMessage.IsPrimary) { // newMessage is an awakened or not primary message // tell all the other messages of this chord that this is the primary thread: newMessage.RegisterMessages(newMessage); var args = newMessage.CollatedArguments.ToArray(); object returnValue = newMessage.MatchingChord.ExecuteBody(args); //check that all the return to have been called... newMessage.OutArguments = args; newMessage.CleanUpReturnCalls(args); newMessage.UnregisterMessages(); arguments = args; return(returnValue); } else { // newMessage is returned to non-primary message arguments = newMessage.PrimaryMessage.OutArguments; return(newMessage.ReturnValue); } }
internal abstract void Dequeue(int count, SyncMessage primaryMessage);
internal void Enqueue(SyncMessage syncMessage) { lock (queueLock) { messageQueue.Add(syncMessage); } }
internal override void Dequeue(int count, SyncMessage primaryMessage) { Dequeue(count, primaryMessage.CollatedArguments); }