Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
replyMsg = _nextSink.SyncProcessMessage(reqMsg);
if (bTopLevel) {
_property.SyncCallOutLCID = null;
if (bClear) {
LogicalCallContext cctxRet =
(LogicalCallContext)
replyMsg.Properties[Message.CallContextKey];
cctxRet.RemotingData.LogicalCalllD = null;
}
}
}
return replyMsg;
}
В нереентерабельном случае для предотвращения блокировок надо заботиться о первоочередном выполнении вновь поступающих вызовов,
Прежде всего нужно узнать идентификатор исходящего вызова. Для этого получаем доступ к его контексту вызова
LogicalCallContext cctx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
и, затем, к самому идентификатору
String lcid = cctx.RemotingData.LogicalCallID;
Может оказаться, что в данный момент исходящий вызов (reqMsg) еще не имеет идентификатора. В таком случае присвоим ему новый еще не использованный идентификатор
bool bClear = false;
if (lcid == null) {
lcid = Identity.GetNewLogicalCalllD;
cctx.RemotingData.LogicalCalllD = lcid;
bClear = true;
}
Здесь статический метод GetNewLogicalCallID класса Identity генерирует новый идентификатор, который и запоминается в контексте вызова.
Если нам пришлось самим построить новый идентификатор для исходящего синхронного вызова, то его нужно и сохранить в свойстве синхронизации. В этом случае условное выражение в следующем операторе if равно true:
bool bTopLevel=false;
if (_property.SyncCallOutLCID==null) {
_property.SyncCallOutLCID = lcid;
bTopLevel = true;
}
Ну а теперь передаем исходящий вызов следующему перехватчику исходящих вызовов и ждем ответа
replyMsg = _nextSink.SyncProcessMessage(reqMsg);
Ну а теперь (после получения ответа) надо за собой почистить (если мы что-то поменяли в контексте вызова и в свойстве синхронизации):
if (bTopLevel) {
_property.SyncCallOutLCID = null;
if (bClear) {
LogicalCallContext cctxRet =
(LogicalCallContext)
replyMsg.Properties[Message.CallContextKey];
cctxRet.RemotingData.LogicalCalllD = null;
}
}
Здесь мы обнуляем свойство SyncCallOutLCID свойства синхронизации (если мы его только что сами установили) и обнуляем идентификатор для полученного ответа replyMsg (если мы сами задавали этот идентификатор для исходящего вызова).
Перехват исходящих асинхронных вызовов
Для обработки исходящих асинхронных вызовов перехватчик использует следующий код:
public virtual IMessageCtrl AsyncProcessMessage(
IMessage reqMsg,
IMessageSink replySink) {
IMessageCtrl msgCtrl = null;
if (!_property.IsReEntrant) {
LogicalCallContext cctx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
String lcid = Identity.GetNewLogicalCalllD;
cctx.RemotingData.LogicalCalllD = lcid;
_property.AsyncCallOutLCIDList.Add(lcid);
}
AsyncReplySink mySink =
new AsyncReplySink(replySink, _property);
msgCtrl = _nextSink.AsyncProcessMessage (
reqMsg,
(IMessageSink)mySink);
return msgCtrl;
}
В качестве входных параметров задаются сам вызов reqMsg и перехватчик, на который вызывающая сторона ожидает получения уведомления о завершении процесса выполнения асинхронного вызова.
В случае нереентерабельного контекста исходящему асинхронному вызову назначается новый идентификатор и этот идентификатор сохраняется в списке _asyncLcidList исходящих асинхронных вызовов, поддерживаемому свойством синхронизации (доступ через свойство AsyncCallOutLCIDList). Заметим, что в случае асинхронных вызовов нет нужны сохранять один и тот же идентификатор по всей цепочке вызовов, в связи с чем здесь не проверяется наличие идентификатора, а сразу же назначается новый:
if (!_property.IsReEntrant) {
LogicalCallContext cctx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
String lcid = Identity.GetNewLogicalCallID;
cctx.RemotingData.LogicalCalllD = lcid;
_property.AsynCallOutLCIDList.Add(lcid);
}
Зачем вообще сохраняется список идентификаторов исходящих асинхронных вызовов? И почему он обновляется только для нереентерабельного случая?
Единственно, для чего этот список необходим (и именно в случае нереентерабельного контекста) — для определения того, является ли новый входящий вызов вложенным (см. код методов IsNestedCall и HandleWorkRequest). При изложении этого вопроса ранее уже отмечалось наличие некоторых проблем, связанных с определение понятия вложенного вызова и его использования.
Теперь пора отправить исходящий асинхронный вызов следующему перехватчику исходящих асинхронных вызовов. И тут мы должны указать, куда посылать уведомления о завершении вызова. Непосредственно использовать перехватчик уведомлений replySink, предоставленный вызывающей стороной, нельзя, т. к. его непосредственное использование может нарушить логику синхронизации, поддерживаемую в домене синхронизации. В связи с этим создается специальный перехватчик уведомлений mySink, который придерживается логики синхронизации и обеспечивает безопасную работу с replySink: