Re[8]: Хороший вопрос
От: igor-booch Россия  
Дата: 03.07.13 12:45
Оценка:
BB>Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?

Хороший вопрос, Вы наверное имеете ввиду Access to modified closure.
Проверил, нет, же, видит прекрасно новые Foo.
И Resharper не ругается на Access to modified closure.

Сделал предположение, что Access to modified closure возникает только если переменная foo будет меняться в том же контексте, в котором создавалась лямбда её использующая:


            Foo foo = new Foo();
            int i = 0;

            Thread checkInitializedThread = new Thread(() =>
            {
                do
                {
                    if (!foo._initialized1 | !foo._initialized2)
                    {
                        Console.WriteLine("Bingo!!!");
                    }
                    Console.WriteLine(foo._i);
                } while (true);
            });

            checkInitializedThread.Start();

            do
            {
                foo = new Foo();
                foo._i = i;
                i++;
            } while (true);




class Foo
{
    public bool _initialized1;
    public bool _initialized2;
    public int _i;

    public Foo()
    {
        //Thread.Sleep(10);
        _initialized1 = true;
        Thread.Sleep(10);
        _initialized2 = true;
    }
}


Resharper заругался на Access to modified closure. Но checkInitializedThread все-равно видит новые Foo
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[9]: Хороший вопрос
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 03.07.13 12:47
Оценка:
Здравствуйте, igor-booch, Вы писали:

BB>>Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?


IB>Хороший вопрос, Вы наверное имеете ввиду Access to modified closure.

IB>Проверил, нет, же, видит прекрасно новые Foo.
IB>И Resharper не ругается на Access to modified closure.

Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.
El pueblo unido jamás será vencido.
Re[10]: Хороший вопрос
От: Sinix  
Дата: 03.07.13 13:08
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.


