У меня есть одноэлементный класс, предназначенный для использования в одном потоке (поток с графическим интерфейсом), чтобы он был защищен от неправильного использования. Я добавляю assert
//header file
class ImageCache final {
public:
ImageCache(const ImageCache &) = delete;
ImageCache &operator=(const ImageCache &) = delete;
static ImageCache &instance()
{
static ImageCache cache;
return cache;
}
void f();
private:
QThread *create_context_ = nullptr;
ImageCache();
};
//cpp
ImageCache::ImageCache()
{
create_context_ = QThread::currentThread();
qInfo("begin, cur thread %p\n", create_context_);
}
void ImageCache::f()
{
assert(create_context_ == QThread::currentThread());
}
все работает нормально, но на одном компьютере есть утверждениеошибка в ImageCache::f
, у меня нет прямого доступа к этой машине (отсюда и этот вопрос).
Интересно, что по логу ImageCache::ImageCache
вообще не вызывался, аОшибка assert из-за
assert(0 == QThread::currentThread());
Я перемещаю реализацию ImageCache::instance
из файла заголовка в файл .cpp
, отправляю обновленный исходный код пользователю этой машины (на моем все работает нормально), он восстанавливает и все начинает работать, как и ожидалось.
Я прошу его для скомпилированных двоичных файлов (с ошибкой подтверждения и без), единственное различие между ними - место реализации ImageCache::instance
,
исравните ассемблер.
нет разницы между вызовами ImageInstance::instance().f()
вообще, и есть одно отличие в дизассемблере ImageInstance::instance
,
, сбой выглядит так:
static ImageCache &instance()
4938f: 55 push %rbp
49390: 48 89 e5 mov %rsp,%rbp
49393: 41 54 push %r12
49395: 53 push %rbx
{
static ImageCache cache;
49396: 48 8b 05 bb db 23 00 mov 0x23dbbb(%rip),%rax # 286f58 <_ZGVZN10ImageCache8instanceEvE5cache@@Base-0x2150>
4939d: 0f b6 00 movzbl (%rax),%eax
493a0: 84 c0 test %al,%al
493a2: 0f 94 c0 sete %al
493a5: 84 c0 test %al,%al
493a7: 74 5c je 49405 <_ZN10ImageCache8instanceEv+0x76>
493a9: 48 8b 05 a8 db 23 00 mov 0x23dba8(%rip),%rax # 286f58 <_ZGVZN10ImageCache8instanceEvE5cache@@Base-0x2150>
493b0: 48 89 c7 mov %rax,%rdi
493b3: e8 08 b7 fe ff callq 34ac0 <__cxa_guard_acquire@plt>
хороший яs выглядит так:
ImageCache &ImageCache::instance()
{
50c12: 55 push %rbp
50c13: 48 89 e5 mov %rsp,%rbp
50c16: 41 54 push %r12
50c18: 53 push %rbx
static ImageCache cache;
50c19: 0f b6 05 98 94 23 00 movzbl 0x239498(%rip),%eax # 28a0b8 <_ZGVZN10ImageCache8instanceEvE5cache>
50c20: 84 c0 test %al,%al
50c22: 0f 94 c0 sete %al
50c25: 84 c0 test %al,%al
50c27: 74 50 je 50c79 <_ZN10ImageCache8instanceEv+0x67>
50c29: 48 8d 3d 88 94 23 00 lea 0x239488(%rip),%rdi # 28a0b8 <_ZGVZN10ImageCache8instanceEvE5cache>
50c30: e8 cb 3d fe ff callq 34a00 <__cxa_guard_acquire@plt>
Разница составляет
//bad
mov 0x23dbbb(%rip),%rax
movzbl (%rax),%eax
//good
movzbl 0x239498(%rip),%eax
Я понимаю, что по какой-то причине %eax
регистр из первого варианта получил неправильное значение, и из-заэто решило, что глобальный объект был инициализирован, в то время как он не инициализирован.Во втором случае все работает, как и ожидалось.
Так что это сбой компилятора (gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 / amd64 / linux
) или я должен использовать ImageCache :: instance внутри .cpp
по какой-то причине или по какой-то другой причине, которая вызывает генерацию разностного кодаКак некоторые ошибки компилятора могут вызвать этот сбой?Код был скомпилирован с -O0 -std=c++11
и некоторыми другими флагами, которые cmake добавляет автоматически при компиляции разделяемой библиотеки с зависимостью от библиотеки Qt.
Также я прошу тестовый код с использованием fprintf(stderr
вместо qInfo
, ипользователь видит вывод во втором случае и не выводит в первом случае.