Ý tưởng đằng sau mô hình/cửa hàng độc quyền tải liên kết là nếu nếu các cửa hàng sau rất sớm sau khi tải, không có hoạt động bộ nhớ can thiệp, và nếu không có gì khác đã chạm vào vị trí, cửa hàng là có khả năng để thành công, nhưng nếu có điều gì đó khác đã chạm vào vị trí, cửa hàng là nhất định để không thành công. Không có gì đảm bảo rằng các cửa hàng sẽ không đôi khi thất bại vì không có lý do rõ ràng; nếu thời gian giữa tải và lưu trữ được giữ ở mức tối thiểu, tuy nhiên, và không có bộ nhớ truy cập giữa chúng, một vòng lặp như:
do
{
new_value = __LDREXW(dest) + 1;
} while (__STREXW(new_value, dest));
nói chung có thể được dựa vào để thành công trong một vài lần.Nếu tính giá trị mới dựa trên giá trị cũ yêu cầu một số tính toán có ý nghĩa, ta nên viết lại vòng lặp như:
do
{
old_value = *dest;
new_value = complicated_function(old_value);
} while (CompareAndStore(dest, new_value, old_value) != 0);
... Assuming CompareAndStore is something like:
uint32_t CompareAndStore(uint32_t *dest, uint32_t new_value, uint_32 old_value)
{
do
{
if (__LDREXW(dest) != old_value) return 1; // Failure
} while(__STREXW(new_value, dest);
return 0;
}
Mã này sẽ phải chạy lại vòng lặp chính của nó nếu có điều gì thay đổi * dest trong khi giá trị mới đang được tính toán , nhưng chỉ vòng lặp nhỏ sẽ cần phải được chạy lại nếu __STREXW thất bại vì một lý do khác [đó là hy vọng không quá khả năng, cho rằng đó sẽ chỉ có khoảng hai hướng dẫn giữa __LDREXW và __STREXW]
Phụ Lục Một ví dụ về một tình huống mà "tính toán giá trị mới dựa trên cũ" có thể phức tạp sẽ là một trong những nơi "giá trị" ar hiệu quả một tham chiếu đến một cấu trúc dữ liệu phức tạp. Mã có thể tìm nạp tham chiếu cũ, lấy được cấu trúc dữ liệu mới từ cũ và sau đó cập nhật tham chiếu. Mô hình này xuất hiện thường xuyên hơn trong các khung được thu gom rác hơn là trong lập trình "kim loại trần", nhưng có nhiều cách nó có thể xuất hiện ngay cả khi lập trình kim loại trần. Các trình phân bổ malloc/calloc bình thường thường không an toàn chủ đề/an toàn ngắt, nhưng các trình phân bổ cho các cấu trúc kích thước cố định thường là. Nếu ta có một "hồ bơi" của một số điện-of-hai số cấu trúc dữ liệu (nói 255), người ta có thể sử dụng một cái gì đó như:
#define FOO_POOL_SIZE_SHIFT 8
#define FOO_POOL_SIZE (1 << FOO_POOL_SIZE_SHIFT)
#define FOO_POOL_SIZE_MASK (FOO_POOL_SIZE-1)
void do_update(void)
{
// The foo_pool_alloc() method should return a slot number in the lower bits and
// some sort of counter value in the upper bits so that once some particular
// uint32_t value is returned, that same value will not be returned again unless
// there are at least (UINT_MAX)/(FOO_POOL_SIZE) intervening allocations (to avoid
// the possibility that while one task is performing its update, a second task
// changes the thing to a new one and releases the old one, and a third task gets
// given the newly-freed item and changes the thing to that, such that from the
// point of view of the first task, the thing never changed.)
uint32_t new_thing = foo_pool_alloc();
uint32_t old_thing;
do
{
// Capture old reference
old_thing = foo_current_thing;
// Compute new thing based on old one
update_thing(&foo_pool[new_thing & FOO_POOL_SIZE_MASK],
&foo_pool[old_thing & FOO_POOL_SIZE_MASK);
} while(CompareAndSwap(&foo_current_thing, new_thing, old_thing) != 0);
foo_pool_free(old_thing);
}
Nếu có sẽ không thường xuyên có nhiều chủ đề/ngắt/bất cứ cố gắng cập nhật cùng một điều cùng một lúc, phương pháp này nên cho phép cập nhật được thực hiện một cách an toàn. Nếu mối quan hệ ưu tiên sẽ tồn tại trong số những thứ có thể cố gắng cập nhật cùng một mục, ưu tiên cao nhất được đảm bảo thành công trong lần thử đầu tiên, mối quan hệ ưu tiên cao nhất sẽ thành công trên bất kỳ nỗ lực nào không được ưu tiên ưu tiên cao nhất, vv Nếu một người đang sử dụng khóa, tác vụ có mức độ ưu tiên cao nhất muốn thực hiện cập nhật sẽ phải đợi bản cập nhật ưu tiên thấp hơn kết thúc; bằng cách sử dụng mô hình CompareAndSwap, nhiệm vụ có mức ưu tiên cao nhất sẽ không bị ảnh hưởng bởi tác vụ thấp hơn (nhưng sẽ khiến tác vụ thấp hơn phải làm công việc lãng phí).
Tôi đã làm chính xác điều tương tự nhưng phần mà tính toán đáng kể là cần thiết cho giá trị mới, vẫn còn câu đố cho tôi. Sử dụng vòng lặp cmxchg có ý nghĩa bởi vì sau đó màn hình độc quyền sẽ không bị xóa bởi một chuyển ngữ cảnh nhưng việc thực hiện lại máy tính đáng kể đòi hỏi rất nhiều chi phí khi tôi quan sát đường phố thất bại mà không có lý do rõ ràng (UP với IRQ được che dấu trong PSR) như đã đề cập trong bài đăng của bạn. – sgupta
@ user1075375: Xem phụ lục – supercat
Những (__LDREXW & __STREXW) được hỗ trợ nội tại trong trình biên dịch Keil cho bộ xử lý vi điều khiển dòng Cortex-M, thường không có sẵn cho các mục tiêu ARM chính (ví dụ, AArch64) và trình biên dịch (ví dụ: gcc, llvm) đúng? http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/BABDEEJC.html – ahcox