2013-02-21 35 views
7

Tình huống tôi sẽ mô tả diễn ra trên iPad 4 (ARMv7), sử dụng libix libix để khóa/mở khóa mutex. Tôi đã nhìn thấy những điều tương tự trên các thiết bị ARMv7 khác, mặc dù (xem dưới đây), vì vậy tôi cho rằng bất kỳ giải pháp sẽ yêu cầu một cái nhìn tổng quát hơn về hành vi của mutexes và hàng rào bộ nhớ cho ARMv7.Chức năng mutex_unlock có phải là hàng rào bộ nhớ không?

Pseudo mã cho kịch bản:

Chủ đề 1 - Sản xuất dữ liệu:

void ProduceFunction() { 
    MutexLock(); 
    int TempProducerIndex = mSharedProducerIndex; // Take a copy of the int member variable for Producers Index 
    mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
    mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable 
    MutexUnlock(); 
} 

Chủ đề 2 - Tiêu thụ dữ liệu:

void ConsumingFunction() { 
    while (mConsumerIndex != mSharedProducerIndex) { 
    doWorkOnData (mSharedArray[mConsumerIndex++]); 
    } 
} 

Trước đây (khi vấn đề nảy sinh trên iPad 2), tôi tin rằng mSharedProducerIndex = TempProducerIndex không phải là sự hoàn hảo ormed atomically, và do đó thay đổi để sử dụng một AtomicCompareAndSwap để gán mSharedProducerIndex. Điều này đã làm việc cho đến thời điểm này, nhưng nó chỉ ra tôi đã sai và lỗi đã trở lại. Tôi đoán 'sửa' chỉ thay đổi một chút thời gian.

Bây giờ tôi đã đi đến kết luận rằng vấn đề thực tế là một trong số thực hiện lệnh của viết trong khóa mutex, tức là nếu một trong hai trình biên dịch hoặc phần cứng quyết định sắp xếp lại:

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable 

.. . đến:

mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable 
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 

... và sau đó người tiêu dùng xen kẽ nhà sản xuất, dữ liệu sẽ chưa được viết khi người tiêu dùng cố đọc nó.

Sau khi một số đọc trên hàng rào bộ nhớ, tôi do đó nghĩ rằng tôi muốn thử di chuyển tín hiệu đến người tiêu dùng bên ngoài mutex_unlock, tin rằng mở khóa sẽ tạo ra một ký ức hàng rào/hàng rào đó sẽ đảm bảo mSharedArray đã được ghi vào:

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
MutexUnlock(); 
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable 

Điều này, tuy nhiên, vẫn không thành công và dẫn tôi đến câu hỏi nếu một mutex_unlock chắc chắn sẽ hoạt động như hàng rào viết hay không?

Tôi cũng đã đọc an article from HP đề xuất rằng trình biên dịch có thể di chuyển mã vào (nhưng không phải trong số) crit_sec s. Vì vậy, ngay cả sau khi thay đổi ở trên, việc viết mSharedProducerIndex có thể trước hàng rào. Có bất kỳ số dặm nào cho lý thuyết này không?

Bằng cách thêm vào một hàng rào rõ ràng vấn đề đi xa:

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
OSMemoryBarrier(); 
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable 

Vì vậy, tôi nghĩ rằng tôi hiểu được vấn đề, và rằng một hàng rào là cần thiết, nhưng bất cứ cái nhìn sâu sắc vào các hành vi của các khóa và tại sao nó doesn' dường như thực hiện một rào cản sẽ thực sự hữu ích.

EDIT:

Về việc thiếu một mutex trong chủ đề của người tiêu dùng: Tôi dựa vào các ghi của int mSharedProducerIndex là một chỉ dẫn duy nhất và do đó hy vọng người tiêu dùng sẽ đọc một trong hai giá trị mới hay cũ . Hoặc là các trạng thái hợp lệ và cung cấp rằng mSharedArray được viết theo thứ tự (tức làtrước khi viết mSharedProducerIndex) điều này sẽ ổn, nhưng từ những gì đã được nói cho đến nay, tôi không thể trả lời về điều này.

Bằng cùng một logic, có vẻ như giải pháp rào cản hiện tại cũng bị thiếu sót, vì ghi mSharedProducerIndex có thể được di chuyển bên trong hàng rào và do đó có thể được sắp xếp lại không chính xác.

Bạn có nên thêm mutex vào người tiêu dùng, chỉ để hoạt động như một rào cản đọc hay có pragma hoặc hướng dẫn vô hiệu hóa việc thực hiện không theo trật tự trên nhà sản xuất, chẳng hạn như EIEIO trên PPC?

+2

Bạn chỉ có thể thực hiện OSMemoryBarrier() ngay sau khi * đọc * 'mSharedProducerIndex', như Steve gợi ý. Thử nghiệm trước đó cho thấy OSMemoryBarrier() nhanh hơn OSSpinLockLock + Unlock() nhanh hơn nhiều so với các mutex. –

