if (f.matched()) {
Logger.error(this, "removed pre-matched message filter found in _filters: "+f);
i.remove();
continue;
}
MATCHED status = f.match(m, tStart);
if(status == MATCHED.TIMED_OUT || status == MATCHED.TIMED_OUT_AND_MATCHED) {
if(timedOut == null)
timedOut = new ArrayList<MessageFilter>();
timedOut.add(f);
i.remove();
continue;
} else if(status == MATCHED.MATCHED) {
matched = true;
i.remove();
match = f;
// We must setMessage() inside the lock to ensure that waitFor() sees it even if it times out.
f.setMessage(m);
if(logMINOR) Logger.minor(this, "Matched (1): "+f);
break; // Only one match permitted per message
} else if(logDEBUG) Logger.minor(this, "Did not match "+f);
}
}
if(timedOut != null) {
for(MessageFilter f : timedOut) {
if(logMINOR) Logger.minor(this, "Timed out "+f);
f.setMessage(null);
f.onTimedOut(_executor);
}
}
if(match != null) {
match.onMatched(_executor);
}
// Feed unmatched messages to the dispatcher
if ((!matched) && (_dispatcher != null)) {
try {
if(logMINOR) Logger.minor(this, "Feeding to dispatcher: "+m);
matched = _dispatcher.handleMessage(m);
} catch (Throwable t) {
Logger.error(this, "Dispatcher threw "+t, t);
}
}
if(timedOut != null) timedOut.clear();
// Keep the last few _unclaimed messages around in case the intended receiver isn't receiving yet
if (!matched) {
if(logMINOR) Logger.minor(this, "Unclaimed: "+m);
/** Check filters and then add to _unmatched is ATOMIC
* It has to be atomic, because otherwise we can get a
* race condition that results in timeouts on MFs.
*
* Specifically:
* - Thread A receives packet
* - Thread A checks filters. It doesn't match any.
* - Thread A feeds to Dispatcher.
* - Thread B creates filter.
* - Thread B checks _unmatched.
* - Thread B adds filter.
* - Thread B sleeps.
* - Thread A returns from Dispatcher. Which didn't match.
* - Thread A adds to _unmatched.
*
* OOPS!
* The only way to fix this is to have checking the
* filters and unmatched be a single atomic operation.
* Another race is possible if we merely recheck the
* filters after we return from dispatcher, for example.
*/
synchronized (_filters) {
if(logMINOR) Logger.minor(this, "Rechecking filters and adding message");
for (ListIterator<MessageFilter> i = _filters.listIterator(); i.hasNext();) {
MessageFilter f = i.next();
MATCHED status = f.match(m, tStart);
if(status == MATCHED.MATCHED) {
matched = true;
match = f;
i.remove();
if(logMINOR) Logger.minor(this, "Matched (2): "+f);