ฉันกำลังดิ้นรนกับมาตรา 5.1.2.4 ของมาตรฐาน C11 โดยเฉพาะความหมายของการเปิดตัว / การได้มา ฉันทราบว่าhttps://preshing.com/20120913/acquire-and-release-semantics/ (ในจำนวนอื่น ๆ ) ระบุว่า:
... ความหมายของการวางจำหน่ายป้องกันการเรียงลำดับหน่วยความจำของการปล่อยการเขียนด้วยการดำเนินการอ่านหรือการเขียนที่นำหน้ามันตามลำดับของโปรแกรม
ดังนั้นสำหรับต่อไปนี้:
typedef struct test_struct
{
_Atomic(bool) ready ;
int v1 ;
int v2 ;
} test_struct_t ;
extern void
test_init(test_struct_t* ts, int v1, int v2)
{
ts->v1 = v1 ;
ts->v2 = v2 ;
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
}
extern int
test_thread_1(test_struct_t* ts, int v2)
{
int v1 ;
while (atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v2 = v2 ; // expect read to happen before store/release
v1 = ts->v1 ; // expect write to happen before store/release
atomic_store_explicit(&ts->ready, true, memory_order_release) ;
return v1 ;
}
extern int
test_thread_2(test_struct_t* ts, int v1)
{
int v2 ;
while (!atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v1 = v1 ;
v2 = ts->v2 ; // expect write to happen after store/release in thread "1"
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
return v2 ;
}
ที่จะถูกดำเนินการ:
> in the "main" thread: test_struct_t ts ;
> test_init(&ts, 1, 2) ;
> start thread "2" which does: r2 = test_thread_2(&ts, 3) ;
> start thread "1" which does: r1 = test_thread_1(&ts, 4) ;
ดังนั้นฉันคาดว่าเธรด "1" จะมี r1 == 1 และเธรด "2" เพื่อให้มี r2 = 4
ฉันคาดหวังว่าเพราะ (ดังต่อไปนี้กาฝาก 16 และ 18 ของภาค 5.1.2.4):
- การอ่านและการเขียน (ไม่ใช่อะตอมมิก) ทั้งหมดนั้นเป็น "ลำดับก่อนหน้า" และด้วยเหตุนี้ "เกิดขึ้นก่อน" อะตอมเขียน / รีลีสในเธรด "1"
- สิ่งที่ "อินเตอร์เธรดเกิดขึ้นก่อน" อะตอมอ่าน / รับในเธรด "2" (เมื่ออ่าน 'จริง')
- ซึ่งในทางกลับกันคือ "sequenced before" และด้วยเหตุนี้ "เกิดขึ้นก่อน" (ไม่ใช่ atomic) อ่านและเขียน (ใน thread "2")
อย่างไรก็ตามเป็นไปได้ทั้งหมดที่ฉันไม่เข้าใจมาตรฐาน
ฉันสังเกตว่ารหัสที่สร้างขึ้นสำหรับ x86_64 รวมถึง:
test_thread_1:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
jne <test_thread_1> -- while is true
mov %esi,0x8(%rdi) -- (W1) ts->v2 = v2
mov 0x4(%rdi),%eax -- (R1) v1 = ts->v1
movb $0x1,(%rdi) -- (X1) atomic_store_explicit(&ts->ready, true, memory_order_release)
retq
test_thread_2:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
je <test_thread_2> -- while is false
mov %esi,0x4(%rdi) -- (W2) ts->v1 = v1
mov 0x8(%rdi),%eax -- (R2) v2 = ts->v2
movb $0x0,(%rdi) -- (X2) atomic_store_explicit(&ts->ready, false, memory_order_release)
retq
และหากว่า R1 และ X1 เกิดขึ้นตามลำดับสิ่งนี้จะให้ผลลัพธ์ที่ฉันคาดหวัง
แต่ความเข้าใจของฉันเกี่ยวกับ x86_64 คือการอ่านเกิดขึ้นตามลำดับกับการอ่านและการเขียนอื่น ๆ เกิดขึ้นตามลำดับกับการเขียนอื่น ๆ แต่การอ่านและการเขียนอาจไม่เกิดขึ้นตามลำดับ ซึ่งหมายความว่ามันเป็นไปได้สำหรับ X1 ที่จะเกิดขึ้นก่อน R1 และแม้กระทั่งสำหรับ X1, X2, W2, R1 ที่จะเกิดขึ้นตามลำดับ - ฉันเชื่อว่า [ดูเหมือนว่าไม่น่าจะหมดหวัง แต่หาก R1 มีปัญหาเกี่ยวกับแคชบ้าง]
ได้โปรด: ฉันไม่เข้าใจอะไร
ผมทราบว่าถ้าผมเปลี่ยนโหลด / ร้านค้าของts->ready
ที่จะmemory_order_seq_cst
รหัสที่สร้างขึ้นสำหรับร้านค้าคือ
xchg %cl,(%rdi)
ซึ่งสอดคล้องกับความเข้าใจของฉันใน x86_64 และจะให้ผลลัพธ์ที่ฉันคาดหวัง
8.2.3.3 Stores Are Not Reordered With Earlier Loads
, ดังนั้นคอมไพเลอร์ของคุณแปลโค้ดของคุณอย่างถูกต้อง (น่าแปลกใจมาก) เช่นนั้นโค้ดของคุณนั้นเรียงตามลำดับอย่างสมบูรณ์และไม่มีอะไรน่าสนใจเกิดขึ้นพร้อมกัน