|  | @@ -90,7 +90,8 @@ struct grpc_resource_user {
 | 
	
		
			
				|  |  |    grpc_closure_list on_allocated;
 | 
	
		
			
				|  |  |    /* True if we are currently trying to allocate from the quota, false if not */
 | 
	
		
			
				|  |  |    bool allocating;
 | 
	
		
			
				|  |  | -  /* How many bytes of allocations are outstanding */
 | 
	
		
			
				|  |  | +  /* The amount of memory (in bytes) that has been requested from this user
 | 
	
		
			
				|  |  | +   * asynchronously but hasn't been granted yet. */
 | 
	
		
			
				|  |  |    int64_t outstanding_allocations;
 | 
	
		
			
				|  |  |    /* True if we are currently trying to add ourselves to the non-free quota
 | 
	
		
			
				|  |  |       list, false otherwise */
 | 
	
	
		
			
				|  | @@ -135,6 +136,9 @@ struct grpc_resource_quota {
 | 
	
		
			
				|  |  |    int64_t size;
 | 
	
		
			
				|  |  |    /* Amount of free memory in the resource quota */
 | 
	
		
			
				|  |  |    int64_t free_pool;
 | 
	
		
			
				|  |  | +  /* Used size of memory in the resource quota. Updated as soon as the resource
 | 
	
		
			
				|  |  | +   * users start to allocate or free the memory. */
 | 
	
		
			
				|  |  | +  gpr_atm used;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    gpr_atm last_size;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -371,6 +375,7 @@ static bool rq_reclaim_from_per_user_free_pool(
 | 
	
		
			
				|  |  |    while ((resource_user = rulist_pop_head(resource_quota,
 | 
	
		
			
				|  |  |                                            GRPC_RULIST_NON_EMPTY_FREE_POOL))) {
 | 
	
		
			
				|  |  |      gpr_mu_lock(&resource_user->mu);
 | 
	
		
			
				|  |  | +    resource_user->added_to_free_pool = false;
 | 
	
		
			
				|  |  |      if (resource_user->free_pool > 0) {
 | 
	
		
			
				|  |  |        int64_t amt = resource_user->free_pool;
 | 
	
		
			
				|  |  |        resource_user->free_pool = 0;
 | 
	
	
		
			
				|  | @@ -386,6 +391,13 @@ static bool rq_reclaim_from_per_user_free_pool(
 | 
	
		
			
				|  |  |        gpr_mu_unlock(&resource_user->mu);
 | 
	
		
			
				|  |  |        return true;
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | +      if (grpc_resource_quota_trace.enabled()) {
 | 
	
		
			
				|  |  | +        gpr_log(GPR_INFO,
 | 
	
		
			
				|  |  | +                "RQ %s %s: failed to reclaim_from_per_user_free_pool; "
 | 
	
		
			
				|  |  | +                "free_pool = %" PRId64 "; rq_free_pool = %" PRId64,
 | 
	
		
			
				|  |  | +                resource_quota->name, resource_user->name,
 | 
	
		
			
				|  |  | +                resource_user->free_pool, resource_quota->free_pool);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |        gpr_mu_unlock(&resource_user->mu);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -622,6 +634,7 @@ grpc_resource_quota* grpc_resource_quota_create(const char* name) {
 | 
	
		
			
				|  |  |    resource_quota->combiner = grpc_combiner_create();
 | 
	
		
			
				|  |  |    resource_quota->free_pool = INT64_MAX;
 | 
	
		
			
				|  |  |    resource_quota->size = INT64_MAX;
 | 
	
		
			
				|  |  | +  resource_quota->used = 0;
 | 
	
		
			
				|  |  |    gpr_atm_no_barrier_store(&resource_quota->last_size, GPR_ATM_MAX);
 | 
	
		
			
				|  |  |    gpr_mu_init(&resource_quota->thread_count_mu);
 | 
	
		
			
				|  |  |    resource_quota->max_threads = INT_MAX;
 | 
	
	
		
			
				|  | @@ -712,7 +725,7 @@ size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota) {
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_resource_quota* grpc_resource_quota_from_channel_args(
 | 
	
		
			
				|  |  | -    const grpc_channel_args* channel_args) {
 | 
	
		
			
				|  |  | +    const grpc_channel_args* channel_args, bool create) {
 | 
	
		
			
				|  |  |    for (size_t i = 0; i < channel_args->num_args; i++) {
 | 
	
		
			
				|  |  |      if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
 | 
	
		
			
				|  |  |        if (channel_args->args[i].type == GRPC_ARG_POINTER) {
 | 
	
	
		
			
				|  | @@ -724,7 +737,7 @@ grpc_resource_quota* grpc_resource_quota_from_channel_args(
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return grpc_resource_quota_create(nullptr);
 | 
	
		
			
				|  |  | +  return create ? grpc_resource_quota_create(nullptr) : nullptr;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void* rq_copy(void* rq) {
 | 
	
	
		
			
				|  | @@ -863,33 +876,68 @@ void grpc_resource_user_free_threads(grpc_resource_user* resource_user,
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&resource_user->resource_quota->thread_count_mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
 | 
	
		
			
				|  |  | -                              grpc_closure* optional_on_done) {
 | 
	
		
			
				|  |  | -  gpr_mu_lock(&resource_user->mu);
 | 
	
		
			
				|  |  | +static void resource_user_alloc_locked(grpc_resource_user* resource_user,
 | 
	
		
			
				|  |  | +                                       size_t size,
 | 
	
		
			
				|  |  | +                                       grpc_closure* optional_on_done) {
 | 
	
		
			
				|  |  |    ru_ref_by(resource_user, static_cast<gpr_atm>(size));
 | 
	
		
			
				|  |  |    resource_user->free_pool -= static_cast<int64_t>(size);
 | 
	
		
			
				|  |  | -  resource_user->outstanding_allocations += static_cast<int64_t>(size);
 | 
	
		
			
				|  |  |    if (grpc_resource_quota_trace.enabled()) {
 | 
	
		
			
				|  |  |      gpr_log(GPR_INFO, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
 | 
	
		
			
				|  |  |              resource_user->resource_quota->name, resource_user->name, size,
 | 
	
		
			
				|  |  |              resource_user->free_pool);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (resource_user->free_pool < 0) {
 | 
	
		
			
				|  |  | -    grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
 | 
	
		
			
				|  |  | -                             GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | +    if (optional_on_done != nullptr) {
 | 
	
		
			
				|  |  | +      resource_user->outstanding_allocations += static_cast<int64_t>(size);
 | 
	
		
			
				|  |  | +      grpc_closure_list_append(&resource_user->on_allocated, optional_on_done,
 | 
	
		
			
				|  |  | +                               GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      if (!resource_user->allocating) {
 | 
	
		
			
				|  |  |        resource_user->allocating = true;
 | 
	
		
			
				|  |  |        GRPC_CLOSURE_SCHED(&resource_user->allocate_closure, GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | -    resource_user->outstanding_allocations -= static_cast<int64_t>(size);
 | 
	
		
			
				|  |  |      GRPC_CLOSURE_SCHED(optional_on_done, GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user,
 | 
	
		
			
				|  |  | +                                   size_t size) {
 | 
	
		
			
				|  |  | +  if (gpr_atm_no_barrier_load(&resource_user->shutdown)) return false;
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&resource_user->mu);
 | 
	
		
			
				|  |  | +  grpc_resource_quota* resource_quota = resource_user->resource_quota;
 | 
	
		
			
				|  |  | +  bool cas_success;
 | 
	
		
			
				|  |  | +  do {
 | 
	
		
			
				|  |  | +    gpr_atm used = gpr_atm_no_barrier_load(&resource_quota->used);
 | 
	
		
			
				|  |  | +    gpr_atm new_used = used + size;
 | 
	
		
			
				|  |  | +    if (static_cast<size_t>(new_used) >
 | 
	
		
			
				|  |  | +        grpc_resource_quota_peek_size(resource_quota)) {
 | 
	
		
			
				|  |  | +      gpr_mu_unlock(&resource_user->mu);
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    cas_success = gpr_atm_full_cas(&resource_quota->used, used, new_used);
 | 
	
		
			
				|  |  | +  } while (!cas_success);
 | 
	
		
			
				|  |  | +  resource_user_alloc_locked(resource_user, size, nullptr);
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&resource_user->mu);
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size,
 | 
	
		
			
				|  |  | +                              grpc_closure* optional_on_done) {
 | 
	
		
			
				|  |  | +  // TODO(juanlishen): Maybe return immediately if shutting down. Deferring this
 | 
	
		
			
				|  |  | +  // because some tests become flaky after the change.
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&resource_user->mu);
 | 
	
		
			
				|  |  | +  grpc_resource_quota* resource_quota = resource_user->resource_quota;
 | 
	
		
			
				|  |  | +  gpr_atm_no_barrier_fetch_add(&resource_quota->used, size);
 | 
	
		
			
				|  |  | +  resource_user_alloc_locked(resource_user, size, optional_on_done);
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&resource_user->mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size) {
 | 
	
		
			
				|  |  |    gpr_mu_lock(&resource_user->mu);
 | 
	
		
			
				|  |  | +  grpc_resource_quota* resource_quota = resource_user->resource_quota;
 | 
	
		
			
				|  |  | +  gpr_atm prior = gpr_atm_no_barrier_fetch_add(&resource_quota->used, -size);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(prior >= static_cast<long>(size));
 | 
	
		
			
				|  |  |    bool was_zero_or_negative = resource_user->free_pool <= 0;
 | 
	
		
			
				|  |  |    resource_user->free_pool += static_cast<int64_t>(size);
 | 
	
		
			
				|  |  |    if (grpc_resource_quota_trace.enabled()) {
 | 
	
	
		
			
				|  | @@ -940,6 +988,12 @@ void grpc_resource_user_slice_allocator_init(
 | 
	
		
			
				|  |  |  void grpc_resource_user_alloc_slices(
 | 
	
		
			
				|  |  |      grpc_resource_user_slice_allocator* slice_allocator, size_t length,
 | 
	
		
			
				|  |  |      size_t count, grpc_slice_buffer* dest) {
 | 
	
		
			
				|  |  | +  if (gpr_atm_no_barrier_load(&slice_allocator->resource_user->shutdown)) {
 | 
	
		
			
				|  |  | +    GRPC_CLOSURE_SCHED(
 | 
	
		
			
				|  |  | +        &slice_allocator->on_allocated,
 | 
	
		
			
				|  |  | +        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource user shutdown"));
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    slice_allocator->length = length;
 | 
	
		
			
				|  |  |    slice_allocator->count = count;
 | 
	
		
			
				|  |  |    slice_allocator->dest = dest;
 |