Сага о E_RPC_DISCONNECT

Сага о E_RPC_DISCONNECT

611

По-видимому, было возбуждено интерфейс, реализующий три с половиной способ, и родился в муках набор реализаций, время его раскопок были шестнадцать (!) на куски. В начале был код
Был код , написанный на dotnet (еще версия 1.1) много лет назад. Фабрики и остальных одиноких — в комплекте. Код был обычным и дуба — где-то в дебрях проект был стек Interop*.*.DLL для еще наиболее старых TLB.
Сделал классический код приложения, и все 16 реализаций в любом месте код был copypasta и однообразные — различаются лишь в namspace из Интерпола.
Что-то вроде этого:
/* остальные флаги */) так круто.Библиотека;
foreach(var активов в lib.Активов) {
/* какой-то большой операции */
}

С тех пор код пережила много вещей — переезд на dotnet 2.0, 3.5, 4.0 и так дальше, начали поддерживать тех Интерпола от 2-ух до шестнадцати — и код остается прежним и по-прежнему не изменяется, лишь плодится почкованием время от времени. Пока в один красивый день не работает этот код на Windows 8.1. Тип apptype = Тип.GetTypeFromProgID(«CoolAppID», false);
var app = активатор.CreateInstance(apptype) как здорово.Приложения;

var lib = app.Open(file, … Одна слеза с 2007 года.

Наиболее того, различные версии COM-сервера, так что это очевидно в коде. И злая колдунья желала съесть Ганс и Гретель
И посетил этот кусочек динозавра несчастный E_RPC_TIMEOUT.
Потому итератор просто погибает до конца цикла! Она ж работает чрезвычайно долго, правильно? Фигня! задумывался, что те, кто pixel ко мне.
Заменить:
var lib = app.Open(file, … /* остальные флаги */) так круто.Библиотека;
var list = new List<CoolAsset>();
foreach(var активов в lib.Активов) {
перечень.Добавить(актив);
}

foreach(var актива в перечень) {
/* какой-то большой операции */
}

/* некие остальные вещи */
/* конец функции */

GM GM. Не стало лучше. …
Но на его место пришел еще наиболее злой E_RPC_DISCONNECT. И в этот момент я мог бы сделать это фортепиано. Не огласить, что они не получили — но не стукнуть мяч
Это посодействовало в том смысле, что E_RPC_TIMEOUT исчез.
Инструменты и материалы:

Windows 8.1-это одно
Удивительно проекта — 1 шт
Adobe Indes Ох какие наружные com-server — 6 шт, играя на всех
Секрет бубен — 1 шт
Давайте начнем.
var lib = app.Open(file, … Что-то вроде этого:
… Атака на Зебра
До этого всего силовыми способами дальнее и черное прошедшее, и он будет nasuem каждый 2-ой строке выходного в журнальчике (Да, я знаю о отладчике). /* остальные флаги */) так круто.Библиотека;
var list = new List<CoolAsset>();
foreach(var активов в lib.Активов) {
перечень.Добавить(актив);
}

try {
log(«>> foreach»);
foreach(var актива в перечень) {
log(«>> foreach получил » + актив);
/* какой-то большой операции */
log(«>> foreach актив «+ актив + «OK»);
}
log(«<< foreach»);
}
catch (Exception e) {
log(«>> foreach не удалось из-за » + e + «n» + e.StackTrace);
}
/* некие остальные вещи */
/* конец функции */

Запустить, медитировать, и что мы лицезреем?
Некие вещи>> foreach
>> foreach получили foo
… E_RPC_DISCONNECT … >> foreach есть бар
… >> foreach актив-бар «ОК»
>> foreach не удалось из-за COMException … >> foreach актива foo ОК

То есть, смотря на код —
log(«>> foreach актив «+ актив + «OK»);
}
log(«<< foreach»);

Свалился до закрывающей фигурной скобки.
Как это может быть?
try {
log(«>> foreach»);
var itr = list.GetEnumerator();
for(;;) {
log(«>> foreach новейший цикл…»);
if(!itr.MoveNext()) break;
log(«>> foreach новейшего цикла и имеют доп элементы для прохода…»);
var актив = itr.Тока;
log(«>> foreach получил » + актив);
/* какой-то большой операции */
log(«>> foreach актив «+ актив + «OK»);
}
log(«<< foreach»);
}
catch (Exception e) {
log(«>> foreach не удалось из-за » + e + «n» + e.StackTrace);
}
/* некие остальные вещи */
/* конец функции */

Естественно, в журнальчике тут мы получаем Написать…

