Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
VladD2,
пожалуйста, дочитай до конца
до того, как будешь отвечать.
Иначе сложно вести обсуждение, когда вопросы задаются, на которые
ответ можно было получить из следующих двух строк, сэкономив и мне,
и себе время.
ПК>> В этих традициях написана VM того же .Net: посмотри, как там с помощью
ПК>> _ASSERTE проверяются предусловия и постусловия. В случае их нарушения
ПК>> никаких исключений не будет. Все просто "тихо" упадет.
V> _ASSERTE работает только в дебаге. И даже в нем от него ничего не
V> упадет. А вот AV после словить можно.
Это (например, AV) и было обозначено словами "тихо упадет".
V> Думаю, выбор _ASSERTE здесь был обусловлен тем, что это все же код ядра
V> и напрямую его никто вызывать не будет (а значит его можно как следует
V> отестировать)
Протестировать "как следует" невозможно ничего, если под этим понимать
некоторое абстрактное "полное" тестирование, гарантирующее отсутствие
ошибок. Снова-таки, каким бы хорошим бы ни было тестирование, какое-то
количество ошибок
останется. В любом случае, хорошее тестирование
не является аргументом для отмены других подходов увеличения надежности
программы, особенно, когда все необходимое для этого в коде уже присутствует.
Иными словами, если бы выбрасывание исключений в случае обнаружения ошибок
программирования само по себе -- заметь, я уже начинаю подсказывать

