но если я помечу атрибутом Cer.Success то таких проблем быть не должно?
При возникновении исключительных условий, метод гарантированно завершится успешно. Всегда следует заключать вызываемый метод в конструкцию CER, даже если он вызывается из области, где CER не используется. Метод завершается успешно, если он выполняет то, для чего предназначен. Например, если пометить свойство Count атрибутом ReliabilityContractAttribute(Cer.Success), это подразумевает, что, когда это свойство выполняется в области CER, оно всегда возвращает число элементов в ArrayList и никогда не оставляет внутренние поля неопределенными.
и солнце б утром не вставало, когда бы не было меня
Пока вы занимались организацией абортария и обсуждали всякое-разное, я доконал свою проблему и обновил статью: ReaderWriterLockSlim fails on dual-socket environments.
Что в сухом остатке: Microsoft включает "фикс" для ReaderWriterLockSlim в Windows 8.1 и Windows 2012 R2, Windows 2012 первой редакции даже со всеми возможными апдейтами по сей день так и остается с глюкавым классом.
Как я и полагал, изменение состоит в MFENCE перед операцией освобождения спинлока.
Кстати, Volatile.Write реализован по-Майкрософтовски, сначала MFENCE, потом запись. Т.е. он синхронизирует все, кроме ячейки памяти, о которой его просили.
Здравствуйте, Serginio1, Вы писали:
S>но если я помечу атрибутом Cer.Success то таких проблем быть не должно?
Неа, это просто способ документировать контракт вашего метода по обработке неожиданных исключений.
Там нет никакой магии и сам по себе метод "безопасным" не станет. Msdn:
The ReliabilityContractAttribute attribute provides a mechanism for you to document your code, and to indicate what type of reliability guarantees you can make in the face of exceptional conditions that could potentially lead to an inconsistent state. In this context, exceptional conditions are defined as asynchronous exceptions that can be generated at run time by the common language runtime, such as aborted threads, out-of-memory situations, and stack overflows. You can apply the ReliabilityContractAttribute attribute to assemblies, types, and methods.
Use this attribute with the Consistency enumeration to define a reliability contract by documenting the level of reliability in a particular piece of code.
Здравствуйте, Tom, Вы писали:
Tom>Он уже безопасен с некоторыми оговорками. Иначе его бы никогда не впихнули в сиквел.
SQL Server после любого асинхронного исключения в потоке проверяет оный на предмет, а не работал ли тот в момент Ч с разделяемым состоянием. И если работал — весь домен тут же выгружается.
Здравствуйте, Tom, Вы писали:
Tom>Забыл совсем сказать, когда бойцы прикручивали CLR к сиквелу — они изобрели HostProtectionAttribute у которого есть MayLeakOnAbort
Вот именно. И если асинхронное исключение прилетит в тот момент, когда работают с тем у чего MayLeakOnAbort или там SharedState, то SQL Server тут же выгрузит домен.
Здравствуйте, drol, Вы писали:
D>Здравствуйте, Tom, Вы писали:
Tom>>Забыл совсем сказать, когда бойцы прикручивали CLR к сиквелу — они изобрели HostProtectionAttribute у которого есть MayLeakOnAbort D>Вот именно. И если асинхронное исключение прилетит в тот момент, когда работают с тем у чего MayLeakOnAbort или там SharedState, то SQL Server тут же выгрузит домен.
И? Есть куча классов у которых MayLeakOnAbort=false и пригодных для использования в Safe сборках.
В который кстате прекрасно можно иметь разделяемое состояние без перевода сборки в unsafe или external access.
Здравствуйте, Tom, Вы писали:
Tom>В который кстате прекрасно можно иметь разделяемое состояние без перевода сборки в unsafe или external access.
Можно. Только вот всё это надо специально делать. И за каждым чихом следить. А то, например, подключите какую-нибудь стороннюю библиотеку, а она и слыхом не слыхивала про хостинг в SQL Server'е.
Здравствуйте, drol, Вы писали:
D>Здравствуйте, Tom, Вы писали:
Tom>>В который кстате прекрасно можно иметь разделяемое состояние без перевода сборки в unsafe или external access.
D>Можно. Только вот всё это надо специально делать. И за каждым чихом следить. А то, например, подключите какую-нибудь стороннюю библиотеку, а она и слыхом не слыхивала про хостинг в SQL Server'е.
Подключать стороннюю библиотеку внутрях сиквела... Хм....
Ну и уже говорилось защита от асинхронных искоючений давно прилкмана CER&Finally!
Здравствуйте, Chabster, Вы писали:
C>Microsoft включает "фикс" для ReaderWriterLockSlim в Windows 8.1 и Windows 2012 R2
Ну вот видите
C>Как я и полагал, изменение состоит в MFENCE перед операцией освобождения спинлока.
MFENCE ??? Да ну ? У x86\x86-64, в обычных условиях, достаточно сильная модель памяти. И store через стандартный MOV фактически является volatile write.
Неужели на Xeon генерится что-то другое ?
C>Кстати, Volatile.Write реализован по-Майкрософтовски, сначала MFENCE, потом запись.
И это правильно.
*Пока абстрагируемся от конкретной архитектуры...
C>Т.е. он синхронизирует все, кроме ячейки памяти, о которой его просили.
У не 21-летнего сеньера очень смешные представления о теме
P.S. Да, и ещё. В Вашей обновлённой блог-записи появились какие-то пространные рассуждения о кэше, кэш-линиях и т.п. Так вот, кэш не имеет никакого отношения к вопросу. Вообще. Бо на x86\x86-64 кэш данных когерентен. И посему, никаких дополнительных условий на наблюдаемый сторонними ядрами\процессорами порядок операций не накладывает.
Здравствуйте, Tom, Вы писали:
Tom>Подключать стороннюю библиотеку внутрях сиквела... Хм....
Ну а для чего ещё нам нужна CLR внутри SQL Server'а ? Чтобы 2+2 складывать ?
Tom>Ну и уже говорилось защита от асинхронных искоючений давно прилкмана CER&Finally!
И в итоге мы очень быстро дойдём до того, что засунем в эти самые CER\finally почти весь код. Но тогда возникает вопрос. А зачем вообще нужен Thread.Abort(), если он нигде не может пролезть ?
Так я все-таки не понял. Рассматриваем гипотетический сценарий создания собственного Hadoop-а. Джобы на обработку (в виде неких сборок с неким реализованным методом) приходят хрен знает откуда, пишутся хрен знает кем и имеют тенденцию виснуть.
Скажу честно, идея многопоточных вычислений на локах (пусть даже таких новомодных как ReaderWriterLockSlim) — это гнилая идея, на мой взгляд. В любой мало-мальски сложной задаче она выльется в хождение по граблям.
Правильная мнокопоточность делается на более высокоуровневых абстракциях как то:
1. Модель акторов и прочие системы ориентированные на обмен событиями.
2. Функциональные вычисления (туда же монады, продолжения и т.п.).
3. Конечных автоматах.
4. DSL-ях по которым многопоточный код генерируется автоматически.
А уж все виды блокировок и прочей низкоуровневой фигни (вроде многопоточных коллекций) должны быть примитивами реализации вышеперечисленного. Гражданские программисты вообще не должны иметь с ними дела.
Лучше всего если поддержка многопоточности будет встроена к язык (как в Эрланг, например). МС делает некоторые шаги (async) в этом направлении, но как всегда очень робкие.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Аноним, Вы писали:
А>> Вперед.
VD>Звучит как "Бокс!" или даже "Фас!" .
Виноват, завелся сильнооо.
VD>Скажу честно, идея многопоточных вычислений на локах (пусть даже таких новомодных как ReaderWriterLockSlim) — это гнилая идея, на мой взгляд. В любой мало-мальски сложной задаче она выльется в хождение по граблям.
VD>Правильная мнокопоточность делается на более высокоуровневых абстракциях как то: VD>1. Модель акторов и прочие системы ориентированные на обмен событиями. VD>2. Функциональные вычисления (туда же монады, продолжения и т.п.). VD>3. Конечных автоматах. VD>4. DSL-ях по которым многопоточный код генерируется автоматически.
VD>А уж все виды блокировок и прочей низкоуровневой фигни (вроде многопоточных коллекций) должны быть примитивами реализации вышеперечисленного. Гражданские программисты вообще не должны иметь с ними дела.
VD>Лучше всего если поддержка многопоточности будет встроена к язык (как в Эрланг, например). МС делает некоторые шаги (async) в этом направлении, но как всегда очень робкие.
Влад, все это красивые слова и теории, а реальность такова, что даже эти теории используют в реализациях такие примитивы, как ReaderWriterLockSlim.
Вот банальный пример. System.Web.Util из чистого 4.5 содержал структуру ReadWriteSpinLock. Ее заимствовали многие классы из System.Web. Простейший тест во много потоков демонстрирует, что этот алгоритм не работает. В свежей версии фреймворка его удалили.
Рихтер мне прислал компилирующуюся и якобы рабочую версию своего класса OneManyResourceLock. Под нагрузкой он так же не работает (на домашнем Core i5, 4 ядра) — то нарушает принцип самой блокировки, то блокируется навсегда. И при этом Джефф уверяет, что его кодом пользуются многие в своих продуктах.
Единственная блокировка, которая у меня прошла все тесты — SRW из WinAPI. Но интероп уничтожает все ее прелести.
Работающего решения, которое было бы сравнимо по производительности с ReaderWriterLockSlim я пока не имею.
Здравствуйте, Chabster, Вы писали:
C>Рихтер мне прислал компилирующуюся и якобы рабочую версию своего класса OneManyResourceLock. Под нагрузкой он так же не работает (на домашнем Core i5, 4 ядра) — то нарушает принцип самой блокировки, то блокируется навсегда. И при этом Джефф уверяет, что его кодом пользуются многие в своих продуктах.
Лучше всего выложить сюда тесты демонстрирующие падение (или что там происходит). Тогда можно будет предметно обсудить проблему.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Лучше всего выложить сюда тесты демонстрирующие падение (или что там происходит). Тогда можно будет предметно обсудить проблему.
Действительно, в общем случае с формальной точки зрения это работать не должно. Там разделяемое состояние читается без задания порядка, путём обычного копирования ref-параметра в локальную переменную
Здравствуйте, scale_tone, Вы писали:
_>Так я все-таки не понял. Рассматриваем гипотетический сценарий создания собственного Hadoop-а. Джобы на обработку (в виде неких сборок с неким реализованным методом) приходят хрен знает откуда, пишутся хрен знает кем и имеют тенденцию виснуть.
_>Так ограничивать их самостоятельность
можно? Или есть еще подводные камни?
А оно вообще будет работать? У нас же делегат не сериализуется и его вызов будет переадресован в исходный домен. Пишу по памяти, проверять надо.
Но это непринципиально, т.к. передача делегата создаёт другую проблему: сборка вашего trickyMethod будет загружена и в основной домен, и во временный. Избавляемся от передачи делегата — решаем обе
Обычно делают чуть иначе:
1. Пишут доверенный код, который будет запускать задачи, предоставлять этим задачам api и тд
2. Запускают этот код в отдельном домене (доменах) и передают ему список задач на выполнение (например, в виде строк — сборка-тип-метод).
3. Если при выполнении одной из задач возникли проблемы — просто прибиваем домен и запускаем заново.
Гранулярность (количество доменов/задач на домен) подбирается опытным путём, в зависимости от падучести кода.
можно? Или есть еще подводные камни? S>А оно вообще будет работать? У нас же делегат не сериализуется и его вызов будет переадресован в исходный домен. Пишу по памяти, проверять надо.
Да вот работает, как ни странно (сам удивился).
S>Но это непринципиально, т.к. передача делегата создаёт другую проблему: сборка вашего trickyMethod будет загружена и в основной домен, и во временный. Избавляемся от передачи делегата — решаем обе
Это понятно. В реальной ситуации метод SafeCaller.CallSafely(), конечно, принимал бы какой-нибудь путь к сборке и имя метода (или просто дефолтный метод из сборки вызывал бы). Я с делегатом написал в целях упрощения и применительно к задаче того топикстартера.
S>Обычно делают чуть иначе:
S>1. Пишут доверенный код, который будет запускать задачи, предоставлять этим задачам api и тд S>2. Запускают этот код в отдельном домене (доменах) и передают ему список задач на выполнение (например, в виде строк — сборка-тип-метод). S>3. Если при выполнении одной из задач возникли проблемы — просто прибиваем домен и запускаем заново.
S>Гранулярность (количество доменов/задач на домен) подбирается опытным путём, в зависимости от падучести кода.
Меня, скорее, больше интересовал вопрос, насколько опасно/безопасно делать AppDomain.Unload(). Который, вроде как, должен прибить все потоки, запущенные внутри этого домена. И именно силами Thread.Abort()...
И еще интересно, что будет, если опасный метод выполнять в потоке, запущенном вне домена...
Здравствуйте, scale_tone, Вы писали:
_>Да вот работает, как ни странно (сам удивился).
Тьфу, ятормоз Если в делегат передать static-метод, код будет работать. Если экземплярный метод, например
//[Serializable]class Program
{
static void Main(string[] args)
{
SafeCaller<C>.CallSafely(new Program().DoTricky, null, TimeSpan.FromMinutes(2));
}
public object DoTricky(C c, object o)
{
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
return null;
}
}
class C { }
— получим или SerialisationException, или десериализованную копию в ThatDomain().
_>Меня, скорее, больше интересовал вопрос, насколько опасно/безопасно делать AppDomain.Unload(). Который, вроде как, должен прибить все потоки, запущенные внутри этого домена. И именно силами Thread.Abort()...
Безопасно, если соблюдать ряд простых правил:
1. Unmanaged-ресурсы аллоцируются и освобождаются через наследника от SafeHandle.
2. Домен с подозрительным кодом в принципе может упасть в любой момент. Если домен выгружен — вызовы методов класса, созданного в этом домене, будут бросать исключения. Хост-домен должен это учитывать и оборачивать вызовы в try-catch.
3. По возможности не надо тасовать потоки между доменами. По памяти — у нас были ошибки, когда AppDomain.Unload прибивал не тот поток.
4. Нужно следить, чтобы подозрительные сборки не загружались в основной домен.
_>И еще интересно, что будет, если опасный метод выполнять в потоке, запущенном вне домена...
Это как? У нас любой managed-код всегда выполняется в рамках текущего домена.
Здравствуйте, Sinix, Вы писали:
S>Тьфу, ятормоз Если в делегат передать static-метод, код будет работать.
И правда. В моем случае это была лямбда ((obj, p) => obj.SlowMethod(p)), и она становилась статическим методом, т.к. не юзала членов класса. Стоит лямбду сделать нестатической — все ломается
S>Безопасно, если соблюдать ряд простых правил: S>1. Unmanaged-ресурсы аллоцируются и освобождаются через наследника от SafeHandle.
А если нет? Что если TrickyMethod, скажем, видео конвертирует, с применением ассемблерных вставок? Что, выгрузка домена в сочетании с Thread.Abort() могут как-то покораптить основной домен?
_>>И еще интересно, что будет, если опасный метод выполнять в потоке, запущенном вне домена... S>Это как? У нас любой managed-код всегда выполняется в рамках текущего домена.
Это как-то так:
public class SafeCaller<TRickyType> : MarshalByRefObject where TRickyType : new()
{
public static object CallNotSoSafely(Func<TRickyType, object, object> trickyMethod, object methodParam, TimeSpan timeout)
{
object result = null;
Thread thatThread = null;
Exception exception = null;
var domain = AppDomain.CreateDomain("ThatDomain " + Guid.NewGuid());
try
{
var callerType = typeof(SafeCaller<TRickyType>);
var caller = (SafeCaller<TRickyType>)domain.CreateInstanceAndUnwrap(callerType.Assembly.FullName, callerType.FullName);
thatThread = new Thread(() =>
{
try
{
result = caller.CallInSameThread(trickyMethod, methodParam);
}
catch (Exception ex)
{
exception = ex;
}
});
thatThread.Start();
if (!thatThread.Join(timeout))
{
throw new TimeoutException("Method timed out!");
}
}
finally
{
if (thatThread != null)
{
thatThread.Abort();
}
AppDomain.Unload(domain);
}
if (exception != null)
{
throw exception;
}
return result;
}
private object CallInSameThread(Func<TRickyType, object, object> trickyMethod, object param)
{
try
{
return trickyMethod(new TRickyType(), param);
}
catch (Exception exception)
{
// this is a stupid way to workaround not serializable exceptionsthrow new Exception("Tricky method throwed an exception: " + exception.Message);
}
}
}
Конкретно, разница в том, что в SafeCaller.CallSafely() поток запускался внутри временного домена, а тут, в SafeCaller.CallNotSoSafely() поток запускается (и абортится) в основном.
Будет разница в безопасности?