[英]atomic linked-list LIFO in AArch64 assembly, using load or store between ldxr / stxr
我使用 ARMv8 64 位的程序集为共享的 memory 上下文实现了 LIFO。
LIFO 在开头插入一个节点,每个节点结构的第一个属性必须是下一个指针。
这是为 LIFO 实现原子插入和删除的正确程序集吗?
它使用LL/SC在 LDXR 和 STXR 之间进行额外的加载或存储来读取 head->next 或将指针存储到新节点中。
typedef union {
void * head[1];
}lifo;
int atomic_lifo_init(lifo * h) {
if (h) {
h->head[0]=NULL;
}
}
inline void *
atomic_lifo_delete (lifo *h)
{
void *ret = NULL;
/*sa_ignore UNUSED_VAR*/
void * tmp = NULL;
asm volatile ("\n"
"2: ldxr %0,[%2] \n" //load the head from h, which points the 1st node of list to local ret pointer.
" cbz %0, 3f \n" //check if the lifo is empty.
" ldr %1,[%0] \n" //store in tmp the 2nd node by derefencing the ret (h->head points 1st node. value of each node is next node as 1st attribute of node structure is pointing next.)
" stxr %w1, %1,[%2] \n" //update h->head with tmp.
" cbnz %w1, 2b \n" //continue if failed
"3: \n"
: "=&r" (ret), "=&r" (tmp)
: "r" (h)
: "memory"
);
return(ret);
}
/*
* atomic_lifo_insert()
* Put an element on a list, protected against SMP
*/
void
atomic_lifo_insert (lifo *h, void *__new)
{
/*sa_ignore UNUSED_VAR*/
void * next = NULL;
void * flag = NULL;
asm volatile (" \n"
"1: ldxr %1,[%2] \n" //load head[0] from h,which points 1st node to local next pointer
" str %1,[%3] \n" //store the local next pointer to value of __new, as 1st attribute of the any node is next (convention used here). so __new's next is pointing current 1st node.
" stxr %w0, %3,[%2] \n" //update the h->head with
__next.
" cbnz %w0, 1b \n" //if stxr is failure try again.
: "=&r" (flag), "=&r" (next)
: "r" (h), "r" (__new)
: "memory"
);
}
我对 ARM 组件真的很陌生,所以非常感谢任何帮助。
您的内联 asm 约束看起来正确,这应该按照您的预期方式编译。 您可能可以使用"+m" (*h)
让编译器选择寻址模式,而不是使用"r"(h)
和[%2]
对其进行硬编码。
就排序而言, ldr
在 ldxr 之后是依赖排序的(如 C11 memory_order_consume
),因此有效。
但是在stxr
将地址发布到其他线程之后, insert
中的 LL/SC 之间的str
可能不会变得可见。 所以我认为你需要stlxr
(一个发布商店)在insert
。
在 LDXR/STXR 之间进行额外的加载或存储是不安全的。 Wikipedia 的LL/SC 文章提到,如果您在 LL 和 SC 之间进行任何加载或存储,某些实现可能会虚假失败。 Wiki 说 PowerPC 确实允许这样做。 但 AArch64 通常明确不:根据 ARM 参考手册( 请参阅@James Greenhalgh 的评论):
只有在 [...] Load-Exclusive 和 Store-Exclusive 之间没有明确的 memory 访问时,LoadExcl/StoreExcl 循环才能保证向前推进。
可能有一些 AArch64 CPU 会在其中创建stxr
故障的无限循环,但可能还有其他 CPU 可以正常工作。 如果它在您测试的 CPU 上实际工作,那么查看是否有任何文档支持它可能是个好主意。
如果new_
节点恰好与头节点位于同一高速缓存行(或 LL/SC 独占块)中,则这很可能是一个问题。 如果这在您关心的微架构上完全有效,请确保您测试该案例,或者以某种方式使其成为不可能。
除此之外,我认为您的整体算法看起来是正确的,所以如果您已经对其进行了测试并发现它有效,那么这可能很好。
但是,我并没有真正仔细考虑过您的算法。 我也没有任何设计或使用原始 LL/SC 的经验。 我原则上知道它们是如何工作的,但我不准备说这绝对是正确的。 从我所做的一点点思考来看,我没有看到任何问题,但这并不意味着没有任何问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.