|
|
|
@ -38,35 +38,88 @@ |
|
|
|
|
#include <grpc/support/alloc.h> |
|
|
|
|
#include <grpc/support/log.h> |
|
|
|
|
|
|
|
|
|
#define NO_CONSUMER ((gpr_atm)1) |
|
|
|
|
typedef struct grpc_aelock_qnode { |
|
|
|
|
gpr_mpscq_node mpscq_node; |
|
|
|
|
grpc_aelock_action action; |
|
|
|
|
void *arg; |
|
|
|
|
} grpc_aelock_qnode; |
|
|
|
|
|
|
|
|
|
void grpc_aelock_init(grpc_aelock *lock, grpc_workqueue *optional_workqueue) { |
|
|
|
|
struct grpc_aelock { |
|
|
|
|
grpc_workqueue *optional_workqueue; |
|
|
|
|
gpr_mpscq queue; |
|
|
|
|
// state is:
|
|
|
|
|
// lower bit - zero if orphaned
|
|
|
|
|
// other bits - number of items queued on the lock
|
|
|
|
|
gpr_atm state; |
|
|
|
|
grpc_closure continue_finishing; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static void continue_finishing(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
bool success); |
|
|
|
|
|
|
|
|
|
grpc_aelock *grpc_aelock_create(grpc_workqueue *optional_workqueue) { |
|
|
|
|
grpc_aelock *lock = gpr_malloc(sizeof(*lock)); |
|
|
|
|
lock->optional_workqueue = optional_workqueue; |
|
|
|
|
gpr_atm_no_barrier_store(&lock->locked, 0); |
|
|
|
|
gpr_atm_no_barrier_store(&lock->state, 1); |
|
|
|
|
gpr_mpscq_init(&lock->queue); |
|
|
|
|
grpc_closure_init(&lock->continue_finishing, continue_finishing, lock); |
|
|
|
|
return lock; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_aelock_destroy(grpc_aelock *lock) { |
|
|
|
|
GPR_ASSERT(gpr_atm_no_barrier_load(&lock->locked) == 0); |
|
|
|
|
static void really_destroy(grpc_aelock *lock) { |
|
|
|
|
GPR_ASSERT(gpr_atm_no_barrier_load(&lock->state) == 0); |
|
|
|
|
gpr_mpscq_destroy(&lock->queue); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_aelock_destroy(grpc_aelock *lock) { |
|
|
|
|
if (gpr_atm_full_fetch_add(&lock->state, -1) == 1) { |
|
|
|
|
really_destroy(lock); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool maybe_finish_one(grpc_exec_ctx *exec_ctx, grpc_aelock *lock) { |
|
|
|
|
gpr_mpscq_node *n = gpr_mpscq_pop(&lock->queue); |
|
|
|
|
if (n == NULL) { |
|
|
|
|
// queue is in an inconsistant state: use this as a cue that we should
|
|
|
|
|
// go off and do something else for a while (and come back later)
|
|
|
|
|
grpc_exec_ctx_enqueue(exec_ctx, &lock->continue_finishing, true, |
|
|
|
|
lock->optional_workqueue); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
grpc_aelock_qnode *ln = (grpc_aelock_qnode *)n; |
|
|
|
|
ln->action(exec_ctx, ln->arg); |
|
|
|
|
gpr_free(ln); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void finish(grpc_exec_ctx *exec_ctx, grpc_aelock *lock) { |
|
|
|
|
while (gpr_atm_full_fetch_add(&lock->locked, -1) != 1) { |
|
|
|
|
gpr_mpscq_node *n; |
|
|
|
|
while ((n = gpr_mpscq_pop(&lock->queue)) == NULL) { |
|
|
|
|
// TODO(ctiller): find something to fill in the time
|
|
|
|
|
do { |
|
|
|
|
switch (gpr_atm_full_fetch_add(&lock->state, -2)) { |
|
|
|
|
case 3: // had one count, one unorphaned --> unlocked unorphaned
|
|
|
|
|
return; |
|
|
|
|
case 2: // and one count, one orphaned --> unlocked and orphaned
|
|
|
|
|
really_destroy(lock); |
|
|
|
|
return; |
|
|
|
|
case 1: |
|
|
|
|
case 0: |
|
|
|
|
// these values are illegal - representing an already unlocked or
|
|
|
|
|
// deleted lock
|
|
|
|
|
GPR_UNREACHABLE_CODE(return ); |
|
|
|
|
} |
|
|
|
|
grpc_aelock_qnode *ln = (grpc_aelock_qnode *)n; |
|
|
|
|
ln->action(exec_ctx, ln->arg); |
|
|
|
|
gpr_free(ln); |
|
|
|
|
} |
|
|
|
|
} while (maybe_finish_one(exec_ctx, lock)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void continue_finishing(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
bool success) { |
|
|
|
|
if (maybe_finish_one(exec_ctx, arg)) finish(exec_ctx, arg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_aelock_execute(grpc_exec_ctx *exec_ctx, grpc_aelock *lock, |
|
|
|
|
grpc_aelock_action action, void *arg, |
|
|
|
|
size_t sizeof_arg) { |
|
|
|
|
if (gpr_atm_full_fetch_add(&lock->locked, 1) == 0) { |
|
|
|
|
gpr_atm last = gpr_atm_full_fetch_add(&lock->state, 2); |
|
|
|
|
GPR_ASSERT(last & 1); // ensure lock has not been destroyed
|
|
|
|
|
if (last == 1) { |
|
|
|
|
action(exec_ctx, arg); |
|
|
|
|
finish(exec_ctx, lock); |
|
|
|
|
} else { |
|
|
|
|