Uyarı C26837
işlevi
func
için comparandcomp
değeri geçici olmayan okuma yoluyla hedef konumdandest
yüklendi.
Bu kural Visual Studio 2022 17.8'e eklendi.
Açıklamalar
InterlockedCompareExchange
işlevi ve gibi InterlockedCompareExchangePointer
türevleri, belirtilen değerler üzerinde atomik bir karşılaştırma ve değişim işlemi gerçekleştirir. Destination
Değer değere Comparand
eşitse, exchange değeri tarafından Destination
belirtilen adreste depolanır. Aksi takdirde hiçbir işlem gerçekleştirilmez. İşlevler, interlocked
birden çok iş parçacığı tarafından paylaşılan bir değişkene erişimi eşitlemek için basit bir mekanizma sağlar. Bu işlev, diğer interlocked
işlevlere yapılan çağrılara göre atomiktir. İyileştirme kodun davranışını beklenmeyen yollarla değiştirebileceğinden, bu işlevlerin kötüye kullanılması beklediğinizden farklı davranan nesne kodu oluşturabilir.
Aşağıdaki kodu inceleyin:
#include <Windows.h>
bool TryLock(__int64* plock)
{
__int64 lock = *plock;
return (lock & 1) &&
_InterlockedCompareExchange64(plock, lock & ~1, lock) == lock;
}
Bu kodun amacı:
- İşaretçiden
plock
geçerli değeri okuyun. - Bu geçerli değerin en az önemli bit kümesine sahip olup olmadığını denetleyin.
- En az önemli bit kümesi varsa, geçerli değerin diğer bitlerini korurken biti temizleyin.
Bunu yapmak için, geçerli değerin bir kopyası işaretçiden plock
okunur ve bir yığın değişkenine lock
kaydedilir. lock
üç kez kullanılır:
- İlk olarak, en az önemli bitin ayarlı olup olmadığını denetlemek için.
- İkincisi, değerine değer
InterlockedCompareExchange64
olarakComparand
. - Son olarak, döndürülen değerin karşılaştırmasında
InterlockedCompareExchange64
Bu, yığın değişkenine kaydedilen geçerli değerin işlevin başlangıcında bir kez okunduğunu ve değişmediğini varsayar. Geçerli değer, işlemi denemeden önce önce denetlendiğinden, sonra açıkça içinde InterlockedCompareExchange64
olarak kullanıldığından Comparand
ve son olarak değerinden InterlockedCompareExchange64
döndürülen değeri karşılaştırmak için kullanıldığından bu gereklidir.
Ne yazık ki, önceki kod, kaynak koddan beklediğinizden farklı davranan derlemede derlenebilir. Önceki kodu Microsoft Visual C++ (MSVC) derleyicisi ve /O1
seçeneğiyle derleyin ve başvurulardan her biri lock
için kilidin değerinin nasıl alınıp alınıp alınılmaması için sonuç derleme kodunu inceleyin. MSVC derleyici sürümü v19.37 aşağıdakine benzer bir derleme kodu oluşturur:
plock$ = 8
bool TryLock(__int64 *) PROC ; TryLock, COMDAT
mov r8b, 1
test BYTE PTR [rcx], r8b
je SHORT $LN3@TryLock
mov rdx, QWORD PTR [rcx]
mov rax, QWORD PTR [rcx]
and rdx, -2
lock cmpxchg QWORD PTR [rcx], rdx
je SHORT $LN4@TryLock
$LN3@TryLock:
xor r8b, r8b
$LN4@TryLock:
mov al, r8b
ret 0
bool TryLock(__int64 *) ENDP ; TryLock
rcx
parametresinin plock
değerini tutar. Derleme kodu, yığındaki geçerli değerin bir kopyasını oluşturmak yerine değeri her seferinde yeniden okuyor plock
. Bu, değerin her okunışında farklı olabileceği anlamına gelir. Bu, geliştiricinin gerçekleştirdiği temizleme işlemini geçersiz kılır. Değeri, en az önemli bit kümesine sahip olduğu doğrulandıktan sonra yeniden okunur plock
. Bu doğrulama gerçekleştirildikten sonra yeniden okunduğu için, yeni değer artık en az önemli bit kümesine sahip olmayabilir. Bir yarış koşulu altında, bu kod başka bir iş parçacığı tarafından zaten kilitliyken belirtilen kilidi başarıyla almış gibi davranabilir.
Kodun davranışı değiştirilmediği sürece derleyicinin bellek okumalarını veya yazmalarını kaldırmasına veya eklemesine izin verilir. Derleyicinin bu tür değişiklikler yapmasını önlemek için, değeri bellekten okuduğunuzda ve bir değişkende önbelleğe aldığınızda okumaları olmaya volatile
zorlayın. olarak volatile
bildirilen nesneler, değerleri istedikleri zaman değişebildiğinden belirli iyileştirmelerde kullanılmaz. Oluşturulan kod, önceki bir volatile
yönerge aynı nesneden bir değer istese bile, istendiğinde her zaman nesnenin geçerli değerini okur. Tersi aynı nedenle de geçerlidir. nesnenin volatile
değeri istenmediği sürece yeniden okunmuyor. hakkında volatile
daha fazla bilgi için bkz volatile
. . Örneğin:
#include <Windows.h>
bool TryLock(__int64* plock)
{
__int64 lock = *static_cast<volatile __int64*>(plock);
return (lock & 1) &&
_InterlockedCompareExchange64(plock, lock & ~1, lock) == lock;
}
Bu kodu daha önce olduğu gibi aynı /O1
seçenekle derleyin. Oluşturulan derleme artık içinde önbelleğe alınmış değerin kullanımı için okumaz plock
lock
.
Kodun nasıl düzeltilebileceğine ilişkin daha fazla örnek için bkz . Örnek.
Kod analizi adı: INTERLOCKED_COMPARE_EXCHANGE_MISUSE
Örnek
Derleyici, içinde önbelleğe alınmış değeri lock
kullanmak yerine aşağıdaki kodu birden çok kez okuyacak plock
şekilde iyileştirir:
#include <Windows.h>
bool TryLock(__int64* plock)
{
__int64 lock = *plock;
return (lock & 1) &&
_InterlockedCompareExchange64(plock, lock & ~1, lock) == lock;
}
Sorunu çözmek için, derleyicinin açıkça belirtilmediği sürece kodu aynı bellekten ardışık olarak okunacak şekilde iyileştirmemesi için okumaları olmaya volatile
zorlayın. Bu, iyileştiricinin beklenmeyen davranışlar ortaya çıkarmasını engeller.
Belleği şöyle volatile
ele almak için ilk yöntem, hedef adresi işaretçi olarak volatile
almaktır:
#include <Windows.h>
bool TryLock(volatile __int64* plock)
{
__int64 lock = *plock;
return (lock & 1) &&
_InterlockedCompareExchange64(plock, lock & ~1, lock) == lock;
}
İkinci yöntem, hedef adresten okuma kullanır volatile
. Bunu yapmanın birkaç farklı yolu vardır:
- İşaretçinin başvurularını
volatile
kaldırmadan önce işaretçiyi işaretçiye atama - Sağlanan işaretçiden işaretçi
volatile
oluşturma - Okuma yardımcı işlevlerini kullanma
volatile
.
Örneğin:
#include <Windows.h>
bool TryLock(__int64* plock)
{
__int64 lock = ReadNoFence64(plock);
return (lock & 1) &&
_InterlockedCompareExchange64(plock, lock & ~1, lock) == lock;
}
Buluşsal yöntemler
Bu kural, işlevin içindeki Destination
değerin veya türevlerinden herhangi birinin okunmayan InterlockedCompareExchange
volatile
bir değer üzerinden yüklenip yüklenmediğini algılayarak uygulanır ve ardından değer olarak Comparand
kullanılır. Ancak, yüklenen değerin exchange değerini belirlemek için kullanılıp kullanılmadığını açıkça denetlemez. Exchange değerinin değerle Comparand
ilişkili olduğunu varsayar.
Ayrıca bkz.
InterlockedCompareExchange
function (winnt.h)
_InterlockedCompareExchange
iç işlevler