баг в .NET?
От: Kesular  
Дата: 01.05.17 22:46
Оценка:
Время от времени, в интеграционных тестах возникает вот такое исключение:

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: Collection was modified; enumeration operation may not execute.


> mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ThrowAsync.AnonymousMethod__6_1(object state) Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
[Native to Managed Transition]


Похоже, баг где-то во внутренностях фреймворка?
Re: баг в .NET?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 02.05.17 01:29
Оценка: +5
Здравствуйте, Kesular, Вы писали:

K>Время от времени, в интеграционных тестах возникает вот такое исключение:


K>

K>An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

K>Additional information: Collection was modified; enumeration operation may not execute.

K>Похоже, баг где-то во внутренностях фреймворка?

Нет, скорее всего, это race condition в тестах. Иногда один поток модифицирует коллекцию, IEnumerator для которой в это время перебирается (с помощью MoveNext или foreach) в другом потоке. При изменении коллекции все живые энумераторы инвалидируются, и начинают бросать такое исключение.

См. документацию:

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException.

The enumerator does not have exclusive access to the collection; therefore, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.


Кроме рекомендуемой здесь ручной синхронизации, можно воспользоваться готовыми решениями из пространства имён System.Collections.Concurrent. См, например, ConcurrentBag.GetEnumerator():

The enumeration represents a moment-in-time snapshot of the contents of the bag. It does not reflect any updates to the collection after GetEnumerator was called. The enumerator is safe to use concurrently with reads from and writes to the bag.

Отредактировано 02.05.2017 1:38 nikov . Предыдущая версия . Еще …
Отредактировано 02.05.2017 1:31 nikov . Предыдущая версия .
Re[2]: баг в .NET?
От: Kesular  
Дата: 02.05.17 02:08
Оценка:
Здравствуйте, nikov, Вы писали:

N>Нет, скорее всего, это race condition в тестах. Иногда один поток модифицирует коллекцию, IEnumerator для которой в это время перебирается (с помощью MoveNext или foreach) в другом потоке. При изменении коллекции все живые энумераторы инвалидируются, и начинают бросать такое исключение.


Спасибо, Кэп, но есть одна небольшая проблема. В стек трейсе нет ни одного метода или класса, который не входит в FCL. А это значит, что коллекция принадлежит именно FCL.
Re[3]: баг в .NET?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 02.05.17 02:39
Оценка:
Здравствуйте, Kesular, Вы писали:

N>>Нет, скорее всего, это race condition в тестах. Иногда один поток модифицирует коллекцию, IEnumerator для которой в это время перебирается (с помощью MoveNext или foreach) в другом потоке. При изменении коллекции все живые энумераторы инвалидируются, и начинают бросать такое исключение.


K>Спасибо, Кэп, но есть одна небольшая проблема. В стек трейсе нет ни одного метода или класса, который не входит в FCL. А это значит, что коллекция принадлежит именно FCL.


Да, я и говорил про несинхронизированные коллекции из стандартной библиотеки. Они обладают таким свойством, как это и указано в документации. А тесты, видимо, используют их некорректным способом — модифицируют из одного потока, в то время как другой поток пытается перебирать их элементы. Пользовательский код, видимо, выполняется в первом потоке, который модифицировал коллекцию, поэтому соовтветствующего стек-трейса не видно в исключении, которое возникло во втором потоке. Можно попробовать сохранять дамп процесса в момент возникновения исключение — возможно, потом в отладчике можно будет увидеть, с какой именно коллекцией возникает проблема.
Отредактировано 02.05.2017 2:45 nikov . Предыдущая версия . Еще …
Отредактировано 02.05.2017 2:40 nikov . Предыдущая версия .
Re[4]: баг в .NET?
От: Kesular  
Дата: 02.05.17 02:46
Оценка:
Здравствуйте, nikov, Вы писали:

N>Да, я и говорил про несинхронизированные коллекции из стандартной библиотеки. Они обладают таким свойством, как это и указано в документации. А тесты, видимо, используют их некорректным способом — модифицируют из одного потока, в то время как другой поток пытается перебирать их элементы. Пользовательский код, видимо, выполняется в первом потоке, который модифицировал коллекцию, поэтому соовтветствующего стек-трейса не видно в исключении, которое возникло во втором потоке. Можно попробовать сохранять дамп процесса в момент возникновения исключение — возможно, потом в отладчике можно будет увидеть, с какой именно коллекцией возникает проблема.


Я имею в виду коллекцию, которая создается и используется классами FCL, а не пользовательским кодом. В коде тестов есть пара коллекций, которые используются из разных потоков, но там с синхронизацией всё нормально.
Re: баг в .NET?
От: Sinix  
Дата: 02.05.17 04:55
Оценка: 57 (2)
Здравствуйте, Kesular, Вы писали:

K>Похоже, баг где-то во внутренностях фреймворка?

Неа, async void.

https://blogs.msdn.microsoft.com/calvin_hsia/2014/04/25/async-methods-failures-can-be-hard-to-diagnose/
http://stackoverflow.com/questions/21466488/async-method-throws-exception
https://code.msdn.microsoft.com/windowsdesktop/Exception-Handling-and-84b500c2
Re: баг в .NET?
От: Sinatr Германия  
Дата: 02.05.17 07:05
Оценка: 1 (1) +2 :)
Здравствуйте, Kesular, Вы писали:

K>Похоже, баг где-то во внутренностях фреймворка?