>> foreach актив-бар «ОК»
>> foreach новейший цикл… То есть, падает MoveNext(). >> foreach новейшего цикла и имеют доп элементы для прохода… E_RPC_DISCONNECT … >> foreach актива foo ОК
… >> foreach получили foo
… Наиболее подробный материал. >> foreach есть бар
… >> foreach не удалось из-за COMException … Нездоровый>> foreach
>> foreach новейший цикл…
На самом деле, мы просто бежали за свой хвост, ничего не обнаружив. Это чисто .Незапятнанная iterator от незапятнанного .NET List<T>! Это вообщем как? Но Эй!
Хвост виляет собакой
Взяв еще одну чашечку кофе и открыл вручную, и громко осквернили индусы обеих компаний, я бросаю все опыты в общем, я привожу в начальный вид, и заменить счетчик цикла цикл со счетчиком:
… Каково же было мое (и не лишь) удивление, когда код работал без ошибок — счастливо избежал и E_RPC_TIMEOUT и E_RPC_DISCONNECT! for(int i=0; i < lib.Активов.Count; ++i) {
var актив = lib.Активы[i];
/* какой-то большой операции */
}
… С Windows 8.1, где воспроизведение неувязка была абсолютной.
Как временное решение будет найдено, но оно ничего не разъясняет. Да и отыскал его лишь поэтому, что в те дни, когда я был Юниором, сооружений foreach не было, и заместо осознанного деяния, я лишь purafil его неприятные старенькые привычки…
Вечер перестает быть тяжелым
Обратно в начальное foreach, он где-то тут. Перейти к гипотезе, что все равно что-то не так с нашего com-объекта. Добавить пару косметических линий для удобства отладки:
… var assetsCollection = lib.Активов;
foreach(var активов в assetsCollection) {
/* какой-то большой операции */
}
assetsCollection = null;

Как бы разумеется, что эти две строчки
Но останова их ставить чрезвычайно комфортно. var assetsCollection = lib.Активов;
… … assetsCollection = null;
… вокруг цикла не влияет, правильно?
Говоря, брейк-брейк начало, tint-оттенок, читал на хабре те 20 минут, пока он жует эти активы. Не свалилась. Да?
Да, я поправить это в 16 раз 😉 Ничего не меняя запустить без отладки. 6 трасс с каждой реализации и каждой прихожей демонстрируют, что старый код шерстистого мамонта опять работает как и до этого — в любом месте. Извините, что? Не свалилась. И, может быть, отладчик воспрепядствовало додуматься, Зоркие глаза.
А сейчас горбатый! Казалось бы — какая разница?
Давайте вспомним этот набор фактов:

В дополнение к нашей нездоровой , код все равно сборщика мусора;
Вокруг COM мы прокси;
Этот прокси-сервер для нас прячет AddRef()/Release();
В классической реализации Release (), как правило, находится if(count ==0) delete this; на стороне COM-сервера:
Для нашего com-прокси (который является чем-то unasledovannye от MarshalByRefObject), В приходе горбун коллектор будет вызывать способ Dispose(), но она по-прежнему Гернот наши Release().

Сейчас мы можем представить, что это некорректно.
Это означает, забота компилятора этот факт отметил опосля первого цикла, где мы находимся коллекция активы в листе раскладной. Разумеется, наши lib.Активов рассчитывается один раз и нигде не употребляется.
странноватая закономерность. При написании com-сервер (который вызывается из хоть какой точки мира) следует ждать, что мастер-коллекция scazut Release() и будет продолжать применять дочерние элементы, некие из которых останутся неинициализированными… Опосля того, как все, не значит, что каждый элемент вызывает AddRef() при попадании. И тогда в наш способ, коллекционер может отыскать ссылку в хоть какое время, и тот факт, что способ работает чрезвычайно долго — эта возможность возрастает практически на 100 процентов. Но дочерние элементы, разумеется, стоимости объектов с отложенной инициализации, и что они и как они держат снутри — я могу лишь догадываться. Я бы даже представил, что гарантированно не.
Но добавив две «незначимые» слова — как как будто я очевидно отдал осознать компилятору, что это локальная переменная, которая живет с начала декларации в конце функции, и сборка его гарантированно начать не ранее, чем «крайнего использования».
Чуток наиболее мусора по умолчанию — и вот оно. Что делает Windows 8.1? И когда dotnet 4.5.
И E_RPC_DISCONNECT пришел еще скорее, чем preferiremos системы, так там что-то есть. Эту гипотезу я даже проверил, — повторял тот же эффект с Windows 2012R2 / .NET 4.5 64bit выбрать, чтоб убедиться, AWS t2.micro instance.
habrahabr.ru Но это все же отражение области полезные, может быть, есть и остальные причины.