Под x86/x64 по-прежнему работает правило "all writes are volatile". Под арм/итаниум — с оговорками (см C# Memory Model Implementation on ARM).
Re[11]: Хороший вопрос
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 03.07.13 13:17
Оценка: +2
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, bl-blx, Вы писали:


BB>>Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.


S>Под x86/x64 по-прежнему работает правило "all writes are volatile". Под арм/итаниум — с оговорками (см C# Memory Model Implementation on ARM).


Да, спасибо — забыл об этом, но, тем не менее, второй поток делает non-volatile read — с чего бы ему в main memory лезть?
El pueblo unido jamás será vencido.
Re[12]: Хороший вопрос
От: Sinix  
Дата: 03.07.13 13:25
Оценка: -1
Здравствуйте, bl-blx, Вы писали:

BB>Да, спасибо — забыл об этом, но, тем не менее, второй поток делает non-volatile read — с чего бы ему в main memory лезть?

Это нужно спрашивать у специалистов, если верить stackoverflow — под x86/64 обновление кэша берёт на себя сам процессор.
Re[7]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 14:59
Оценка:
S>Или просто посмотреть содержимое ex.InnerException

Вы не совсем понимаете глубину проблемы. В моей задаче InnerException не поможет.
Re[7]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 15:01
Оценка:
A>Есть несколько решений этой проблемы:
A>http://stackoverflow.com/questions/57383/in-c-how-can-i-rethrow-innerexception-without-losing-stack-trace

Для исключения в конструкторе это не подойдет.
Re[13]: Хороший вопрос
От: drol  
Дата: 03.07.13 15:32
Оценка: 20 (1)
Здравствуйте, Sinix, Вы писали:

S>под x86/64 обновление кэша берёт на себя сам процессор.


Кэш данных в архитектурах x86/x64 когерентен. И к обсуждаемым вопросам не имеет никакого отношения.
Re[12]: Хороший вопрос
От: drol  
Дата: 03.07.13 15:51
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>второй поток делает non-volatile read — с чего бы ему в main memory лезть?


Совершенно верно. В потоке checkInitializedThread и foo, и foo._initialized1, и foo._initialized2, и foo._i могут быть закэшированы. Сделать это способна любая фаза кодогенерации: IL, JIT и т.д.
Re[8]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 16:04
Оценка:
Здравствуйте, abibok, Вы писали:

A>>Есть несколько решений этой проблемы:

A>>http://stackoverflow.com/questions/57383/in-c-how-can-i-rethrow-innerexception-without-losing-stack-trace

A>Для исключения в конструкторе это не подойдет.

Ну тогда и твой вариант не подойдет

public static object CreateInstance(Type type, object[] arguments)
{
   try
   {
      Activator.CreateInstance(t, args);
   }
   catch(TargetInvocationException ex)
   {
     throw ex.InnerException.PreserveStackTrace();
   }
}

public static class ExceptionHelper
{
    private static readonly Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        var preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static Exception PreserveStackTrace(this Exception ex )
    {
        _preserveInternalException( ex );
        return ex;
    }
}
Re[9]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 16:11
Оценка:
A>>Для исключения в конструкторе это не подойдет.
A>Ну тогда и твой вариант не подойдет

А мой как раз подойдет. Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.
Re[10]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 16:17
Оценка:
Здравствуйте, abibok, Вы писали:

A>>>Для исключения в конструкторе это не подойдет.

A>>Ну тогда и твой вариант не подойдет

A>А мой как раз подойдет.

В чем будет отличие от моего?

A>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.

И на каждый вызов делает компиляцию создаваемого Expression-а?
Re[7]: Конструктор не бросает исключений, не связанных с проверкой аргументов
От: abibok  
Дата: 03.07.13 16:25
Оценка:
IB>А Initialize(...) тоже не может выкидывать исключения не связанные с проверкой аргументов?

Может, это же обычный метод. И его исключения не завернут в TargetInvocationException.

IB>Проверка аргументов понятие растяжимое.

IB>Она может быть любой сложности.

Здесь нет абсолютного решения — только конструктор или только отдельный метод. Нужно смотреть по ситуации. Я просто показал, что есть ситуации, когда наличие метода оправдано.
Re[11]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 16:33
Оценка:
A>>А мой как раз подойдет.
A>В чем будет отличие от моего?

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

A>>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.

A>И на каждый вызов делает компиляцию создаваемого Expression-а?

Да. Это большая проблема? Мы не обязаны пользоваться этой функцией для создания абсолютно всех объектов. А там где это необходимо, потеря производительности (не такая уж и большая) оправдана.
Re[8]: Стоит ли переносить инициализацию в отдельный метод?
От: Sinix  
Дата: 03.07.13 17:18
Оценка:
Здравствуйте, abibok, Вы писали:

S>>Или просто посмотреть содержимое ex.InnerException


A>Вы не совсем понимаете глубину проблемы. В моей задаче InnerException не поможет.

Задачу вы пока не озвучили

Если как написано в другом посте, проблема в отладке — в настройках (debug-exceptions) можно включить прерывание отладки в момент броска исключения.
Re[12]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 17:30
Оценка:
Здравствуйте, abibok, Вы писали:

A>В вашем случае (кстати, дайте пример вашего кода) исключение в конструкторе ловится внутри CLR, заворачивается в TargetInvocationException и перебрасывается. Все, куча информации потеряна. Например, значения локальных переменных в момент бросания уже не получить.


О чем это ты, можно поподробнее?
Вот код:
  Скрытый текст
public static class ExceptionHelper
{
    private static readonly Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        var preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate(typeof(Action<Exception>), preserveStackTrace);
    }

    public static Exception PreserveStackTrace(this Exception ex)
    {
        _preserveInternalException(ex);
        return ex;
    }
}

public class A
{
    public A(int x, string s)
    {
        throw new ArgumentException("s");
    }
}

static class Program
{
    public static object CreateInstance(Type type, object[] arguments)
    {
        try
        {
            return Activator.CreateInstance(type, arguments);
        }
        catch (TargetInvocationException ex)
        {
            throw ex.InnerException.PreserveStackTrace();
        }
    }

    private static void Main()
    {
        try
        {
            //var a = new A(42, "Hi");
            var a = CreateInstance(typeof(A), new object[] { 42, "Hi" });
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}


В консоль выводится:

System.ArgumentException: s
at ConsoleApplication46.A..ctor(Int32 x, String s) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 34
at ConsoleApplication46.Program.CreateInstance(Type type, Object[] arguments) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 56
at ConsoleApplication46.Program.Main() in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 80



A>>>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.

A>>И на каждый вызов делает компиляцию создаваемого Expression-а?
A>Да. Это большая проблема? Мы не обязаны пользоваться этой функцией для создания абсолютно всех объектов. А там где это необходимо, потеря производительности (не такая уж и большая) оправдана.
А че б не закэшировать активаторы? Ну да фиг с ним...
Re[9]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 17:32
Оценка: 20 (1)
S>Задачу вы пока не озвучили

Задача — сохранить максимум информации о контексте приложения в момент возникновения исключительной ситуации для логирования и последующей отладки. Это включает в себя правильный callstack со всей информацией, включая значения локальных переменных и параметров функций в каждом фрейме, а не только текстовое представление стека вызовов. Для этого исключение нужно перехватывать сразу после того, как оно было брошено и до того, как оно войдет в блок catch.

S>Если как написано в другом посте, проблема в отладке — в настройках (debug-exceptions) можно включить прерывание отладки в момент броска исключения.


Речь идет не только об отладке, но и о штатной работе программы. Плюс такая настройка будет прерывать отладку для любых исключений, а нас интересуют только те, которые unhandled.
Re[13]: Стоит ли переносить инициализацию в отдельный метод?
От: abibok  
Дата: 03.07.13 17:48
Оценка:
A>О чем это ты, можно поподробнее?

Представьте себе, что конструктор не такой простой, а имеет сложную внутреннюю логику, которая зависит от состояния членов класса, параметров и значений локальных переменных.
Мы хотим всю эту информацию сохранить в лог, а разработчику предоставить managed dump, который будет указывать прямо на исходное исключение, а не на место, где это исключение поймали и перевыбросили.
Более того, мы хотим иметь не только стек вызовов, который привел к исключению — А(B(C())), но и историю выполнения — X->Y->Z->A(B(C())). И если исключение произошло из-за значения члена класса _myField, то проследить по истории до места, где _myField стал таким, и это место тоже включить в лог. А потом автоматически сделать root cause analysis "из-за проблемы в методе X у нас возникают исключения в C, T и Q".

Это общая задача, которая должна решаться для любого метода. Просто конструкторы немного особенные и создание объектов при таком подходе требуют специального обращения.
A>В консоль выводится:
A>

A>System.ArgumentException: s
A> at ConsoleApplication46.A..ctor(Int32 x, String s) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 34
A> at ConsoleApplication46.Program.CreateInstance(Type type, Object[] arguments) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 56
A> at ConsoleApplication46.Program.Main() in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 80


Мне кажется или у вас с номерами строк не все в порядке?

A>А че б не закэшировать активаторы? Ну да фиг с ним...


Конечно, можно закэшировать.
Re[7]: UninitializedException
От: artelk  
Дата: 03.07.13 17:52
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, HowardLovekraft, Вы писали:


HL>>Если в какой-то момент времени выполняется конструктор объекта, это значит, что он очень даже достижим.


S>Необязательно.

S>http://ericlippert.com/2013/06/10/construction-destruction/

Я правильно понимаю, что в общем виде мы не можем обеспечить гарантию освобождения unmanaged ресурсов?
void F()
{
  new SomeClassWithUnmanagedResources();
}

Finalizer может закончить работу до того, как в конструкторе будут выделены и сохранены в полях unmanaged ресурсы, так?
Мне, как разработчику SomeClassWithUnmanagedResources, что делать, чтобы обеспечить гарантию их освобождения?
Re[10]: Стоит ли переносить инициализацию в отдельный метод?
От: artelk  
Дата: 03.07.13 17:57
Оценка:
Здравствуйте, abibok, Вы писали:

S>>Задачу вы пока не озвучили


A>Задача — сохранить максимум информации о контексте приложения в момент возникновения исключительной ситуации для логирования и последующей отладки. Это включает в себя правильный callstack со всей информацией, включая значения локальных переменных и параметров функций в каждом фрейме, а не только текстовое представление стека вызовов. Для этого исключение нужно перехватывать сразу после того, как оно было брошено и до того, как оно войдет в блок catch.


Это что-то фирменное. Можно показать?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.