|  | @@ -220,6 +220,52 @@ static bool bpreclaim(grpc_exec_ctx *exec_ctx, grpc_buffer_pool *buffer_pool,
 | 
	
		
			
				|  |  |    return true;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * bu_slice: a slice implementation that is backed by a grpc_buffer_user
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +typedef struct {
 | 
	
		
			
				|  |  | +  gpr_slice_refcount base;
 | 
	
		
			
				|  |  | +  gpr_refcount refs;
 | 
	
		
			
				|  |  | +  grpc_buffer_user *buffer_user;
 | 
	
		
			
				|  |  | +  size_t size;
 | 
	
		
			
				|  |  | +} bu_slice_refcount;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void bu_slice_ref(void *p) {
 | 
	
		
			
				|  |  | +  bu_slice_refcount *rc = p;
 | 
	
		
			
				|  |  | +  gpr_ref(&rc->refs);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void bu_slice_unref(void *p) {
 | 
	
		
			
				|  |  | +  bu_slice_refcount *rc = p;
 | 
	
		
			
				|  |  | +  if (gpr_unref(&rc->refs)) {
 | 
	
		
			
				|  |  | +    /* TODO(ctiller): this is dangerous, but I think safe for now:
 | 
	
		
			
				|  |  | +       we have no guarantee here that we're at a safe point for creating an
 | 
	
		
			
				|  |  | +       execution context, but we have no way of writing this code otherwise.
 | 
	
		
			
				|  |  | +       In the future: consider lifting gpr_slice to grpc, and offering an
 | 
	
		
			
				|  |  | +       internal_{ref,unref} pair that is execution context aware. Alternatively,
 | 
	
		
			
				|  |  | +       make exec_ctx be thread local and 'do the right thing' (whatever that is)
 | 
	
		
			
				|  |  | +       if NULL */
 | 
	
		
			
				|  |  | +    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 | 
	
		
			
				|  |  | +    grpc_buffer_user_free(&exec_ctx, rc->buffer_user, rc->size);
 | 
	
		
			
				|  |  | +    grpc_exec_ctx_finish(&exec_ctx);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static gpr_slice bu_slice_create(grpc_buffer_user *buffer_user, size_t size) {
 | 
	
		
			
				|  |  | +  bu_slice_refcount *rc = gpr_malloc(sizeof(bu_slice_refcount) + size);
 | 
	
		
			
				|  |  | +  rc->base.ref = bu_slice_ref;
 | 
	
		
			
				|  |  | +  rc->base.unref = bu_slice_unref;
 | 
	
		
			
				|  |  | +  gpr_ref_init(&rc->refs, 1);
 | 
	
		
			
				|  |  | +  rc->buffer_user = buffer_user;
 | 
	
		
			
				|  |  | +  rc->size = size;
 | 
	
		
			
				|  |  | +  gpr_slice slice;
 | 
	
		
			
				|  |  | +  slice.refcount = &rc->base;
 | 
	
		
			
				|  |  | +  slice.data.refcounted.bytes = (uint8_t *)(rc + 1);
 | 
	
		
			
				|  |  | +  slice.data.refcounted.length = size;
 | 
	
		
			
				|  |  | +  return slice;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /*******************************************************************************
 | 
	
		
			
				|  |  |   * grpc_buffer_pool internal implementation
 | 
	
		
			
				|  |  |   */
 | 
	
	
		
			
				|  | @@ -284,6 +330,20 @@ static void bu_destroy(grpc_exec_ctx *exec_ctx, void *bu, grpc_error *error) {
 | 
	
		
			
				|  |  |    grpc_buffer_pool_internal_unref(exec_ctx, buffer_user->buffer_pool);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void bu_allocated_slices(grpc_exec_ctx *exec_ctx, void *ts,
 | 
	
		
			
				|  |  | +                                grpc_error *error) {
 | 
	
		
			
				|  |  | +  grpc_buffer_user_slice_allocator *alloc_temp_storage = ts;
 | 
	
		
			
				|  |  | +  if (error == GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  | +    for (size_t i = 0; i < alloc_temp_storage->count; i++) {
 | 
	
		
			
				|  |  | +      gpr_slice_buffer_add(alloc_temp_storage->dest,
 | 
	
		
			
				|  |  | +                           bu_slice_create(alloc_temp_storage->buffer_user,
 | 
	
		
			
				|  |  | +                                           alloc_temp_storage->length));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  grpc_closure_run(exec_ctx, alloc_temp_storage->on_done,
 | 
	
		
			
				|  |  | +                   GRPC_ERROR_REF(error));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  typedef struct {
 | 
	
		
			
				|  |  |    int64_t size;
 | 
	
		
			
				|  |  |    grpc_buffer_pool *buffer_pool;
 | 
	
	
		
			
				|  | @@ -491,3 +551,18 @@ void grpc_buffer_user_finish_reclaimation(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                          &buffer_user->buffer_pool->bpreclaimation_done_closure,
 | 
	
		
			
				|  |  |                          GRPC_ERROR_NONE, false);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_buffer_user_alloc_slices(
 | 
	
		
			
				|  |  | +    grpc_exec_ctx *exec_ctx, grpc_buffer_user *buffer_user,
 | 
	
		
			
				|  |  | +    grpc_buffer_user_slice_allocator *alloc_temp_storage, size_t length,
 | 
	
		
			
				|  |  | +    size_t count, gpr_slice_buffer *dest, grpc_closure *on_done) {
 | 
	
		
			
				|  |  | +  grpc_closure_init(&alloc_temp_storage->on_allocated, bu_allocated_slices,
 | 
	
		
			
				|  |  | +                    alloc_temp_storage);
 | 
	
		
			
				|  |  | +  alloc_temp_storage->on_done = on_done;
 | 
	
		
			
				|  |  | +  alloc_temp_storage->length = length;
 | 
	
		
			
				|  |  | +  alloc_temp_storage->count = count;
 | 
	
		
			
				|  |  | +  alloc_temp_storage->dest = dest;
 | 
	
		
			
				|  |  | +  alloc_temp_storage->buffer_user = buffer_user;
 | 
	
		
			
				|  |  | +  grpc_buffer_user_alloc(exec_ctx, buffer_user, count * length,
 | 
	
		
			
				|  |  | +                         &alloc_temp_storage->on_allocated);
 | 
	
		
			
				|  |  | +}
 |