@@ -268,6 +268,74 @@ static int s_test_s3_buffer_pool_reserve_over_limit(struct aws_allocator *alloca
268268};
269269AWS_TEST_CASE (test_s3_buffer_pool_reserve_over_limit , s_test_s3_buffer_pool_reserve_over_limit )
270270
271+ /* test that one big ticket release can complete several pending reserves*/
272+ static int s_test_s3_buffer_pool_reserve_over_limit_multi (struct aws_allocator * allocator , void * ctx ) {
273+ (void )allocator ;
274+ (void )ctx ;
275+
276+ struct aws_s3_buffer_pool * buffer_pool = aws_s3_default_buffer_pool_new (
277+ allocator , (struct aws_s3_buffer_pool_config ){.part_size = MB_TO_BYTES (8 ), .memory_limit = GB_TO_BYTES (1 )});
278+
279+ struct aws_s3_buffer_ticket * tickets [109 ];
280+ struct aws_future_s3_buffer_ticket * ticket_futures [109 ];
281+
282+ ticket_futures [0 ] = aws_s3_default_buffer_pool_reserve (
283+ buffer_pool , (struct aws_s3_buffer_pool_reserve_meta ){.size = MB_TO_BYTES (32 )});
284+ ASSERT_TRUE (aws_future_s3_buffer_ticket_is_done (ticket_futures [0 ]));
285+ ASSERT_INT_EQUALS (aws_future_s3_buffer_ticket_get_error (ticket_futures [0 ]), AWS_OP_SUCCESS );
286+ tickets [0 ] = aws_future_s3_buffer_ticket_get_result_by_move (ticket_futures [0 ]);
287+ struct aws_byte_buf buf0 = aws_s3_buffer_ticket_claim (tickets [0 ]);
288+ ASSERT_NOT_NULL (buf0 .buffer );
289+
290+ for (size_t i = 1 ; i < 109 ; ++ i ) {
291+ ticket_futures [i ] = aws_s3_default_buffer_pool_reserve (
292+ buffer_pool , (struct aws_s3_buffer_pool_reserve_meta ){.size = MB_TO_BYTES (8 )});
293+ ASSERT_TRUE (aws_future_s3_buffer_ticket_is_done (ticket_futures [i ]));
294+ ASSERT_INT_EQUALS (aws_future_s3_buffer_ticket_get_error (ticket_futures [i ]), AWS_OP_SUCCESS );
295+ tickets [i ] = aws_future_s3_buffer_ticket_get_result_by_move (ticket_futures [i ]);
296+ struct aws_byte_buf buf = aws_s3_buffer_ticket_claim (tickets [i ]);
297+ ASSERT_NOT_NULL (buf .buffer );
298+ }
299+
300+ struct aws_future_s3_buffer_ticket * over_future1 = aws_s3_default_buffer_pool_reserve (
301+ buffer_pool , (struct aws_s3_buffer_pool_reserve_meta ){.size = MB_TO_BYTES (8 )});
302+
303+ ASSERT_FALSE (aws_future_s3_buffer_ticket_is_done (over_future1 ));
304+ struct s_reserve_state state1 = {.future = over_future1 };
305+ aws_future_s3_buffer_ticket_register_callback (over_future1 , s_on_pool_buffer_reserved , & state1 );
306+
307+ struct aws_future_s3_buffer_ticket * over_future2 = aws_s3_default_buffer_pool_reserve (
308+ buffer_pool , (struct aws_s3_buffer_pool_reserve_meta ){.size = MB_TO_BYTES (8 )});
309+
310+ ASSERT_FALSE (aws_future_s3_buffer_ticket_is_done (over_future2 ));
311+ struct s_reserve_state state2 = {.future = over_future2 };
312+ aws_future_s3_buffer_ticket_register_callback (over_future2 , s_on_pool_buffer_reserved , & state2 );
313+
314+ /* Release big ticket */
315+ aws_s3_buffer_ticket_release (tickets [0 ]);
316+ aws_future_s3_buffer_ticket_release (ticket_futures [0 ]);
317+
318+ ASSERT_TRUE (aws_future_s3_buffer_ticket_is_done (over_future1 ));
319+ ASSERT_TRUE (aws_future_s3_buffer_ticket_is_done (over_future2 ));
320+ ASSERT_NOT_NULL (state1 .ticket );
321+ ASSERT_NOT_NULL (state2 .ticket );
322+
323+ for (size_t i = 1 ; i < 109 ; ++ i ) {
324+ aws_s3_buffer_ticket_release (tickets [i ]);
325+ aws_future_s3_buffer_ticket_release (ticket_futures [i ]);
326+ }
327+
328+ aws_s3_buffer_ticket_release (state1 .ticket );
329+ aws_s3_buffer_ticket_release (state2 .ticket );
330+ aws_future_s3_buffer_ticket_release (over_future1 );
331+ aws_future_s3_buffer_ticket_release (over_future2 );
332+
333+ aws_s3_default_buffer_pool_destroy (buffer_pool );
334+
335+ return 0 ;
336+ };
337+ AWS_TEST_CASE (test_s3_buffer_pool_reserve_over_limit_multi , s_test_s3_buffer_pool_reserve_over_limit_multi )
338+
271339static void s_on_pool_buffer_reserved_instant_release (void * user_data ) {
272340 struct s_reserve_state * state = user_data ;
273341
@@ -324,6 +392,66 @@ AWS_TEST_CASE(
324392 test_s3_buffer_pool_reserve_over_limit_instant_release ,
325393 s_test_s3_buffer_pool_reserve_over_limit_instant_release )
326394
395+ static void s_on_pool_buffer_reserved_cancel (void * user_data ) {
396+ struct s_reserve_state * state = user_data ;
397+
398+ if (aws_future_s3_buffer_ticket_get_error (state -> future ) == AWS_OP_SUCCESS ) {
399+ state -> ticket = aws_future_s3_buffer_ticket_get_result_by_move (state -> future );
400+ }
401+ }
402+
403+ /* make sure that cancelling pending futures does not break the pool */
404+ static int s_test_s3_buffer_pool_reserve_over_limit_cancel (struct aws_allocator * allocator , void * ctx ) {
405+ (void )allocator ;
406+ (void )ctx ;
407+
408+ struct aws_s3_buffer_pool * buffer_pool = aws_s3_default_buffer_pool_new (
409+ allocator , (struct aws_s3_buffer_pool_config ){.part_size = MB_TO_BYTES (8 ), .memory_limit = GB_TO_BYTES (1 )});
410+
411+ struct aws_s3_buffer_ticket * tickets [112 ];
412+ struct aws_future_s3_buffer_ticket * ticket_futures [112 ];
413+ for (size_t i = 0 ; i < 112 ; ++ i ) {
414+ ticket_futures [i ] = aws_s3_default_buffer_pool_reserve (
415+ buffer_pool , (struct aws_s3_buffer_pool_reserve_meta ){.size = MB_TO_BYTES (8 )});
416+ ASSERT_TRUE (aws_future_s3_buffer_ticket_is_done (ticket_futures [i ]));
417+ ASSERT_INT_EQUALS (aws_future_s3_buffer_ticket_get_error (ticket_futures [i ]), AWS_OP_SUCCESS );
418+ tickets [i ] = aws_future_s3_buffer_ticket_get_result_by_move (ticket_futures [i ]);
419+ struct aws_byte_buf buf = aws_s3_buffer_ticket_claim (tickets [i ]);
420+ ASSERT_NOT_NULL (buf .buffer );
421+ }
422+
423+ struct aws_future_s3_buffer_ticket * over_future = aws_s3_default_buffer_pool_reserve (
424+ buffer_pool , (struct aws_s3_buffer_pool_reserve_meta ){.size = MB_TO_BYTES (8 )});
425+
426+ ASSERT_FALSE (aws_future_s3_buffer_ticket_is_done (over_future ));
427+
428+ struct s_reserve_state state = {.future = over_future };
429+
430+ aws_future_s3_buffer_ticket_register_callback (over_future , s_on_pool_buffer_reserved_cancel , & state );
431+
432+ /* simulate request cancel by erroring out the future */
433+ aws_future_s3_buffer_ticket_set_error (over_future , AWS_ERROR_S3_CANCELED );
434+
435+ for (size_t i = 0 ; i < 112 ; ++ i ) {
436+
437+ aws_s3_buffer_ticket_release (tickets [i ]);
438+
439+ aws_future_s3_buffer_ticket_release (ticket_futures [i ]);
440+ }
441+
442+ /* make sure that errored future was never filled */
443+ ASSERT_NULL (state .ticket );
444+
445+ aws_s3_buffer_ticket_release (state .ticket );
446+
447+ aws_future_s3_buffer_ticket_release (over_future );
448+
449+ aws_s3_default_buffer_pool_destroy (buffer_pool );
450+
451+ return 0 ;
452+ };
453+ AWS_TEST_CASE (test_s3_buffer_pool_reserve_over_limit_cancel , s_test_s3_buffer_pool_reserve_over_limit_cancel )
454+
327455static int s_test_s3_buffer_pool_too_small (struct aws_allocator * allocator , void * ctx ) {
328456 (void )allocator ;
329457 (void )ctx ;
0 commit comments