void* ponyint_mpmcq_pop(mpmcq_t* q) { size_t my_ticket = atomic_fetch_add_explicit(&q->ticket, 1, memory_order_relaxed); while(my_ticket != atomic_load_explicit(&q->waiting_for, memory_order_relaxed)) ponyint_cpu_relax(); atomic_thread_fence(memory_order_acquire); mpmcq_node_t* tail = atomic_load_explicit(&q->tail, memory_order_relaxed); // Get the next node rather than the tail. The tail is either a stub or has // already been consumed. mpmcq_node_t* next = atomic_load_explicit(&tail->next, memory_order_relaxed); // Bailout if we have no next node. if(next == NULL) { atomic_store_explicit(&q->waiting_for, my_ticket + 1, memory_order_relaxed); return NULL; } atomic_store_explicit(&q->tail, next, memory_order_relaxed); atomic_store_explicit(&q->waiting_for, my_ticket + 1, memory_order_release); // Synchronise-with the push. atomic_thread_fence(memory_order_acquire); // We'll return the data pointer from the next node. void* data = atomic_load_explicit(&next->data, memory_order_relaxed); // Since we will be freeing the old tail, we need to be sure no other // consumer is still reading the old tail. To do this, we set the data // pointer of our new tail to NULL, and we wait until the data pointer of // the old tail is NULL. atomic_store_explicit(&next->data, NULL, memory_order_release); while(atomic_load_explicit(&tail->data, memory_order_relaxed) != NULL) ponyint_cpu_relax(); atomic_thread_fence(memory_order_acquire); // Free the old tail. The new tail is the next node. POOL_FREE(mpmcq_node_t, tail); return data; }
void* ponyint_mpmcq_pop(mpmcq_t* q) { mpmcq_dwcas_t cmp, xchg; mpmcq_node_t* next; cmp.aba = q->tail.aba; cmp.node = q->tail.node; do { // Get the next node rather than the tail. The tail is either a stub or has // already been consumed. next = _atomic_load(&cmp.node->next); // Bailout if we have no next node. if(next == NULL) return NULL; // Make the next node the tail, incrementing the aba counter. If this // fails, cmp becomes the new tail and we retry the loop. xchg.aba = cmp.aba + 1; xchg.node = next; } while(!_atomic_dwcas(&q->tail.dw, &cmp.dw, xchg.dw)); // We'll return the data pointer from the next node. void* data = _atomic_load(&next->data); // Since we will be freeing the old tail, we need to be sure no other // consumer is still reading the old tail. To do this, we set the data // pointer of our new tail to NULL, and we wait until the data pointer of // the old tail is NULL. _atomic_store(&next->data, NULL); while(_atomic_load(&cmp.node->data) != NULL) ponyint_cpu_relax(); // Free the old tail. The new tail is the next node. POOL_FREE(mpmcq_node_t, cmp.node); return data; }