--
помогало повышать надежность системы, то, безусловно, _ASSERTE имело бы
прямой смысл превратить в release версии VM в исключения. Этого не сделали.
Соответственно, эта часть ответа на вопрос "
почему их в release
версии не превращают в исключения?" неверна. Вовсе не потому, что VM
можно хорошо протестировать, и это делается. Причина -- в другом.
(Я знаю, я так и не дал ответа на уже несколько раз прозвучавшие вопросы
о том, в чем же так часто поминаемая мной разница между исключительными
ситуациями, и ошибками программирования. Я не уверен, что смогу дать
совершенно точное формальное определение того и другого, но думаю, что
ближе к концу сообщения то, что я имею в виду, должно стать более ясным.
Давай пока для простоты примем, что _ASSERTE проверяют именно корректность
программы, т.е. обозначают именно то, что я имею в виду, говоря о диагностике
ошибок программирования. "Правильно" это или "не правильно" -- вопрос
совсем отдельный. Мы ж пока пытаемся понять друг друга, не так ли? О, это
идея, я в остальной части сообщения с тобой в "горячо" -- "холодно" играть
буду. Там, где твои реплики сильно отличаются от того, о чем я говорил, буду
писать "холодно", там где подбираются поближе -- "тепло".)
V> ну, и требованиями производительности. Все же все эти проверки потребляют
V> время, а в ядре рантайма это может оказаться критическим.
Если не строить догадки, а посмотреть на код, то можно будет увидеть, что
в подавляющем количестве мест, где используются _ASSERTE, стоимостью
выполняемых проверок можно абсолютно смело пренебречь. По крайней мере,
по сравнению с окружающим кодом. Например:
void AppDomain::ExceptionUnwind(Frame *pFrame)
{
LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n", pFrame));
#if _DEBUG_ADUNLOAD
printf("%x AppDomain::ExceptionUnwind for %8.8p\n", GetThread()->GetThreadId(), pFrame);
#endif
Thread *pThread = GetThread();
_ASSERTE(pThread);
// if the frame was pushed in managed code, then the cleanup in the managed code finally will
// already have popped returned from the context, so don't need to do anything. However, if we
// are still the current frame on an ExceptionUnwind, then we need to clean ourselves off. And if
// the frame was pushed outside of EnterContext as part of a failed attempt to enter the context
// then the return context will be null, so don't need to do anything with this frame.
Context *pReturnContext = pFrame->GetReturnContext();
if (pReturnContext && pThread->GetContext() != pReturnContext)
{
pThread->ReturnToContext(pFrame, FALSE);
}
if (! pThread->ShouldChangeAbortToUnload(pFrame))
{
LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n"));
return;
}
LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: changing to unload\n"));
BEGIN_ENSURE_COOPERATIVE_GC();
OBJECTREF throwable = NULL;
CreateExceptionObjectWithResource(kAppDomainUnloadedException, L"Remoting_AppDomainUnloaded_ThreadUnwound", &throwable);
// reset the exception to an AppDomainUnloadedException
if (throwable != NULL)
GetThread()->SetThrowable(throwable);
END_ENSURE_COOPERATIVE_GC();
}
Я не буду приводить множество других фрагментов кода VM, содержащих _ASSERTE.
Этот вполне характерен. Даже если допустить, что часть остальных фрагментов
содержит гипотетические "дорогие" проверки, вполне можно было бы ввести
две разновидности _ASSERTE, одну для "дорогих" проверок, другую -- для
"дешевых", и превращать в выбрасывание исключений только последние. Этого
тоже не сделали.
В общем, полагаю, должно быть понятно, что и вторая из названных тобой причин,
не объясняет, почему же проверки, диагностирующие ошибки программирования,
в release версии VM исключения не бросают, а просто убираются, оставляя
программу на произвол AV, как ты верно заметил.
Холодно.
V> Однако, если ты посмотришь мнее предвзято, то заметиль, что код ядра
V> генерирует не мало исключений.
О! Теплее... Теперь осталось понять, где же проходит эта самая граница,
разделяющая assert и выбрасывание исключения. Я приведу еще один фрагмент
кода, где эту разницу можно уловить чуть получше:
void BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF **apObjRefs)
{
THROWSCOMPLUSEXCEPTION();
CHECKGC();
_ASSERTE((nRequested > 0) && apObjRefs);
Thread *pThread = SetupThread();
if (NULL == pThread)
{
COMPlusThrowOM();
}
// Enter preemptive state, take the lock and go back to cooperative mode.
pThread->EnablePreemptiveGC();
m_pLargeHeapHandleTableCrst->Enter();
pThread->DisablePreemptiveGC();
EE_TRY_FOR_FINALLY
{
// Make sure the large heap handle table is initialized.
if (!m_pLargeHeapHandleTable)
InitLargeHeapHandleTable();
// Allocate the handles.
m_pLargeHeapHandleTable->AllocateHandles(nRequested, apObjRefs);
}
EE_FINALLY
{
// Release the lock now that the operation is finished.
m_pLargeHeapHandleTableCrst->Leave();
} EE_END_FINALLY;
}
ПК>> При этом проверки в критических местах уже присутствуют.
ПК>> Вопрос: почему их в release версии не превращают в исключения?
ПК>> Ответ на этот вопрос поможет понять позицию, которую я в этой теме
ПК>> озвучивал.
V> Не поможет. Так как в релизе они с вероятностью в 99% превратятся в AV
V> со всеми вытекающими.
Что ты подразумевал под словами "они"? "Исключения" или "случаи, проверяемые
с помощью _ASSERTE, если последние будут отключены, как это и происходит по
умолчанию"? Если второе -- очевидно, что во многих из мест, где расставлены
_ASSERTE, будут сгенерированы AV и т.п. Вопрос как раз в этом и заключается:
почему Microsoft не пошли по, вроде бы, перспективному по твоим словам пути,
и не превратили эти _ASSERTE в релизной версии в исключения?
Прохладно...
ПК>> Тестирование миллионами пользователей здесь вообще ни при чем, т.к.
ПК>> ошибки из разряда "вылетов" виртуальной машины пользователи
ПК>> рапортовать просто не должны.
V> Должен, не должен. А оно есть. Как факт. И ничего ты с этим не поделашь.
Так ты так и не ответил, почему же разработчики VM предпочли "вылеты"
порождению исключений? Ведь множество проверок по коду уже расставлено.
Казалось бы -- сделай в релизе
#define _ASSERTE(x) do { if (!x) throw ...; } while (0)
и VM станет надежнее. Так почему, все-таки, этого не сделали?
ПК>> Их нужно отлавливать "до того", на этапе разработки и внутреннего
ПК>> тестирования. То же самое верно и для прикладных приложений.
V> Желательно отлавливать до. Но никто от ошибок не застрахован.
V> И ассерт может быть не верным, в конц концов.
Ага. Теплее. Одна причина, наконец, названа: диагностика ошибок может
содержать ошибки, и, соответственно, может
снижать надежность
программы, внося в нее дополнительную сложность. Но, учитывая
тривиальность подавляющего количества _ASSERT, эта причина сама по себе
не могла перевесить декларируемые положительные стороны для повышения
надежности программы путем сообщения об ошибках программирования
с помощью исключений. Какие же еще причины, которые, действительно,
объясняют, почему же _ASSERTE просто-напросто выбрасывают из release
версии VM?
ПК>> Бывают исключительные ситуации, которые программа и должна
ПК>> обрабатывать: нехватка ресурсов
V> А она отчего появилась?
Из-за самых разных факторов: какая-то из программ заняла память, кончилось
место на диске и т.п. Программа обычно с этим сделать что-либо разумное
в месте обнаружения ошибки не в силах, кроме как прервать текущую операцию,
и попробовать восстановить исполнение где-то на более высоком уровне, для
чего, собственно, исключения подходят очень неплохо. Суть в том, что эти
аспекты работы программы не объясняются ошибками в ней, а являются частью
ее
предусмотренного поведения (подсказка).
ПК>> какие-то нарушения в окружении,
V> А они откуда?
Причин тоже может быть много: например, нет прав доступа к некоторому файлу,
или сетевое соединение разорвано. Это еще один характерный пример ситуаций,
которые надежная программа должна обрабатывать (подсказка: даже если
представить, что в ней совсем нет ошибок). При этом, будет это сделано
с помощью исключений или же какими-то другими способами -- влияет только
на удобство и прочие аспекты работы программиста. Это просто механизмы
выполнения того, что программа
должна делать (очень горячо, уже
не просто подсказываю, а прямо таки пальцем показываю).
ПК>> неверные данные
V> Как не верные? Значит опять ошибка програмиста?
Нет, опять холодно. Это, например, неправильный ввод пользователя (ввел
"100" там, где больше "10" вводить нельзя). Или же сбой дисковой подсистемы,
скажем, файл с данными запорчен из-за "плохого сектора". Или просто
злобный пользователь взял, да и удалил файл, в котором хранились данные
приложения. В общем, снова-таки, возникновение этих ситуаций объясняется
не ошибками в программе, а какими-то совершенно внешними факторами.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен