Этот код небезопасен практически при любых обстоятельствах. На многоядерных процессорах у вас не будет когерентности кэша между ядрами, потому что чтение и запись bool не являются атомарными операциями. Это означает, что каждому ядру не гарантируется одинаковое значение в кэше или даже в памяти, если кэш из последней записи не был очищен.
Однако даже на одноядерных устройствах с ограниченными ресурсами это небезопасно, поскольку у вас нет контроля над планировщиком. Вот пример, для простоты я собираюсь притвориться, что это только два потока на устройстве.
Когда ui_thread запускается, следующие строки кода могут быть запущены в одном и том же временном интервале.
// new Thread(downloader_thread).start();
// ...
should_pause = true;
Далее запускается нить downloader_thread, и во временном интервале выполняются следующие строки:
quitted = false;
while(!should_quit)
{
fill_buffer(bfr);
Планировщик выгружает downloader_thread перед возвратом fill_buffer, а затем активирует выполняемый ui_thread.
while(!is_paused) sleep(50);
// resize buffer or something else non-thread-safe
should_pause = false;
Операция изменения размера буфера выполняется, когда downloader_thread находится в процессе заполнения буфера. Это означает, что буфер поврежден, и вы, скорее всего, скоро выйдете из строя. Это не будет происходить каждый раз, но тот факт, что вы заполняете буфер перед тем, как установить is_paused в значение true, повышает вероятность того, что это произойдет, но даже если вы изменили порядок этих двух операций на downloader_thread, у вас все равно будет состояние гонки , но вы скорее всего зашли бы в тупик, а не повредили буфер.
Кстати, это тип спин-блокировки, он просто не работает. Спинлоки не очень подходят для времени ожидания, которое может охватывать много временных интервалов, вызывающих вращение процессора. Ваша имплементация действительно спит, что немного лучше, но планировщик все равно должен запустить ваш поток, и переключатели контекста потока недешевы. Если вы ожидаете критического раздела или семафора, планировщик не активирует ваш поток снова, пока ресурс не станет свободным.
Возможно, вам удастся избежать этого в той или иной форме на конкретной платформе / архитектуре, но действительно легко совершить ошибку, которую очень трудно отследить.