Как программист ошибку искал
Один день из жизни команды разработчиков
«Слипы в коде – это очень и очень плохо!» – разорялся программист Вася на совещании. «Согласен, но давай пока так оставим?» – робко парировал его коллега Антон. «Лааадно, ты прав. Так действительно проще.» – нехотя согласился Вася.sleep_for
sleep_until
Наглядный пример
Однажды для решения задачи программист Антон использовал sleep_for. Код ожидания какого-то очень нужного события выглядел примерно вот так:while(!something_happened) std::this_thread::sleep_for(std::chrono::milliseconds(100));
Что же произошло?
Первым делом, конечно же, Антон пошёл читать документацию стандартной библиотеки, а конкретнее, вызов std::this_thread::sleep_for: вдруг чего забыл сделать или флаг какой выставить? Оказалось, всё верно: просто передаёшь интервал времени и радуешься, никаких дополнительных настроек или флагов. Следующим шагом стало изучение реализации функции sleep_for в Visual Studio 2015 (именно этой средой и пользовались для сборки проекта). И именно этот шаг оказался решающим в поиске причин возникшего бага. Оказалось, что sleep_for реализован через sleep_until, и в качестве часов использовались не steady_clock, а обычные system_clock. Набрав тестовый пример с проверкой члена is_steady, наш герой убедился, что часы, которые используются для функции sleep_for являются чувствительными к подведению. Простой тест показал, что, если в момент выполнения строчки кода под номером 2 успеть перевести часы, к примеру, на полчаса назад, то и ждать поток будет не запланированные 100 миллисекунды, а целых полчаса. Если подвести часы не назад, а вперёд, то ситуация станет ещё плачевнее – поток никогда так и не проснётся. Если вам это кажется неправильным, то так оно и есть – такое поведение вызывает вопросы не только у вас. Судя по этому багу, проблема до сих пор не исправлена: ПРУФP.S. Приведённая выше история является вымышленной, все совпадения событий и действующих лиц случайны.