Интернет-журнал 'Домашняя лаборатория', 2007 №6 - Вязовский
Шрифт:
Интервал:
Закладка:
lock(_workltemQueue) {
_workItemQueue.Dequeue();
}
}
Иными словами, фиктивная работа просто удаляется из очереди, после чего выполнение метода SyncProcessMessage завершается возвращением результата replyMsg и разбуженный поток продолжает выполнять основной вызов.
Случай нереентерабельного контекста
Теперь рассмотрим ветвь метода SyncProcessMessage класса SynchronizedClientContextSink, относящуюся к случаю нереентерабельного контекста:
public virtual IMessage SyncProcessMessage (
IMessage reqMsg) {
IMessage replyMsg;
if (_property.IsReEntrant) {
……
}
else {
LogicalCallContext cctx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
String lcid = cctx.RemotingData.LogicalCalllD;
bool bClear = false;
if (lcid == null) {
lcid = Identity.GetNewLogicalCalllD ();
cctx.RemotingData.LogicalCalllD = lcid;
bClear = 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;
}
}
}
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:
AsyncReplySink mySink =
new AsyncReplySink(replySink, _property);
Класс AsyncReplySink будет рассмотрен чуть позже.
И вот, наконец, исходящий асинхронный вызов reqMsg передается следующему перехватчику исходящих асинхронных вызовов, а для получения уведомления указывается mySink:
msgCtrl = _nextSink.AsyncProcessMessage (
reqMsg,
(IMessageSink)mySink);
return msgCtrl;
Теперь рассмотрим класс AsyncReplySink:
internal class AsyncReplySink: IMessageSink {
…..
}
В конструкторе