Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
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:
AsyncReplySink mySink =
new AsyncReplySink(replySink, _property);
Класс AsyncReplySink будет рассмотрен чуть позже.
И вот, наконец, исходящий асинхронный вызов reqMsg передается следующему перехватчику исходящих асинхронных вызовов, а для получения уведомления указывается mySink:
msgCtrl = _nextSink.AsyncProcessMessage (
reqMsg,
(IMessageSink)mySink);
return msgCtrl;
Теперь рассмотрим класс AsyncReplySink:
internal class AsyncReplySink: IMessageSink {
…..
}
В конструкторе в полях _nextSink и _property сохраняются соответственно ссылка
на следующий перехватчик (в нашем случае это будет replySink) и ссылка на свойство синхронизацииinternal AsyncReplySink(IMessageSink nextsink,
SynchronizationAttribute prop) {
_nextSink = nextSink;
_property = prop;
}
Как и в любом перехватчике, основными методами являются SyncProcessMessage и AsyncProcessMessage.
Вот код для обработки уведомлений, полученных в виде синхронного вызова:
public virtual IMessage SyncProcessMessage (
IMessage reqMsg) {
Workltem work = new WorkItem (
reqMsg,
_nextSink,
null);
_property.HandieWorkRequest(work);
if (!_property.IsReEntrant) {
_property.AsyncCallOutLCIDList.Remove(
((LogicalCallContext)
reqMsg.Properties[Message.
CallContextKey]).
RemotingData.LogicalCallID);
}
return work.ReplyMessage;
}
Мы не можем сразу же послать уведомление на обработку в перехватчик replySink, так как не уверены в том, что он не нарушит логики синхронизации. В связи с этим мы инкапсулируем уведомление в работу
Workltem work = new WorkItem (
reqMsg,
_nextSink,
null);
и обрабатываем его как обычную новую работу, инкапсулирующию синхронный вызов:
_property.HandieWorkRequest(work);
В зависимости от ситуации эта работа будет поставлена в очередь или будет выполняться без задержек, но в любом случае логика синхронизации не будет нарушена. Здесь наш поток блокируется до завершения обработки работы work (включая время простоя в очереди).
По завершении ее обработки удаляем соответствующий идентификатор из списка исходящих асинхронных вызовов (только в случае нереентерабельного контекста) и возвращаем результат:
if (!_property.IsReEntrant) {
_property.AsyncCallOutLCIDList.Remove(
((LogicalCallContext)
reqMsg.Properties[Message.
CallContextKey]).
RemotingData.LogicalCalllD);
}
return work.ReplyMessage;
Завершаем рассмотрением метода AsyncProcessMessage. Здесь все просто. Полагается, что уведомления о завершении асинхронных вызовов не должны посылаться в виде асинхронных вызовов. В связи с этим данный метод просто вызывает исключение NotSupportedExeption:
public virtual IMessageCtrl AsyncProcessMessage (
IMessage reqMsg,
IMessageSink replySink) {
throw new NotSupportedException;
}
Литература
1. Роберт Орфали, Дан Харки, Джери Эдвардс. Основы CORBA. М., 1999.
2. Дейл Роджерсон. Основы COM. Microsoft Corporation. 1997. </li>
3. Эндрю Трельсен. Модель СОМ и применение ATL 3.0. 2001.