Шрифт:
Интервал:
Закладка:
_property = prop;
_nextSink = nextSink
}
Каждый перехватчик должен обеспечить обработку как синхронных, так и асинхронных вызовов. Соответствующие методы объявлены в интерфейсе IMessageSink. Это SyncProcessMessage и AsyncProcessMessage.
В данном перехватчике обработка синхронных вызовов осуществляется следующим образом:
public virtual IMessage SyncProcessMessage(IMessage reqMsg) {
Workitem work = new Workitem(reqMsg,
_nextSink, null);
_property.HandleWorkRequest(work);
return work.ReplyMessage;
}
Собственно вызов в форме сообщения типа IMessage передается как единственный входной параметр. Возвращаемое значение (также типа IMessage) содержит результат, полученный данным перехватчиком от следующего в цепочки перехватчика после того, как вызов пройдет через всю цепочку перехватчиков до сервера, будет обработан на сервере, а ответ от него пройдет через всю цепочку перехватчиков в обратном направлении до данного перехватчика. Тут надо заметить, что на этом пути в каком-либо промежуточном перехватчике вызов может быть преобразован в асинхронную форму.
Роль данного перехватчика состоит в инкапсуляции вызова в объект типа WorkItem и его сохранении в очереди работ. Для этого вызывается конструктор WorkItem (reqMsg, _nextSink, null), первые два параметра которого задают вызов в форме сообщения и ссылку на следующий перехватчик. Третий параметр используется только в случае асинхронных вызовов. По умолчанию инкапсулирующая вызов работа work относится к синхронному типу.
После инкапсуляции вызова соответствующая работа передается свойству синхронизации:
_property.HandleWorkRequest(work);
Метод HandleWorkRequest класса SynchronizationAttribute ответственен за запись инкапсулированного вызова в очередь работ, за своевременное извлечение его из очереди и передачу следующему перехватчику, и, наконец, за получение ответа. Ответ доступен через свойство ReplyMessage работы, инкапсулирующей вызов. Значение этого свойства и возвращается как результат вызова метода SyncProcessMessage.
Как свойство синхронизации обрабатывает инкапсулированный синхронный вызов, полученный от перехватчика
Теперь временно прервем процесс изучение класса SynchronizedServerContextSink И рассмотрим метод HandleWorkRequest класса SynchronizationAttribute. Ниже приведена часть кода этого метода, которая относится к обработке именно синхронных вызовов:
internal virtual void HandleWorkRequest(WorkItem work) {
bool bQueued;
if (!IsNestedCall(work._reqMsg)) {
if (work.IsAsync()) {
…….
}
else {
lock(work) {
lock(_workItemQueue) {
if ((!_locked) &&
(_workltemQueue.Count == 0)) {
_locked = true;
bQueued = false;
}
else {
bQueued = true;
work.SetWaiting();
_workltemQueue.Enqueue(work);
}
}
if (bQueued == true) {
Monitor.Wait(work);
if (!worк. IsDummy()) {
DispatcherCallBack(null, true);
}
else {
lock(_workltemQueue) {
_workItemQueue.Dequeue();
}
}
}
else {
if (!worк. IsDummy()) {
work.SetSignaled();
ExecuteWorkltem(work);
HandleWorkCompletion();
}
}
}
}
}
else {
work.SetSignaled();
work.Execute();
}
}
Прежде всего выясняется — является ли инкапсулированный вызов work._reqMsg вложенным вызовом, т. е. вызовом, инициированным в процессе выполнения выполняемого в данный момент синхронного или исходящего асинхронного вызова:
if (!IsNestedCall(work._reqMsg)) {……..
Правила обработки вложенного вызова зависят от реентерабельности контекста синхронизации. Если контекст реентерабельный, то никакой специальной обработки вложенных вызовов производить не надо. В этом случае любой новый вызов (в том числе и вложенный) имеет право исполняться в реентерабельном контексте в то время, как выполняемый в данный момент вызов приостановлен на время ожидания ответа на какой-либо сделанный в его рамках внешний вызов.
Если же контекст синхронизации нереентерабельный, то никакой новый вызов не может выполняться в данном контексте, если в данном контексте в данное время выполняется какой-либо синхронный вызов. Если при выполнении синхронного вызова была инициирована цепочка вызовов и последний в этой цепочке вызов является вызовом в данный контекст синхронизации, то его блокировка приведет к блокировке всей очереди вызовов (текущий вызов никогда не завершится). Именно в связи с этим в случае нереентерабельного контекста синхронизации и синхронного исполняемого вызова вложенный вызов должен исполняться вне очереди.
Рассмотрим определенный В ЭТОМ же классе SynchronizationAttribute метод IsNestedCall.
internal bool IsNestedCall(IMessage reqMsg) {
bool bNested = false;
if (!IsReEntrant) {
String lcid = SyncCallOutLCID;
if (lcid!= null) {
LogicalCallContext callCtx =
(LogicalCallContext)
reqMsg.Properties[Mes sage.CallContextKey];
if (callCtx!=null &&
lcid.Equals(callCtx.RemotingData.LogicalCalllD)) {
bNested = true;
}
}
if (IbNested && AsyncCallOutLCIDList.Count>0) {
LogicalCallContext callCtx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
if (AsyncCallOutLCIDList.Contains(
callCtx.RemotingData.LogicalCalllD)) {
bNested = true;
}
}
}
return bNested;
}
Этот метод возвращает true если текущий контекст (точнее, текущий домен синхронизации) нереентерабельный, а новый вызов (reqMsg) является вложенным для исполняемого в данный момент синхронного вызова, или для одного из исходящих асинхронных вызовов. Во всех остальных случаях возвращается false.
Вначале выясняется синхронность выполняемого в данный момент вызова:
String lcid = SyncCallOutLCID;
if (lcid!= null) {
......
}
Свойство SyncCallOutLCID атрибута синхронизации возвращает идентификатор логического вызова исполняемого в данный момент вызова, если этот вызов синхронный. В противном случае возвращается null.
Теперь в случае синхронности исполняемого вызова мы получаем доступ к контексту вызова для вызова reqMsg:
LogicalCallContext callCtx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
Надо заметить, что тут имеется ввиду класс Message из пространства имен System.Runtime.Remoting.Messaging. Реализация такого класса в этом пространстве имен имеется в Rotor, но отсутствует в .NET. Статическое поле CallContextKey равно __CallContext.
Если доступ к контексту вызова получен, то проверяется, что идентификатор логического вызова исполняемого в данный момент синхронного вызова lcid совпадает с идентификатором логического вызова для вызова reqMsg. В случае их совпадения флаг вложенности bNested получает значение true:
if (callCtx!=null &&
lcid.Equals(callCtx.RemotingData.LogicalCalllD)) {
bNested = true;
}
Здесь ОПЯТЬ приходится отметить, что в .NET у класса LogicalCallContext нет свойства RemotingData типа CallContextRemotingData, нет и самого класса CallContextRemotingData и