Добавить в цитаты Настройки чтения

Страница 233 из 372

          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)) {……..

Правила обработки вложенного вызова зависят от реентерабельности контекста синхронизации. Если контекст реентерабельный, то никакой специальной обработки вложенных вызовов производить не надо. В этом случае любой новый вызов (в том числе и вложенный) имеет право исполняться в реентерабельном контексте в то время, как выполняемый в данный момент вызов приостановлен на время ожидания ответа на какой-либо сделанный в его рамках внешний вызов.

Если же контекст синхронизации нереентерабельный, то никакой новый вызов не может выполняться в данном контексте, если в данном контексте в данное время выполняется какой-либо синхронный вызов. Если при выполнении синхронного вызова была инициирована цепочка вызовов и последний в этой цепочке вызов является вызовом в данный контекст синхронизации, то его блокировка приведет к блокировке всей очереди вызовов (текущий вызов никогда не завершится). Именно в связи с этим в случае нереентерабельного контекста синхронизации и синхронного исполняемого вызова вложенный вызов должен исполняться вне очереди.