Trả lời

5

Sản phẩm của bạn được đồng bộ hóa nhưng bạn không thực hiện bất kỳ đồng bộ hóa nào (bạn cũng cần phải đồng bộ hóa bộ nhớ với các rào cản) khi sử dụng. Vì vậy, ngay cả khi bạn có rào cản bộ nhớ hoàn hảo cho nhà sản xuất thì rào cản bộ nhớ sẽ không giúp người tiêu dùng.

Trong mã của bạn, bạn có thể bị ảnh hưởng bởi thứ tự của trình biên dịch, thứ tự phần cứng ngay cả với giá trị cũ của mSharedProducerIndex trên lõi khác đang chạy Thread # 2.

Bạn nên đọc Chapter 11: Memory Ordering từ Cortex™-A Series Programmer’s Guide, đặc biệt là 11.2.1 Memory barrier use example.

Tôi nghĩ rằng vấn đề của bạn là bạn đang nhận được cập nhật một phần trong chuỗi người tiêu dùng. Vấn đề là bên trong phần quan trọng trong nhà sản xuất không phải là nguyên tử và nó có thể được sắp xếp lại.

Bằng not atomic Ý tôi là nếu mSharedArray[TempProducerIndex++] = NewData; của bạn không phải là cửa hàng từ (NewData có loại int), nó có thể được thực hiện trong một vài bước.

By reordering Tôi muốn nói là mutex cung cấp các rào cản trong và ngoài nhưng không áp đặt bất kỳ thứ tự nào trong phần quan trọng. Vì bạn không có bất kỳ cấu trúc đặc biệt nào ở phía người tiêu dùng, bạn có thể thấy mSharedProducerIndex được cập nhật nhưng vẫn thấy các cập nhật một phần cho mSharedArray[mConsumerIndex]. Mutex chỉ đảm bảo khả năng hiển thị bộ nhớ sau khi thực hiện rời khỏi phần quan trọng.

Tôi tin rằng điều này cũng giải thích lý do tại sao nó hoạt động khi bạn thêm OSMemoryBarrier(); bên phần quan trọng, bởi vì cách cpu này buộc phải ghi dữ liệu vào mSharedArray sau đó cập nhật mConsumerIndex và khi khác core/thread thấy mConsumerIndex chúng ta biết rằng mSharedArray được sao chép hoàn toàn vì của rào chắn.

Tôi nghĩ triển khai của bạn với OSMemoryBarrier(); là chính xác giả sử bạn có nhiều nhà sản xuất và một người tiêu dùng. Tôi không đồng ý với bất kỳ ý kiến ​​đề xuất đặt một rào cản bộ nhớ trong người tiêu dùng, vì tôi tin rằng sẽ không sửa chữa một phần cập nhật hoặc sắp xếp lại xảy ra trong phần quan trọng bên trong nhà sản xuất.

Là câu trả lời cho câu hỏi của bạn trong tiêu đề, nói chung, các nhân viên đã đọc rào cản trước khi họ nhập và viết rào cản sau khi họ rời khỏi.

+0

cảm ơn cho đầu vào của bạn cho đến nay, tôi đã thực hiện một chỉnh sửa cho câu hỏi mà hy vọng sẽ làm rõ điều này một chút. – sjwarner

+0

@sjwarner đã cập nhật câu trả lời của tôi. – auselen

+0

@sjwarner nếu mSharedArray không phải là một mảng ints, tôi nghĩ rằng bạn vẫn có thể có vấn đề về khả năng hiển thị do đó tôi khuyên bạn nên đặt một rào cản đọc trong chuỗi tiêu dùng - chỉ ở đầu vòng lặp. Nó có thể là bộ nhớ cache của lõi trong quá trình cập nhật và bạn sẽ không chờ đợi nó - trái ngược với những gì một rào cản đọc làm. – auselen

5

"Lý thuyết" là chính xác, viết có thể được di chuyển từ sau khi viết hàng rào đến trước nó.

Vấn đề cơ bản với mã của bạn là không có đồng bộ hóa nào trong chủ đề 2. Bạn đọc mSharedProducerIndex mà không có rào cản đọc, vì vậy ai biết bạn sẽ nhận được giá trị nào. Không có gì mà bạn làm trong chuỗi 1 sẽ giải quyết điều đó.

+0

Xin lỗi, tôi đã đơn giản hóa mã giả của tôi, sẽ thực hiện chỉnh sửa sau giây lát. Chúc mừng cho câu trả lời cho bây giờ, mặc dù. – sjwarner

+0

Chỉnh sửa được thực hiện, hy vọng nó làm rõ tình hình. Chúc mừng :) – sjwarner

Các vấn đề liên quan