InitlfNecessary();
SynchronizedServerContextSink propertySink =
new SynchronizedServerContextSink (
this,
nextSink);
return (IMessageSink) propertySink;
}
Единственным входным параметром является ссылка на перехватчик, последний на данный момент в цепочке перехватчиков входящих вызовов для данного контекста. Возвращаемое значение является ссылкой на новый перехватчик, который будет добавлен в эту цепочку.
Прежде всего выполняется инициализация свойства синхронизации, если это необходимо. Метод InitIfNeccessary был рассмотрен ранее. Реально он будет выполнять инициализацию свойства синхронизации только в том случае, когда текущий контекст является первым контекстом домена синхронизации. В противном случае свойство синхронизации уже было инициализировано ранее.
Далее формируется экземпляр класса SynchronizedServerContextSink, который будет рассмотрен позже. Этот объект и будет выступать в роли перехватчика входящих вызовов. Ему передаются два параметра:
• this — ссылка на свойство синхронизации (экземпляр данного класса SynchronizationAttribute) — новое или ранее сформированное, живущее в первом контексте домена синхронизации
• nextsink — ссылка на следующий в цепочке перехватчик.
И, наконец, возвращается полученная ссылка на новый перехватчик.
Таким образом, хотя свойство синхронизации одно на весь домен синхронизации, для каждого контекста в этом домене формируется свой перехватчик, имеющий ссылку на это свойство синхронизации.
Как перехватчик обрабатывает синхронные вызовы
Теперь рассмотрим класс SynchronizedServerContextSink:
internal class SynchronizedServerContextSink
: InternalSink, IMessageSink {… }
Мы видим, что данный класс наследует классу InternalSink и реализует интерфейс IMessageSink. Класс InternalSink отсутствует в .NET. Интерфейс IMessageSink объявляет методы, которые должны быть реализованы перехватчиками всех типов.
Класс SynchronizedServerContextSinkсодержит два поля данных:
internal IMessageSink _nextSink;
internal SynchronizationAttribute _property;
Первое содержит ссылку на следующий перехватчик, а второе — на свойство синхронизации текущего домена синхронизации.
Единственный конструктор инициирует эти поля:
internal sSynchronizedServerContextSink (
SynchronizationAttribute prop,
IMessageSink nextSink) {
_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)) {……..
Правила обработки вложенного вызова зависят от реентерабельности контекста синхронизации. Если контекст реентерабельный, то никакой специальной обработки вложенных вызовов производить не надо. В этом случае любой новый вызов (в том числе и вложенный) имеет право исполняться в реентерабельном контексте в то время, как выполняемый в данный момент вызов приостановлен на время ожидания ответа на какой-либо сделанный в его рамках внешний вызов.
Если же контекст синхронизации нереентерабельный, то никакой новый вызов не может выполняться в данном контексте, если в данном контексте в данное время выполняется какой-либо синхронный вызов. Если при выполнении синхронного вызова была инициирована цепочка вызовов и последний в этой цепочке вызов является вызовом в данный контекст синхронизации, то его блокировка приведет к блокировке всей очереди вызовов (текущий вызов никогда не завершится). Именно в связи с этим в случае нереентерабельного контекста синхронизации и синхронного исполняемого вызова вложенный вызов должен исполняться вне очереди.