|
|
|
@ -40,11 +40,16 @@ |
|
|
|
|
|
|
|
|
|
#define NO_CONSUMER ((gpr_atm)1) |
|
|
|
|
|
|
|
|
|
static void bad_action(grpc_exec_ctx *exec_ctx, void *arg) { |
|
|
|
|
GPR_UNREACHABLE_CODE(return ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_aelock_init(grpc_aelock *lock, grpc_workqueue *optional_workqueue) { |
|
|
|
|
lock->optional_workqueue = optional_workqueue; |
|
|
|
|
gpr_atm_no_barrier_store(&lock->head, NO_CONSUMER); |
|
|
|
|
lock->tail = &lock->stub; |
|
|
|
|
gpr_atm_no_barrier_store(&lock->stub.next, 0); |
|
|
|
|
gpr_atm_no_barrier_store(&lock->tombstone.next, 0); |
|
|
|
|
lock->tombstone.action = bad_action; |
|
|
|
|
lock->tail = &lock->tombstone; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_aelock_destroy(grpc_aelock *lock) { |
|
|
|
@ -56,15 +61,35 @@ static void finish(grpc_exec_ctx *exec_ctx, grpc_aelock *lock) { |
|
|
|
|
grpc_aelock_qnode *tail = lock->tail; |
|
|
|
|
grpc_aelock_qnode *next = |
|
|
|
|
(grpc_aelock_qnode *)gpr_atm_acq_load(&tail->next); |
|
|
|
|
if (next == NULL) { |
|
|
|
|
if (gpr_atm_rel_cas(&lock->head, (gpr_atm)&lock->stub, NO_CONSUMER)) { |
|
|
|
|
return; |
|
|
|
|
if (tail == &lock->tombstone) { |
|
|
|
|
if (next == NULL) { |
|
|
|
|
if (gpr_atm_rel_cas(&lock->head, (gpr_atm)&lock->tombstone, |
|
|
|
|
NO_CONSUMER)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
lock->tail = next; |
|
|
|
|
tail = next; |
|
|
|
|
next = (grpc_aelock_qnode *)gpr_atm_acq_load(&tail->next); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
} |
|
|
|
|
if (next != NULL) { |
|
|
|
|
lock->tail = next; |
|
|
|
|
|
|
|
|
|
next->action(exec_ctx, next->arg); |
|
|
|
|
gpr_free(next); |
|
|
|
|
tail->action(exec_ctx, tail->arg); |
|
|
|
|
gpr_free(tail); |
|
|
|
|
} else { |
|
|
|
|
grpc_aelock_qnode *head = |
|
|
|
|
(grpc_aelock_qnode *)gpr_atm_acq_load(&lock->head); |
|
|
|
|
if (head != tail) { |
|
|
|
|
// TODO(ctiller): consider sleeping?
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
gpr_atm_no_barrier_store(&lock->tombstone.next, 0); |
|
|
|
|
while (!gpr_atm_rel_cas(&lock->head, (gpr_atm)head, |
|
|
|
|
(gpr_atm)&lock->tombstone)) { |
|
|
|
|
head = (grpc_aelock_qnode *)gpr_atm_acq_load(&lock->head); |
|
|
|
|
} |
|
|
|
|
gpr_atm_rel_store(&head->next, (gpr_atm)&lock->tombstone); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -72,11 +97,11 @@ static void finish(grpc_exec_ctx *exec_ctx, grpc_aelock *lock) { |
|
|
|
|
void grpc_aelock_execute(grpc_exec_ctx *exec_ctx, grpc_aelock *lock, |
|
|
|
|
grpc_aelock_action action, void *arg, |
|
|
|
|
size_t sizeof_arg) { |
|
|
|
|
gpr_atm cur; |
|
|
|
|
gpr_atm head; |
|
|
|
|
retry_top: |
|
|
|
|
cur = gpr_atm_acq_load(&lock->head); |
|
|
|
|
if (cur == NO_CONSUMER) { |
|
|
|
|
if (!gpr_atm_rel_cas(&lock->head, NO_CONSUMER, (gpr_atm)&lock->stub)) { |
|
|
|
|
head = gpr_atm_acq_load(&lock->head); |
|
|
|
|
if (head == NO_CONSUMER) { |
|
|
|
|
if (!gpr_atm_rel_cas(&lock->head, NO_CONSUMER, (gpr_atm)&lock->tombstone)) { |
|
|
|
|
goto retry_top; |
|
|
|
|
} |
|
|
|
|
action(exec_ctx, arg); |
|
|
|
@ -86,18 +111,19 @@ retry_top: |
|
|
|
|
|
|
|
|
|
grpc_aelock_qnode *n = gpr_malloc(sizeof(*n) + sizeof_arg); |
|
|
|
|
n->action = action; |
|
|
|
|
gpr_atm_no_barrier_store(&n->next, 0); |
|
|
|
|
if (sizeof_arg > 0) { |
|
|
|
|
memcpy(n + 1, arg, sizeof_arg); |
|
|
|
|
n->arg = n + 1; |
|
|
|
|
} else { |
|
|
|
|
n->arg = arg; |
|
|
|
|
} |
|
|
|
|
while (!gpr_atm_rel_cas(&lock->head, cur, (gpr_atm)n)) { |
|
|
|
|
gpr_atm_no_barrier_store(&n->next, 0); |
|
|
|
|
while (!gpr_atm_rel_cas(&lock->head, head, (gpr_atm)n)) { |
|
|
|
|
retry_queue_load: |
|
|
|
|
cur = gpr_atm_acq_load(&lock->head); |
|
|
|
|
if (cur == NO_CONSUMER) { |
|
|
|
|
if (!gpr_atm_rel_cas(&lock->head, NO_CONSUMER, (gpr_atm)&lock->stub)) { |
|
|
|
|
head = gpr_atm_acq_load(&lock->head); |
|
|
|
|
if (head == NO_CONSUMER) { |
|
|
|
|
if (!gpr_atm_rel_cas(&lock->head, NO_CONSUMER, |
|
|
|
|
(gpr_atm)&lock->tombstone)) { |
|
|
|
|
goto retry_queue_load; |
|
|
|
|
} |
|
|
|
|
gpr_free(n); |
|
|
|
@ -106,5 +132,5 @@ retry_top: |
|
|
|
|
return; // early out
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
gpr_atm_no_barrier_store(&((grpc_aelock_qnode *)cur)->next, (gpr_atm)n); |
|
|
|
|
gpr_atm_rel_store(&((grpc_aelock_qnode *)head)->next, (gpr_atm)n); |
|
|
|
|
} |
|
|
|
|