Весьма голословное утверждение. На StackOverflow такие вопросы сразу закрывают. Ну серьезно, перед тем как обвинять других, покажите код, в котором проблема воспроизводится, потратьте немного своего времени. Очень вероятно, что в попытках воспроизвести, вы сами локализуете проблемное место. См. mcve.
---
ПроГLамеры объединяйтесь..
Re[2]: баг в .NET?
От: Kesular  
Дата: 02.05.17 16:19
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Неа, async void.


Похоже на то, есть там один такой метод. Но это всё равно баг, поскольку компилятор должен выдавать ошибку про невалидный код.
А не так, что ошибка благополучно игнорируется, пока не произойдет мутное исключение где-то в совсем другом месте.
Отредактировано 02.05.2017 16:23 Kesular . Предыдущая версия .
Re[2]: баг в .NET?
От: Kesular  
Дата: 02.05.17 16:20
Оценка: -5
Здравствуйте, Sinatr, Вы писали:

S>Весьма голословное утверждение. На StackOverflow такие вопросы сразу закрывают. Ну серьезно, перед тем как обвинять других, покажите код, в котором проблема воспроизводится, потратьте немного своего времени.


Там вообще закрывают и/или минусуют любые вопросы, которые выходят за уровень знаний обычной средней макаки с клавиатурой. Хоть трать своё время, хоть не трать.
Так что это не показатель.
Re[3]: баг в .NET?
От: Sinix  
Дата: 02.05.17 18:33
Оценка:
Здравствуйте, Kesular, Вы писали:

K>Похоже на то, есть там один такой метод. Но это всё равно баг, поскольку компилятор должен выдавать ошибку про невалидный код.

Идея хорошая, только не компилятор, а аналайзер студии или решарпера. Если будет код с примером — можно будет джетбрейновцам предложение подкинуть.

K>А не так, что ошибка благополучно игнорируется, пока не произойдет мутное исключение где-то в совсем другом месте.

Ну так async void же. Какое ещё поведение у него должно быть?

P.S. А какая версия компилятора / target фреймворк, кстати? Смутно помнится, что от версии к версии поведение менялось.
Re[4]: баг в .NET?
От: Kesular  
Дата: 02.05.17 18:45
Оценка: 22 (1)
Здравствуйте, Sinix, Вы писали:

S>Идея хорошая, только не компилятор, а аналайзер студии или решарпера. Если будет код с примером — можно будет джетбрейновцам предложение подкинуть.


Именно компилятор. Насколько я понимаю, async void вообще не имеет практического применения и всегда приводит к ошибкам, а отловить его — очень легко.

S>Ну так async void же. Какое ещё поведение у него должно быть?


Практически любое, кроме "что-то сломалось непонятно когда, непонятно где, непонятно почему, иди ищи причины в гугле".

S>P.S. А какая версия компилятора / target фреймворк, кстати? Смутно помнится, что от версии к версии поведение менялось.


6 / 4.5.2
Re[5]: баг в .NET?
От: Sinix  
Дата: 02.05.17 18:53
Оценка: 30 (3) +1
Здравствуйте, Kesular, Вы писали:

K>Именно компилятор. Насколько я понимаю, async void вообще не имеет практического применения

Обработчики событий в UI.

K>и всегда приводит к ошибкам, а отловить его — очень легко.

Угу, вплоть до Async Void Methods Can Crash Your Process.
В общем, я бы заменил async void на async Task и подождал бы падения теста. Должно помочь. А, ну и убедиться, что pdb тестам доступны, по идее лог должен больше информации содержать.
Re[3]: баг в .NET?
От: _NN_ www.nemerleweb.com
Дата: 03.05.17 20:27
Оценка: 5 (1)
Здравствуйте, Kesular, Вы писали:

S>>Неа, async void.


K>Похоже на то, есть там один такой метод. Но это всё равно баг, поскольку компилятор должен выдавать ошибку про невалидный код.

Возьмите анализаторы на будущее:

https://github.com/prodot/ReCommended-Extension
https://marketplace.visualstudio.com/items?itemName=SemihOkur.AsyncFixer
https://marketplace.visualstudio.com/items?itemName=CalebClarke.AsyncVoidAnalyzer
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[6]: Win10 success story
От: Kesular  
Дата: 18.05.17 00:06
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Угу, вплоть до Async Void Methods Can Crash Your Process.

S>В общем, я бы заменил async void на async Task и подождал бы падения теста. Должно помочь. А, ну и убедиться, что pdb тестам доступны, по идее лог должен больше информации содержать.

Проблема не в том, что внутри метода произошло исключение — там все исключения ловятся на месте. Причина какая-то другая. И да, pdb доступны и стектрейс выше — абсолютно полный. Но замена на async Task каким-то образом подавляет эту проблему.
Re[7]: Win10 success story
От: Sinix  
Дата: 18.05.17 06:18
Оценка: :)
Здравствуйте, Kesular, Вы писали:

K>Проблема не в том, что внутри метода произошло исключение — там все исключения ловятся на месте. Причина какая-то другая. И да, pdb доступны и стектрейс выше — абсолютно полный. Но замена на async Task каким-то образом подавляет эту проблему.


Нету ручек — нет конфетки. В всмысле, нет воспроизводящейся ошибки — нет проблемы. Из спортивного интереса конечно можно накатать тест, который вызывает async void-метод, бросающий исключения, и попробовать поймать ошибку.
Но я бы не заморачивался, реальной необходимости в async void, как я понял, нет.
Re[8]: Win10 success story
От: Kesular  
Дата: 18.05.17 17:08
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Из спортивного интереса конечно можно накатать тест, который вызывает async void-метод, бросающий исключения, и попробовать поймать ошибку.


Он их не бросает, см выше.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.