Repository: aeron-io/aeron (language: java)

Issue:
MediaDriver keepalive timeout due to driver being slow on munmaps
In the case where we have an application subscribing to a large number of streams, if we then shut that application down we have a massive influx of freeing the publication images.
Due to munmap being potentially slow (in the milliseconds) and the large number of streams, we can see a single driver_conductor work loop take over 1 second. This then triggers media driver keepalive timeouts for clients.

Would it be possible to queue up the munmaps so that other driver_conductor work isn't entirely blocked by a large number of munmaps? i.e. only free N publication images per driver_conductor work loop?


Discussion / hints:
Just FYI. We are looking into what can be done better here.
What would be the downside of queuing the image frees?
That may be what we end up as a solution. However, it makes reasoning about free space in `/dev/shm` difficult as well as adds some possible conflicts to consider.

Fix patch:
diff --git a/aeron-client/src/main/c/CMakeLists.txt b/aeron-client/src/main/c/CMakeLists.txt
index fd8c718057..d1f358e4aa 100644
--- a/aeron-client/src/main/c/CMakeLists.txt
+++ b/aeron-client/src/main/c/CMakeLists.txt
@@ -87,6 +87,7 @@ set(SOURCE
     util/aeron_arrayutil.c
     util/aeron_bitutil.c
     util/aeron_clock.c
+    util/aeron_deque.c
     util/aeron_dlopen.c
     util/aeron_env.c
     util/aeron_error.c
@@ -156,6 +157,7 @@ set(HEADERS
     util/aeron_arrayutil.h
     util/aeron_bitutil.h
     util/aeron_clock.h
+    util/aeron_deque.h
     util/aeron_dlopen.h
     util/aeron_env.h
     util/aeron_error.h
diff --git a/aeron-client/src/main/c/util/aeron_deque.c b/aeron-client/src/main/c/util/aeron_deque.c
new file mode 100644
index 0000000000..8da7c08ce7
--- /dev/null
+++ b/aeron-client/src/main/c/util/aeron_deque.c
@@ -0,0 +1,142 @@
+//
+// Created by mike on 23/01/23.
+//
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include "aeron_deque.h"
+#include "aeron_alloc.h"
+#include "aeron_error.h"
+
+
+static inline size_t aeron_deque_size(aeron_deque_t *deque)
+{
+    return deque->last_element >= deque->first_element ? deque->last_element - deque->first_element :
+        deque->element_count - (deque->first_element - deque->last_element);
+}
+
+static inline bool aeron_deque_has_capacity(aeron_deque_t *deque)
+{
+    return aeron_deque_size(deque) < (deque->element_count - 1);
+}
+
+int aeron_deque_init(aeron_deque_t *deque, size_t initial_element_count, size_t element_size)
+{
+    size_t initial_size = initial_element_count * element_size;
+    if (aeron_alloc((void **)&deque->data, initial_size) < 0)
+    {
+        AERON_APPEND_ERR("%s", "");
+        return -1;
+    }
+
+    deque->element_count = initial_element_count;
+    deque->element_size = element_size;
+    deque->first_element = 0;
+    deque->last_element = 0;
+
+    return 0;
+}
+
+void aeron_deque_close(aeron_deque_t *deque)
+{
+    aeron_free(deque->data);
+}
+
+static int aeron_deque_reallocate(aeron_deque_t *deque)
+{
+    if ((SIZE_MAX >> 1) <= deque->element_count)
+    {
+        AERON_SET_ERR(ENOMEM, "increased size will exceed %" PRIu64, SIZE_MAX);
+        return -1;
+    }
+
+    if (deque->first_element == deque->last_element)
+    {
+        return 0;
+    }
+
+    size_t new_element_count = deque->element_count << 1;
+    size_t new_size = new_element_count * deque->element_size;
+    void *new_data = NULL;
+
+    if (aeron_alloc(&new_data, new_size) < 0)
+    {
+        AERON_APPEND_ERR("%s", "");
+        return -1;
+    }
+
+    size_t size = aeron_deque_size(deque);
+    if (deque->first_element < deque->last_element)
+    {
+        size_t first_offset = deque->first_element * deque->element_size;
+        uint8_t *first_ptr = deque->data + first_offset;
+        size_t mem_size = size * deque->element_size;
+
+        memcpy(new_data, first_ptr, mem_size);
+    }
+    else if (deque->first_element > deque->last_element)
+    {
+        size_t first_offset = deque->first_element * deque->element_size;
+        uint8_t *first_ptr = deque->data + first_offset;
+        size_t num_bytes_first_to_end_of_buffer = (deque->element_count - deque->first_element) * deque->element_size;
+        size_t num_bytes_begin_of_buffer_to_last = deque->last_element * deque->element_size;
+        uint8_t *dest_last_ptr = (uint8_t *)new_data + num_bytes_first_to_end_of_buffer;
+
+        memcpy(new_data, (void *)first_ptr, num_bytes_first_to_end_of_buffer);
+        memcpy((void *)dest_last_ptr, deque->data, num_bytes_begin_of_buffer_to_last);
+    }
+
+    void *old_data = (void *)deque->data;
+    deque->data = new_data;
+    deque->first_element = 0;
+    deque->last_element = size;
+    deque->element_count = new_element_count;
+
+    aeron_free(old_data);
+
+    return 0;
+}
+
+int aeron_deque_add_last(aeron_deque_t *deque, void *value)
+{
+    if (!aeron_deque_has_capacity(deque))
+    {
+        if (aeron_deque_reallocate(deque) < 0)
+        {
+            AERON_APPEND_ERR("%s", "");
+            return -1;
+        }
+    }
+
+    size_t last_offset = deque->last_element * deque->element_size;
+    uint8_t *last_ptr = deque->data + last_offset;
+    memcpy(last_ptr, (uint8_t *)value, deque->element_size);
+    ++deque->last_element;
+    if (deque->last_element == deque->element_count)
+    {
+        deque->last_element = 0;
+    }
+
+    return 0;
+}
+
+int aeron_deque_remove_first(aeron_deque_t *deque, void *value)
+{
+    if (aeron_deque_size(deque) == 0)
+    {
+        AERON_SET_ERR(EINVAL, "%s", "deque is empty");
+        return -1;
+    }
+    size_t first_offset = deque->first_element * deque->element_size;
+    uint8_t *first_ptr = deque->data + first_offset;
+    memcpy(value, (void *)first_ptr, deque->element_size);
+    ++deque->first_element;
+    if (deque->first_element == deque->element_count)
+    {
+        deque->first_element = 0;
+    }
+
+    return 0;
+}
+
diff --git a/aeron-client/src/main/c/util/aeron_deque.h b/aeron-client/src/main/c/util/aeron_deque.h
new file mode 100644
index 0000000000..4df722049e
--- /dev/null
+++ b/aeron-client/src/main/c/util/aeron_deque.h
@@ -0,0 +1,40 @@
+//
+// Created by mike on 23/01/23.
+//
+
+#ifndef AERON_DEQUE_H
+#define AERON_DEQUE_H
+
+#include <stdint.h>
+#include <string.h>
+
+struct aeron_deque_stct
+{
+    uint8_t *data;
+    size_t element_count;
+    size_t element_size;
+    size_t first_element;
+    size_t last_element;
+};
+typedef struct aeron_deque_stct aeron_deque_t;
+
+int aeron_deque_init(aeron_deque_t *deque, size_t initial_element_count, size_t element_size);
+
+void aeron_deque_close(aeron_deque_t *deque);
+
+/**
+ * Add value into the deque as the last element.  Will memcpy into the deque from the void pointer provided using
+ * the specified element size.  May need to allocate in order to increase the size of the dequeue.
+ *
+ * Errors:
+ *   ENOMEM if growing the array fails.
+ *
+ * @param deque to add the value too.
+ * @param value value to be added.
+ * @return 0 on success, -1 on failure.
+ */
+int aeron_deque_add_last(aeron_deque_t *deque, void *value);
+
+int aeron_deque_remove_first(aeron_deque_t *deque, void *value);
+
+#endif //AERON_DEQUE_H
diff --git a/aeron-driver/src/main/c/CMakeLists.txt b/aeron-driver/src/main/c/CMakeLists.txt
index d9eb8a74e3..8b5c48a35f 100644
--- a/aeron-driver/src/main/c/CMakeLists.txt
+++ b/aeron-driver/src/main/c/CMakeLists.txt
@@ -153,6 +153,7 @@ SET(C_CLIENT_SOURCE
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_arrayutil.c
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_bitutil.c
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_clock.c
+    ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_deque.c
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_dlopen.c
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_env.c
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_error.c
@@ -220,6 +221,7 @@ set(C_CLIENT_HEADERS
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_arrayutil.h
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_bitutil.h
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_clock.h
+    ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_deque.h
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_dlopen.h
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_env.h
     ${AERON_C_CLIENT_SOURCE_PATH}/util/aeron_error.h
diff --git a/aeron-driver/src/main/c/aeron_driver_conductor.c b/aeron-driver/src/main/c/aeron_driver_conductor.c
index 296e18a9cb..57381a2d3a 100644
--- a/aeron-driver/src/main/c/aeron_driver_conductor.c
+++ b/aeron-driver/src/main/c/aeron_driver_conductor.c
@@ -462,7 +462,7 @@ int aeron_driver_conductor_init(aeron_driver_conductor_t *conductor, aeron_drive
     conductor->clients.on_time_event = aeron_client_on_time_event;
     conductor->clients.has_reached_end_of_life = aeron_client_has_reached_end_of_life;
     conductor->clients.delete_func = aeron_client_delete;
-    conductor->clients.free_func = aeron_client_free;
+    conductor->clients.free_func = NULL;
 
     conductor->ipc_publications.array = NULL;
     conductor->ipc_publications.length = 0;
@@ -487,7 +487,7 @@ int aeron_driver_conductor_init(aeron_driver_conductor_t *conductor, aeron_drive
     conductor->send_channel_endpoints.has_reached_end_of_life =
         aeron_send_channel_endpoint_entry_has_reached_end_of_life;
     conductor->send_channel_endpoints.delete_func = aeron_send_channel_endpoint_entry_delete;
-    conductor->send_channel_endpoints.free_func = aeron_send_channel_endpoint_entry_free;
+    conductor->send_channel_endpoints.free_func = NULL;
 
     conductor->receive_channel_endpoints.array = NULL;
     conductor->receive_channel_endpoints.length = 0;
@@ -496,7 +496,7 @@ int aeron_driver_conductor_init(aeron_driver_conductor_t *conductor, aeron_drive
     conductor->receive_channel_endpoints.has_reached_end_of_life =
         aeron_receive_channel_endpoint_entry_has_reached_end_of_life;
     conductor->receive_channel_endpoints.delete_func = aeron_receive_channel_endpoint_entry_delete;
-    conductor->receive_channel_endpoints.free_func = aeron_receive_channel_endpoint_entry_free;
+    conductor->receive_channel_endpoints.free_func = NULL;
 
     conductor->publication_images.array = NULL;
     conductor->publication_images.length = 0;
@@ -512,7 +512,7 @@ int aeron_driver_conductor_init(aeron_driver_conductor_t *conductor, aeron_drive
     conductor->lingering_resources.on_time_event = aeron_linger_resource_entry_on_time_event;
     conductor->lingering_resources.has_reached_end_of_life = aeron_linger_resource_entry_has_reached_end_of_life;
     conductor->lingering_resources.delete_func = aeron_linger_resource_entry_delete;
-    conductor->lingering_resources.free_func = aeron_linger_resource_entry_free;
+    conductor->lingering_resources.free_func = NULL;
 
     conductor->ipc_subscriptions.array = NULL;
     conductor->ipc_subscriptions.length = 0;
@@ -526,6 +526,12 @@ int aeron_driver_conductor_init(aeron_driver_conductor_t *conductor, aeron_drive
     conductor->spy_subscriptions.length = 0;
     conductor->spy_subscriptions.capacity = 0;
 
+    if (aeron_deque_init(&conductor->end_of_life_queue, 1024, sizeof(aeron_end_of_life_resource_t)))
+    {
+        AERON_APPEND_ERR("%s", "");
+        return -1;
+    }
+
     conductor->errors_counter = aeron_counters_manager_addr(&conductor->counters_manager, AERON_SYSTEM_COUNTER_ERRORS);
     conductor->unblocked_commands_counter = aeron_counters_manager_addr(
         &conductor->counters_manager, AERON_SYSTEM_COUNTER_UNBLOCKED_COMMANDS);
@@ -978,7 +984,7 @@ void aeron_client_delete(aeron_driver_conductor_t *conductor, aeron_client_t *cl
     client->heartbeat_timestamp.value_addr = NULL;
 }
 
-bool aeron_client_free(aeron_client_t *client)
+bool aeron_client_free(void *resource)
 {
     return true;
 }
@@ -1112,18 +1118,14 @@ void aeron_ipc_publication_entry_delete(aeron_driver_conductor_t *conductor, aer
     entry->publication = NULL;
 }
 
-bool aeron_ipc_publication_entry_free(aeron_ipc_publication_entry_t *entry)
+static inline bool aeron_ipc_publication_free_voidp(void *publication)
 {
-    aeron_ipc_publication_t *publication = entry->publication;
-    if (publication->raw_log_free_func(&publication->mapped_raw_log, publication->log_file_name))
-    {
-        aeron_free(publication->log_file_name);
-        publication->log_file_name = NULL;
-
-        return true;
-    }
+    return aeron_ipc_publication_free((aeron_ipc_publication_t *)publication);
+}
 
-    return false;
+void aeron_ipc_publication_entry_free(aeron_driver_conductor_t *conductor, aeron_ipc_publication_entry_t *entry)
+{
+    aeron_driver_conductor_add_end_of_life_resource(conductor, entry->publication, aeron_ipc_publication_free_voidp);
 }
 
 void aeron_network_publication_entry_on_time_event(
@@ -1163,18 +1165,14 @@ void aeron_network_publication_entry_delete(
     }
 }
 
-bool aeron_network_publication_entry_free(aeron_network_publication_entry_t *entry)
+static inline bool aeron_network_publication_free_voidp(void *publication)
 {
-    aeron_network_publication_t *publication = entry->publication;
-    if (publication->raw_log_free_func(&publication->mapped_raw_log, publication->log_file_name))
-    {
-        aeron_free(publication->log_file_name);
-        publication->log_file_name = NULL;
-
-        return true;
-    }
+    return aeron_network_publication_free((aeron_network_publication_t *)publication);
+}
 
-    return false;
+void aeron_network_publication_entry_free(aeron_driver_conductor_t *conductor, aeron_network_publication_entry_t *entry)
+{
+    aeron_driver_conductor_add_end_of_life_resource(conductor, entry->publication, aeron_network_publication_free_voidp);
 }
 
 void aeron_driver_conductor_cleanup_spies(aeron_driver_conductor_t *conductor, aeron_network_publication_t *publication)
@@ -1226,11 +1224,6 @@ void aeron_send_channel_endpoint_entry_delete(
     aeron_send_channel_endpoint_delete(&conductor->counters_manager, entry->endpoint);
 }
 
-bool aeron_send_channel_endpoint_entry_free(aeron_send_channel_endpoint_entry_t *entry)
-{
-    return true;
-}
-
 void aeron_receive_channel_endpoint_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_receive_channel_endpoint_entry_t *entry, int64_t now_ns, int64_t now_ms)
 {
@@ -1267,11 +1260,6 @@ void aeron_receive_channel_endpoint_entry_delete(
     aeron_receive_channel_endpoint_delete(&conductor->counters_manager, entry->endpoint);
 }
 
-bool aeron_receive_channel_endpoint_entry_free(aeron_receive_channel_endpoint_entry_t *entry)
-{
-    return true;
-}
-
 void aeron_publication_image_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_publication_image_entry_t *entry, int64_t now_ns, int64_t now_ms)
 {
@@ -1302,18 +1290,14 @@ void aeron_publication_image_entry_delete(
     entry->image = NULL;
 }
 
-bool aeron_publication_image_entry_free(aeron_publication_image_entry_t *entry)
+static inline bool aeron_publication_image_free_voidp(void *image)
 {
-    aeron_publication_image_t *image = entry->image;
-    if (image->raw_log_free_func(&image->mapped_raw_log, image->log_file_name))
-    {
-        aeron_free(image->log_file_name);
-        image->log_file_name = NULL;
-
-        return true;
-    }
+    return aeron_publication_image_free((aeron_publication_image_t *)image);
+}
 
-    return false;
+void aeron_publication_image_entry_free(aeron_driver_conductor_t *conductor, aeron_publication_image_entry_t *entry)
+{
+    aeron_driver_conductor_add_end_of_life_resource(conductor, entry->image, aeron_publication_image_free_voidp);
 }
 
 void aeron_linger_resource_entry_on_time_event(
@@ -1336,11 +1320,6 @@ void aeron_linger_resource_entry_delete(aeron_driver_conductor_t *conductor, aer
     aeron_free(entry->buffer);
 }
 
-bool aeron_linger_resource_entry_free(aeron_linger_resource_entry_t *entry)
-{
-    return true;
-}
-
 // Called from conductor
 void aeron_driver_conductor_image_transition_to_linger(
     aeron_driver_conductor_t *conductor, aeron_publication_image_t *image)
@@ -1378,26 +1357,63 @@ void aeron_driver_conductor_image_transition_to_linger(
     }
 }
 
-#define AERON_DRIVER_CONDUCTOR_CHECK_MANAGED_RESOURCE(c, l, t, now_ns, now_ms) \
-for (int last_index = (int)l.length - 1, i = last_index; i >= 0; i--) \
-{ \
-    t *elem = &l.array[i]; \
-    l.on_time_event(c, elem, now_ns, now_ms); \
-    if (l.has_reached_end_of_life(c, elem)) \
-    { \
-        if (l.free_func(elem)) \
-        { \
-            l.delete_func(c, elem); \
-            aeron_array_fast_unordered_remove((uint8_t *)l.array, sizeof(t), i, last_index); \
-            last_index--; \
-            l.length--; \
-        } \
-        else \
-        { \
-            int64_t *counter = aeron_system_counter_addr(&c->system_counters, AERON_SYSTEM_COUNTER_FREE_FAILS); \
-            aeron_counter_ordered_increment(counter, 1); \
-        } \
-    } \
+void aeron_driver_conductor_add_end_of_life_resource(
+    aeron_driver_conductor_t *conductor,
+    void *resource,
+    aeron_end_of_life_resource_free_t free_func)
+{
+    aeron_end_of_life_resource_t end_of_life_resource = { 0 };
+    int64_t *counter = aeron_system_counter_addr(
+        &conductor->system_counters, AERON_SYSTEM_COUNTER_FREE_FAILS);
+
+    end_of_life_resource.resource = resource;
+    end_of_life_resource.free_func = free_func;
+    if (!aeron_deque_add_last(&conductor->end_of_life_queue, (void *)&end_of_life_resource))
+    {
+        aeron_counter_ordered_increment(counter, 1);
+    }
+}
+
+int aeron_driver_conductor_free_end_of_life_resources(aeron_driver_conductor_t *conductor)
+{
+    const int limit = (int)conductor->context->resource_free_limit;
+    aeron_end_of_life_resource_t end_of_life_resource = { 0 };
+    int count = 0;
+
+    for (; count < limit; count++)
+    {
+        if (aeron_deque_remove_first(&conductor->end_of_life_queue, (void *)&end_of_life_resource) < 0)
+        {
+            break;
+        }
+
+        if (!end_of_life_resource.free_func(end_of_life_resource.resource))
+        {
+            int64_t *counter = aeron_system_counter_addr(&conductor->system_counters, AERON_SYSTEM_COUNTER_FREE_FAILS);
+            aeron_counter_ordered_increment(counter, 1);
+            aeron_deque_add_last(&conductor->end_of_life_queue, (void *)&end_of_life_resource);
+        }
+    }
+
+    return count;
+}
+
+#define AERON_DRIVER_CONDUCTOR_CHECK_MANAGED_RESOURCE(c, l, t, now_ns, now_ms)             \
+for (int last_index = (int)l.length - 1, i = last_index; i >= 0; i--)                      \
+{                                                                                          \
+    t *elem = &l.array[i];                                                                 \
+    l.on_time_event(c, elem, now_ns, now_ms);                                              \
+    if (l.has_reached_end_of_life(c, elem))                                                \
+    {                                                                                      \
+        if (NULL != l.free_func)                                                           \
+        {                                                                                  \
+            l.free_func(c, elem);                                                          \
+        }                                                                                  \
+        l.delete_func(c, elem);                                                            \
+        aeron_array_fast_unordered_remove((uint8_t *)l.array, sizeof(t), i, last_index);   \
+        last_index--;                                                                      \
+        l.length--;                                                                        \
+    }                                                                                      \
 }
 
 void aeron_driver_conductor_on_check_managed_resources(
@@ -2863,6 +2879,8 @@ int aeron_driver_conductor_do_work(void *clientd)
         aeron_publication_image_track_rebuild(conductor->publication_images.array[i].image, now_ns);
     }
 
+    work_count += aeron_driver_conductor_free_end_of_life_resources(conductor);
+
     return work_count;
 }
 
@@ -2882,6 +2900,7 @@ void aeron_driver_conductor_on_close(void *clientd)
     for (size_t i = 0, length = conductor->ipc_publications.length; i < length; i++)
     {
         aeron_ipc_publication_close(&conductor->counters_manager, conductor->ipc_publications.array[i].publication);
+        aeron_ipc_publication_free(conductor->ipc_publications.array[i].publication);
     }
     aeron_free(conductor->ipc_publications.array);
 
@@ -2889,6 +2908,7 @@ void aeron_driver_conductor_on_close(void *clientd)
     {
         aeron_network_publication_close(
             &conductor->counters_manager, conductor->network_publications.array[i].publication);
+        aeron_network_publication_free(conductor->network_publications.array[i].publication);
     }
     aeron_free(conductor->network_publications.array);
 
@@ -2928,9 +2948,13 @@ void aeron_driver_conductor_on_close(void *clientd)
     for (size_t i = 0, length = conductor->publication_images.length; i < length; i++)
     {
         aeron_publication_image_close(&conductor->counters_manager, conductor->publication_images.array[i].image);
+        aeron_publication_image_free(conductor->publication_images.array[i].image);
     }
     aeron_free(conductor->publication_images.array);
 
+    // Free remaining end_of_life_entries.
+    aeron_deque_close(&conductor->end_of_life_queue);
+
     aeron_system_counters_close(&conductor->system_counters);
     aeron_counters_manager_close(&conductor->counters_manager);
     aeron_distinct_error_log_close(&conductor->error_log);
diff --git a/aeron-driver/src/main/c/aeron_driver_conductor.h b/aeron-driver/src/main/c/aeron_driver_conductor.h
index 40d7657a83..95687bfa1c 100644
--- a/aeron-driver/src/main/c/aeron_driver_conductor.h
+++ b/aeron-driver/src/main/c/aeron_driver_conductor.h
@@ -32,6 +32,7 @@
 #include "aeron_driver_conductor_proxy.h"
 #include "aeron_publication_image.h"
 #include "reports/aeron_loss_reporter.h"
+#include "util/aeron_deque.h"
 
 #define AERON_DRIVER_CONDUCTOR_LINGER_RESOURCE_TIMEOUT_NS (5 * 1000 * 1000 * 1000LL)
 #define AERON_DRIVER_CONDUCTOR_CLOCK_UPDATE_INTERNAL_NS (1000 * 1000LL)
@@ -116,7 +117,7 @@ typedef struct aeron_ipc_publication_entry_stct
 {
     aeron_ipc_publication_t *publication;
 }
-aeron_ipc_publication_entry_t;
+ aeron_ipc_publication_entry_t;
 
 typedef struct aeron_network_publication_entry_stct
 {
@@ -150,6 +151,15 @@ typedef struct aeron_linger_resource_entry_stct
 }
 aeron_linger_resource_entry_t;
 
+typedef bool (*aeron_end_of_life_resource_free_t)(void *resource);
+
+struct aeron_end_of_life_resource_stct
+{
+    void *resource;
+    aeron_end_of_life_resource_free_t free_func;
+};
+typedef struct aeron_end_of_life_resource_stct aeron_end_of_life_resource_t;
+
 typedef struct aeron_driver_conductor_stct aeron_driver_conductor_t;
 
 typedef struct aeron_driver_conductor_stct
@@ -175,7 +185,7 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_client_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_client_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_client_t *);
-        bool (*free_func)(aeron_client_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_client_t *);
     }
     clients;
 
@@ -195,7 +205,7 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_ipc_publication_entry_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_ipc_publication_entry_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_ipc_publication_entry_t *);
-        bool (*free_func)(aeron_ipc_publication_entry_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_ipc_publication_entry_t *);
     }
     ipc_publications;
 
@@ -223,7 +233,7 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_network_publication_entry_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_network_publication_entry_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_network_publication_entry_t *);
-        bool (*free_func)(aeron_network_publication_entry_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_network_publication_entry_t *);
     }
     network_publications;
 
@@ -235,7 +245,7 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_send_channel_endpoint_entry_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_send_channel_endpoint_entry_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_send_channel_endpoint_entry_t *);
-        bool (*free_func)(aeron_send_channel_endpoint_entry_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_send_channel_endpoint_entry_t *);
     }
     send_channel_endpoints;
 
@@ -247,7 +257,7 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_receive_channel_endpoint_entry_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_receive_channel_endpoint_entry_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_receive_channel_endpoint_entry_t *);
-        bool (*free_func)(aeron_receive_channel_endpoint_entry_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_receive_channel_endpoint_entry_t *);
     }
     receive_channel_endpoints;
 
@@ -259,7 +269,7 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_publication_image_entry_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_publication_image_entry_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_publication_image_entry_t *);
-        bool (*free_func)(aeron_publication_image_entry_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_publication_image_entry_t *);
     }
     publication_images;
 
@@ -271,10 +281,12 @@ typedef struct aeron_driver_conductor_stct
         void (*on_time_event)(aeron_driver_conductor_t *, aeron_linger_resource_entry_t *, int64_t, int64_t);
         bool (*has_reached_end_of_life)(aeron_driver_conductor_t *, aeron_linger_resource_entry_t *);
         void (*delete_func)(aeron_driver_conductor_t *, aeron_linger_resource_entry_t *);
-        bool (*free_func)(aeron_linger_resource_entry_t *);
+        void (*free_func)(aeron_driver_conductor_t *, aeron_linger_resource_entry_t *);
     }
     lingering_resources;
 
+    aeron_deque_t end_of_life_queue;
+
     int64_t *errors_counter;
     int64_t *unblocked_commands_counter;
     int64_t *client_timeouts_counter;
@@ -297,9 +309,14 @@ void aeron_client_on_time_event(
 
 bool aeron_client_has_reached_end_of_life(aeron_driver_conductor_t *conductor, aeron_client_t *client);
 
+void aeron_driver_conductor_add_end_of_life_resource(
+    aeron_driver_conductor_t *conductor,
+    void *resource,
+    aeron_end_of_life_resource_free_t free_func);
+
 void aeron_client_delete(aeron_driver_conductor_t *conductor, aeron_client_t *);
 
-bool aeron_client_free(aeron_client_t *);
+bool aeron_client_free(void *);
 
 void aeron_ipc_publication_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_ipc_publication_entry_t *entry, int64_t now_ns, int64_t now_ms);
@@ -309,7 +326,7 @@ bool aeron_ipc_publication_entry_has_reached_end_of_life(
 
 void aeron_ipc_publication_entry_delete(aeron_driver_conductor_t *conductor, aeron_ipc_publication_entry_t *);
 
-bool aeron_ipc_publication_entry_free(aeron_ipc_publication_entry_t *);
+void aeron_ipc_publication_entry_free(aeron_driver_conductor_t *conductor, aeron_ipc_publication_entry_t *entry);
 
 void aeron_network_publication_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_network_publication_entry_t *entry, int64_t now_ns, int64_t now_ms);
@@ -319,7 +336,7 @@ bool aeron_network_publication_entry_has_reached_end_of_life(
 
 void aeron_network_publication_entry_delete(aeron_driver_conductor_t *conductor, aeron_network_publication_entry_t *);
 
-bool aeron_network_publication_entry_free(aeron_network_publication_entry_t *);
+void aeron_network_publication_entry_free(aeron_driver_conductor_t *conductor, aeron_network_publication_entry_t *entry);
 
 void aeron_send_channel_endpoint_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_send_channel_endpoint_entry_t *entry, int64_t now_ns, int64_t now_ms);
@@ -330,8 +347,6 @@ bool aeron_send_channel_endpoint_entry_has_reached_end_of_life(
 void aeron_send_channel_endpoint_entry_delete(
     aeron_driver_conductor_t *conductor, aeron_send_channel_endpoint_entry_t *);
 
-bool aeron_send_channel_endpoint_entry_free(aeron_send_channel_endpoint_entry_t *);
-
 void aeron_receive_channel_endpoint_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_receive_channel_endpoint_entry_t *entry, int64_t now_ns, int64_t now_ms);
 
@@ -341,8 +356,6 @@ bool aeron_receive_channel_endpoint_entry_has_reached_end_of_life(
 void aeron_receive_channel_endpoint_entry_delete(
     aeron_driver_conductor_t *conductor, aeron_receive_channel_endpoint_entry_t *);
 
-bool aeron_receive_channel_endpoint_entry_free(aeron_receive_channel_endpoint_entry_t *);
-
 void aeron_publication_image_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_publication_image_entry_t *entry, int64_t now_ns, int64_t now_ms);
 
@@ -351,7 +364,7 @@ bool aeron_publication_image_entry_has_reached_end_of_life(
 
 void aeron_publication_image_entry_delete(aeron_driver_conductor_t *conductor, aeron_publication_image_entry_t *);
 
-bool aeron_publication_image_entry_free(aeron_publication_image_entry_t *);
+void aeron_publication_image_entry_free(aeron_driver_conductor_t *conductor, aeron_publication_image_entry_t *entry);
 
 void aeron_linger_resource_entry_on_time_event(
     aeron_driver_conductor_t *conductor, aeron_linger_resource_entry_t *entry, int64_t now_ns, int64_t now_ms);
@@ -361,8 +374,6 @@ bool aeron_linger_resource_entry_has_reached_end_of_life(
 
 void aeron_linger_resource_entry_delete(aeron_driver_conductor_t *conductor, aeron_linger_resource_entry_t *);
 
-bool aeron_linger_resource_entry_free(aeron_linger_resource_entry_t *);
-
 void aeron_driver_conductor_image_transition_to_linger(
     aeron_driver_conductor_t *conductor, aeron_publication_image_t *image);
 
diff --git a/aeron-driver/src/main/c/aeron_driver_context.c b/aeron-driver/src/main/c/aeron_driver_context.c
index 6d32cd00d2..0bb8929484 100644
--- a/aeron-driver/src/main/c/aeron_driver_context.c
+++ b/aeron-driver/src/main/c/aeron_driver_context.c
@@ -333,6 +333,8 @@ static void aeron_driver_conductor_on_endpoint_change_null(const void *channel)
 #define AERON_RECEIVER_IO_VECTOR_CAPACITY_DEFAULT UINT32_C(2)
 #define AERON_SENDER_IO_VECTOR_CAPACITY_DEFAULT UINT32_C(2)
 #define AERON_SENDER_MAX_MESSAGES_PER_SEND_DEFAULT UINT32_C(2)
+#define AERON_DRIVER_RESOURCE_FREE_LIMIT_DEFAULT UINT32_C(10)
+#define AERON_DRIVER_RESOURCE_FREE_QUEUE_LENGTH_DEFAULT UINT32_C(65536)
 #define AERON_CPU_AFFINITY_DEFAULT (-1)
 #define AERON_DRIVER_CONNECT_DEFAULT true
 
@@ -529,6 +531,8 @@ int aeron_driver_context_init(aeron_driver_context_t **context)
     _context->receiver_io_vector_capacity = AERON_RECEIVER_IO_VECTOR_CAPACITY_DEFAULT;
     _context->sender_io_vector_capacity = AERON_SENDER_IO_VECTOR_CAPACITY_DEFAULT;
     _context->network_publication_max_messages_per_send = AERON_SENDER_MAX_MESSAGES_PER_SEND_DEFAULT;
+    _context->resource_free_limit = AERON_DRIVER_RESOURCE_FREE_LIMIT_DEFAULT;
+    _context->resource_free_queue_length = AERON_DRIVER_RESOURCE_FREE_LIMIT_DEFAULT;
     _context->connect_enabled = AERON_DRIVER_CONNECT_DEFAULT;
     _context->conductor_cpu_affinity_no = AERON_CPU_AFFINITY_DEFAULT;
     _context->sender_cpu_affinity_no = AERON_CPU_AFFINITY_DEFAULT;
@@ -973,6 +977,20 @@ int aeron_driver_context_init(aeron_driver_context_t **context)
         1,
         AERON_NETWORK_PUBLICATION_MAX_MESSAGES_PER_SEND);
 
+    _context->resource_free_limit = aeron_config_parse_uint32(
+        AERON_DRIVER_RESOURCE_FREE_LIMIT_ENV_VAR,
+        getenv(AERON_DRIVER_RESOURCE_FREE_LIMIT_ENV_VAR),
+        (uint32_t)_context->resource_free_limit,
+        1,
+        UINT32_MAX);
+
+    _context->resource_free_queue_length = aeron_config_parse_uint32(
+        AERON_DRIVER_RESOURCE_FREE_QUEUE_LENGTH_ENV_VAR,
+        getenv(AERON_DRIVER_RESOURCE_FREE_QUEUE_LENGTH_ENV_VAR),
+        (uint32_t)_context->resource_free_queue_length,
+        16,
+        UINT32_MAX);
+
     _context->to_driver_buffer = NULL;
     _context->to_clients_buffer = NULL;
     _context->counters_values_buffer = NULL;
@@ -2851,6 +2869,38 @@ uint32_t aeron_driver_context_get_network_publication_max_messages_per_send(aero
         context->network_publication_max_messages_per_send : AERON_SENDER_MAX_MESSAGES_PER_SEND_DEFAULT;
 }
 
+int aeron_driver_context_set_resource_free_limit(aeron_driver_context_t *context, uint32_t value)
+{
+    if (NULL == context)
+    {
+        return -1;
+    }
+
+    context->resource_free_limit = value;
+    return 0;
+}
+
+uint32_t aeron_driver_context_get_resource_free_limit(aeron_driver_context_t *context)
+{
+    return NULL != context ? context->resource_free_limit : AERON_DRIVER_RESOURCE_FREE_LIMIT_DEFAULT;
+}
+
+int aeron_driver_context_set_resource_free_queue_length(aeron_driver_context_t *context, uint32_t value)
+{
+    if (NULL == context)
+    {
+        return -1;
+    }
+
+    context->resource_free_queue_length = value;
+    return 0;
+}
+
+uint32_t aeron_driver_context_get_resource_free_queue_length(aeron_driver_context_t *context)
+{
+    return NULL != context ? context->resource_free_queue_length : AERON_DRIVER_RESOURCE_FREE_QUEUE_LENGTH_DEFAULT;
+}
+
 void aeron_set_thread_affinity_on_start(void *state, const char *role_name)
 {
     aeron_driver_context_t *context = (aeron_driver_context_t *)state;
diff --git a/aeron-driver/src/main/c/aeron_driver_context.h b/aeron-driver/src/main/c/aeron_driver_context.h
index 152f36b160..46af6c4b99 100644
--- a/aeron-driver/src/main/c/aeron_driver_context.h
+++ b/aeron-driver/src/main/c/aeron_driver_context.h
@@ -134,6 +134,8 @@ typedef struct aeron_driver_context_stct
     uint32_t receiver_io_vector_capacity;                   /* aeron.receiver.io.vector.capacity = 2 */
     uint32_t sender_io_vector_capacity;                     /* aeron.sender.io.vector.capacity = 2 */
     uint32_t network_publication_max_messages_per_send;     /* aeron.network.publication.max.messages.per.send = 2 */
+    uint32_t resource_free_limit;                           /* aeron.driver.resource.free.limit = 10 */
+    uint32_t resource_free_queue_length;                    /* aeron.driver.resource.free.queue.length = 65536 */
 
     int32_t conductor_cpu_affinity_no;                      /* aeron.conductor.cpu.affinity = -1 */
     int32_t receiver_cpu_affinity_no;                       /* aeron.receiver.cpu.affinity = -1 */
diff --git a/aeron-driver/src/main/c/aeron_ipc_publication.c b/aeron-driver/src/main/c/aeron_ipc_publication.c
index 6d53e6d213..64e229d4b9 100644
--- a/aeron-driver/src/main/c/aeron_ipc_publication.c
+++ b/aeron-driver/src/main/c/aeron_ipc_publication.c
@@ -200,12 +200,25 @@ void aeron_ipc_publication_close(aeron_counters_manager_t *counters_manager, aer
         }
         aeron_free(subscribable->array);
 
-        publication->raw_log_close_func(&publication->mapped_raw_log, publication->log_file_name);
-        aeron_free(publication->log_file_name);
         aeron_free(publication->channel);
     }
+}
+
+bool aeron_ipc_publication_free(aeron_ipc_publication_t *publication)
+{
+    if (NULL == publication)
+    {
+        return true;
+    }
+
+    if (!publication->raw_log_free_func(&publication->mapped_raw_log, publication->log_file_name))
+    {
+        return false;
+    }
 
+    aeron_free(publication->log_file_name);
     aeron_free(publication);
+    return true;
 }
 
 int aeron_ipc_publication_update_pub_lmt(aeron_ipc_publication_t *publication)
diff --git a/aeron-driver/src/main/c/aeron_ipc_publication.h b/aeron-driver/src/main/c/aeron_ipc_publication.h
index bcd4db377b..501e7f9734 100644
--- a/aeron-driver/src/main/c/aeron_ipc_publication.h
+++ b/aeron-driver/src/main/c/aeron_ipc_publication.h
@@ -95,6 +95,8 @@ int aeron_ipc_publication_create(
 
 void aeron_ipc_publication_close(aeron_counters_manager_t *counters_manager, aeron_ipc_publication_t *publication);
 
+bool aeron_ipc_publication_free(aeron_ipc_publication_t *publication);
+
 int aeron_ipc_publication_update_pub_lmt(aeron_ipc_publication_t *publication);
 
 void aeron_ipc_publication_clean_buffer(aeron_ipc_publication_t *publication, int64_t position);
diff --git a/aeron-driver/src/main/c/aeron_network_publication.c b/aeron-driver/src/main/c/aeron_network_publication.c
index 28d7358849..fee7861152 100644
--- a/aeron-driver/src/main/c/aeron_network_publication.c
+++ b/aeron-driver/src/main/c/aeron_network_publication.c
@@ -264,12 +264,25 @@ void aeron_network_publication_close(
         publication->conductor_fields.managed_resource.clientd = NULL;
 
         aeron_retransmit_handler_close(&publication->retransmit_handler);
-        publication->raw_log_close_func(&publication->mapped_raw_log, publication->log_file_name);
         publication->flow_control->fini(publication->flow_control);
-        aeron_free(publication->log_file_name);
+    }
+}
+
+bool aeron_network_publication_free(aeron_network_publication_t *publication)
+{
+    if (NULL == publication)
+    {
+        return true;
     }
 
+    if (!publication->raw_log_free_func(&publication->mapped_raw_log, publication->log_file_name))
+    {
+         return false;
+    }
+
+    aeron_free(publication->log_file_name);
     aeron_free(publication);
+    return true;
 }
 
 int aeron_network_publication_setup_message_check(
diff --git a/aeron-driver/src/main/c/aeron_network_publication.h b/aeron-driver/src/main/c/aeron_network_publication.h
index 0090b4e267..207f735acc 100644
--- a/aeron-driver/src/main/c/aeron_network_publication.h
+++ b/aeron-driver/src/main/c/aeron_network_publication.h
@@ -138,6 +138,8 @@ int aeron_network_publication_create(
 void aeron_network_publication_close(
     aeron_counters_manager_t *counters_manager, aeron_network_publication_t *publication);
 
+bool aeron_network_publication_free(aeron_network_publication_t *publication);
+
 void aeron_network_publication_incref(void *clientd);
 
 void aeron_network_publication_decref(void *clientd);
diff --git a/aeron-driver/src/main/c/aeron_publication_image.c b/aeron-driver/src/main/c/aeron_publication_image.c
index 8aa6255563..81ee9b7070 100644
--- a/aeron-driver/src/main/c/aeron_publication_image.c
+++ b/aeron-driver/src/main/c/aeron_publication_image.c
@@ -272,16 +272,29 @@ int aeron_publication_image_close(aeron_counters_manager_t *counters_manager, ae
         aeron_free(subscribable->array);
         aeron_free(image->connections.array);
 
-        image->raw_log_close_func(&image->mapped_raw_log, image->log_file_name);
         image->congestion_control->fini(image->congestion_control);
-        aeron_free(image->log_file_name);
     }
 
-    aeron_free(image);
-
     return 0;
 }
 
+bool aeron_publication_image_free(aeron_publication_image_t *image)
+{
+    if (NULL == image)
+    {
+        return true;
+    }
+
+    if (!image->raw_log_free_func(&image->mapped_raw_log, image->log_file_name))
+    {
+        return false;
+    }
+
+    aeron_free(image->log_file_name);
+    aeron_free(image);
+    return true;
+}
+
 void aeron_publication_image_clean_buffer_to(aeron_publication_image_t *image, int64_t position)
 {
     int64_t clean_position = image->conductor_fields.clean_position;
diff --git a/aeron-driver/src/main/c/aeron_publication_image.h b/aeron-driver/src/main/c/aeron_publication_image.h
index c80110f6db..001432bd3d 100644
--- a/aeron-driver/src/main/c/aeron_publication_image.h
+++ b/aeron-driver/src/main/c/aeron_publication_image.h
@@ -165,6 +165,8 @@ int aeron_publication_image_create(
 
 int aeron_publication_image_close(aeron_counters_manager_t *counters_manager, aeron_publication_image_t *image);
 
+bool aeron_publication_image_free(aeron_publication_image_t *image);
+
 void aeron_publication_image_clean_buffer_to(aeron_publication_image_t *image, int64_t position);
 
 void aeron_publication_image_on_gap_detected(void *clientd, int32_t term_id, int32_t term_offset, size_t length);
diff --git a/aeron-driver/src/main/c/aeronmd.h b/aeron-driver/src/main/c/aeronmd.h
index e01b6c1564..12269c5ab9 100644
--- a/aeron-driver/src/main/c/aeronmd.h
+++ b/aeron-driver/src/main/c/aeronmd.h
@@ -824,6 +824,14 @@ uint32_t aeron_driver_context_get_sender_io_vector_capacity(aeron_driver_context
 int aeron_driver_context_set_network_publication_max_messages_per_send(aeron_driver_context_t *context, uint32_t value);
 uint32_t aeron_driver_context_get_network_publication_max_messages_per_send(aeron_driver_context_t *context);
 
+#define AERON_DRIVER_RESOURCE_FREE_LIMIT_ENV_VAR "AERON_DRIVER_RESOURCE_FREE_LIMIT"
+int aeron_driver_context_set_resource_free_limit(aeron_driver_context_t *context, uint32_t value);
+uint32_t aeron_driver_context_get_resource_free_limit(aeron_driver_context_t *context);
+
+#define AERON_DRIVER_RESOURCE_FREE_QUEUE_LENGTH_ENV_VAR "AERON_DRIVER_RESOURCE_FREE_QUEUE_LENGTH"
+int aeron_driver_context_set_resource_free_queue_length(aeron_driver_context_t *context, uint32_t value);
+uint32_t aeron_driver_context_get_resource_free_queue_length(aeron_driver_context_t *context);
+
 #define AERON_CONDUCTOR_CPU_AFFINITY_ENV_VAR "AERON_CONDUCTOR_CPU_AFFINITY"
 #define AERON_RECEIVER_CPU_AFFINITY_ENV_VAR "AERON_RECEIVER_CPU_AFFINITY"
 #define AERON_SENDER_CPU_AFFINITY_ENV_VAR "AERON_SENDER_CPU_AFFINITY"
diff --git a/aeron-driver/src/main/java/io/aeron/driver/Configuration.java b/aeron-driver/src/main/java/io/aeron/driver/Configuration.java
index 31732cab15..75d281d872 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/Configuration.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/Configuration.java
@@ -235,6 +235,16 @@ public final class Configuration
      */
     public static final int SEND_TO_STATUS_POLL_RATIO_DEFAULT = 6;
 
+    /**
+     * Property name for the limit of the number of driver managed resources that can be freed in a single duty cycle.
+     */
+    public static final String RESOURCE_FREE_LIMIT_PROP_NAME = "aeron.driver.resource.free.limit";
+
+    /**
+     * Default value for the limit of the number of driver managed resources that can be freed in a single duty cycle.
+     */
+    public static final int RESOURCE_FREE_LIMIT_DEFAULT = 10;
+
     /**
      * Property name for SO_RCVBUF setting on UDP sockets which must be sufficient for Bandwidth Delay Product (BDP).
      */
@@ -1291,6 +1301,18 @@ public static int sendToStatusMessagePollRatio()
         return getInteger(SEND_TO_STATUS_POLL_RATIO_PROP_NAME, SEND_TO_STATUS_POLL_RATIO_DEFAULT);
     }
 
+    /**
+     * Limit the number of driver managed resources that can be freed in the same duty cycle.
+     *
+     * @return limit of the number of resources.
+     * @see #RESOURCE_FREE_LIMIT_PROP_NAME
+     * @see #RESOURCE_FREE_LIMIT_DEFAULT
+     */
+    public static int resourceFreeLimit()
+    {
+        return getInteger(RESOURCE_FREE_LIMIT_PROP_NAME, RESOURCE_FREE_LIMIT_DEFAULT);
+    }
+
     /**
      * Timeout between a counter being freed and being available to be reused.
      *
diff --git a/aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java b/aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java
index f81fd53008..a37d6b972b 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java
@@ -46,6 +46,7 @@
 import org.agrona.concurrent.status.UnsafeBufferPosition;
 
 import java.net.InetSocketAddress;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 
@@ -102,6 +103,7 @@ public final class DriverConductor implements Agent
     private final ArrayList<SubscriptionLink> subscriptionLinks = new ArrayList<>();
     private final ArrayList<CounterLink> counterLinks = new ArrayList<>();
     private final ArrayList<AeronClient> clients = new ArrayList<>();
+    private final ArrayDeque<DriverManagedResource> endOfLiveResources = new ArrayDeque<>();
     private final ObjectHashSet<SessionKey> activeSessionSet = new ObjectHashSet<>();
     private final EpochClock epochClock;
     private final NanoClock nanoClock;
@@ -191,6 +193,7 @@ public void onClose()
         publicationImages.forEach(PublicationImage::free);
         networkPublications.forEach(NetworkPublication::free);
         ipcPublications.forEach(IpcPublication::free);
+        freeEndOfLifeResources(Integer.MAX_VALUE);
         toDriverCommands.consumerHeartbeatTime(Aeron.NULL_VALUE);
         ctx.close();
     }
@@ -217,6 +220,7 @@ public int doWork()
         workCount += clientCommandAdapter.receive();
         workCount += trackStreamPositions(workCount, nowNs);
         workCount += nameResolver.doWork(cachedEpochClock.time());
+        workCount += freeEndOfLifeResources(ctx.resourceFreeLimit());
 
         return workCount;
     }
@@ -1970,17 +1974,37 @@ private <T extends DriverManagedResource> void checkManagedResources(
 
             if (resource.hasReachedEndOfLife())
             {
-                if (resource.free())
-                {
-                    fastUnorderedRemove(list, i, lastIndex--);
-                    CloseHelper.close(ctx.errorHandler(), resource);
-                }
-                else
-                {
-                    ctx.systemCounters().get(FREE_FAILS).incrementOrdered();
-                }
+                CloseHelper.close(ctx.errorHandler(), resource::close);
+                endOfLiveResources.add(resource);
+                fastUnorderedRemove(list, i, lastIndex--);
+            }
+        }
+    }
+
+    private int freeEndOfLifeResources(final int freeLimit)
+    {
+        int workCount = 0;
+
+        for (int i = 0; i < freeLimit; i++)
+        {
+            final DriverManagedResource resource = endOfLiveResources.pollFirst();
+            if (null == resource)
+            {
+                break;
+            }
+
+            if (resource.free())
+            {
+                workCount++;
+            }
+            else
+            {
+                ctx.systemCounters().get(FREE_FAILS).incrementOrdered();
+                endOfLiveResources.addLast(resource);
             }
         }
+
+        return workCount;
     }
 
     private void linkSpies(final ArrayList<SubscriptionLink> links, final NetworkPublication publication)
diff --git a/aeron-driver/src/main/java/io/aeron/driver/DriverManagedResource.java b/aeron-driver/src/main/java/io/aeron/driver/DriverManagedResource.java
index 9cb0e902db..c31385dcd3 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/DriverManagedResource.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/DriverManagedResource.java
@@ -18,7 +18,7 @@
 /**
  * Common behaviour supported for driver resources such as publications and images.
  */
-public interface DriverManagedResource extends AutoCloseable
+public interface DriverManagedResource
 {
     /**
      * Free external resources such as files. if successful then return true.
@@ -31,7 +31,8 @@ default boolean free()
     }
 
     /**
-     * Close resources that are not external.
+     * Close resources that are not external.  This will not call {@link DriverManagedResource#free()}.  That will
+     * need to be called separately at the appropriate time.
      */
     void close();
 
diff --git a/aeron-driver/src/main/java/io/aeron/driver/IpcPublication.java b/aeron-driver/src/main/java/io/aeron/driver/IpcPublication.java
index 207fc932aa..e2640f1122 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/IpcPublication.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/IpcPublication.java
@@ -229,8 +229,6 @@ public void close()
                 CloseHelper.close(errorHandler, untetheredSubscription.position);
             }
         }
-
-        CloseHelper.close(errorHandler, rawLog);
     }
 
     /**
diff --git a/aeron-driver/src/main/java/io/aeron/driver/MediaDriver.java b/aeron-driver/src/main/java/io/aeron/driver/MediaDriver.java
index 0cda425f64..e102ce6dfd 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/MediaDriver.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/MediaDriver.java
@@ -474,6 +474,7 @@ public static final class Context extends CommonContext
         private int publicationReservedSessionIdHigh = Configuration.publicationReservedSessionIdHigh();
         private int lossReportBufferLength = Configuration.lossReportBufferLength();
         private int sendToStatusMessagePollRatio = Configuration.sendToStatusMessagePollRatio();
+        private int resourceFreeLimit = Configuration.resourceFreeLimit();
 
         private Long receiverGroupTag = Configuration.groupTag();
         private long flowControlGroupTag = Configuration.flowControlGroupTag();
@@ -3141,6 +3142,31 @@ public long receiverCycleThresholdNs()
             return receiverCycleThresholdNs;
         }
 
+        /**
+         * Maximum number of {@link DriverManagedResource}s to free within a single duty cycle within the conductor.
+         *
+         * @param resourceFreeLimit number of resources to limit to.
+         * @return this for a fluent API.
+         * @see Configuration#resourceFreeLimit()
+         * @see Configuration#RESOURCE_FREE_LIMIT_PROP_NAME
+         * @see Configuration#RESOURCE_FREE_LIMIT_DEFAULT
+         */
+        public Context resourceFreeLimit(final int resourceFreeLimit)
+        {
+            this.resourceFreeLimit = resourceFreeLimit;
+            return this;
+        }
+
+        /**
+         * Maximum number of {@link DriverManagedResource}s to free within a single duty cycle within the conductor.
+         *
+         * @return limit on the number of resources that can be freed.
+         */
+        public int resourceFreeLimit()
+        {
+            return resourceFreeLimit;
+        }
+
         /**
          * Clock used record channel receive timestamps.
          *
diff --git a/aeron-driver/src/main/java/io/aeron/driver/NetworkPublication.java b/aeron-driver/src/main/java/io/aeron/driver/NetworkPublication.java
index 18eb7c8a93..d54b13bae7 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/NetworkPublication.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/NetworkPublication.java
@@ -275,7 +275,6 @@ public void close()
         }
 
         CloseHelper.close(flowControl);
-        CloseHelper.close(errorHandler, rawLog);
     }
 
     /**
diff --git a/aeron-driver/src/main/java/io/aeron/driver/PublicationImage.java b/aeron-driver/src/main/java/io/aeron/driver/PublicationImage.java
index 4282054db6..e1503518e4 100644
--- a/aeron-driver/src/main/java/io/aeron/driver/PublicationImage.java
+++ b/aeron-driver/src/main/java/io/aeron/driver/PublicationImage.java
@@ -276,7 +276,6 @@ public void close()
         }
 
         CloseHelper.close(errorHandler, congestionControl);
-        CloseHelper.close(errorHandler, rawLog);
     }
 
     /**


Test patch:
diff --git a/aeron-client/src/test/c/CMakeLists.txt b/aeron-client/src/test/c/CMakeLists.txt
index 1ee060dad6..9695cebce8 100644
--- a/aeron-client/src/test/c/CMakeLists.txt
+++ b/aeron-client/src/test/c/CMakeLists.txt
@@ -57,6 +57,7 @@ aeron_c_client_test(fragment_assembler_test aeron_fragment_assembler_test.cpp)
 aeron_c_client_test(aeron_fileutil_test util/aeron_fileutil_test.cpp)
 aeron_c_client_test(uri_test aeron_uri_test.cpp)
 aeron_c_client_test(bitutil_test util/aeron_bitutil_test.cpp)
+aeron_c_client_test(deque_test util/aeron_deque_test.cpp)
 aeron_c_client_test(fileutil_test util/aeron_fileutil_test.cpp)
 aeron_c_client_test(math_test util/aeron_math_test.cpp)
 aeron_c_client_test(strutil_test util/aeron_strutil_test.cpp)
diff --git a/aeron-client/src/test/c/util/aeron_deque_test.cpp b/aeron-client/src/test/c/util/aeron_deque_test.cpp
new file mode 100644
index 0000000000..e9c64228f5
--- /dev/null
+++ b/aeron-client/src/test/c/util/aeron_deque_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2014-2023 Real Logic Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+extern "C"
+{
+#include "util/aeron_deque.h"
+}
+
+class DequeTest : public testing::Test
+{
+public:
+    DequeTest() = default;
+};
+
+TEST_F(DequeTest, shouldAddLastAndRemoveFirst)
+{
+    aeron_deque_t deque = {};
+    uint64_t expected = 8723642836;
+    uint64_t actual = 0;
+    ASSERT_EQ(0, aeron_deque_init(&deque, 8, sizeof(uint64_t)));
+    ASSERT_EQ(0, aeron_deque_add_last(&deque, &expected));
+    ASSERT_EQ(0, aeron_deque_remove_first(&deque, &actual));
+
+    ASSERT_EQ(expected, actual);
+
+    aeron_deque_close(&deque);
+}
+
+TEST_F(DequeTest, shouldReturnErrorIfEmptyOnRemove)
+{
+    aeron_deque_t deque = {};
+    uint64_t actual = 0;
+    ASSERT_EQ(0, aeron_deque_init(&deque, 8, sizeof(uint64_t)));
+    ASSERT_EQ(-1, aeron_deque_remove_first(&deque, &actual));
+
+    aeron_deque_close(&deque);
+}
+
+TEST_F(DequeTest, shouldWrapAroundWithAddsAndRemoves)
+{
+    aeron_deque_t deque = {};
+    uint64_t expected = 0;
+    uint64_t actual = 0;
+    ASSERT_EQ(0, aeron_deque_init(&deque, 8, sizeof(uint64_t)));
+
+    for (int i = 0; i < 64; i++)
+    {
+        expected++;
+
+        ASSERT_EQ(0, aeron_deque_add_last(&deque, &expected));
+        ASSERT_EQ(0, aeron_deque_remove_first(&deque, &actual));
+        ASSERT_EQ(expected, actual);
+    }
+
+    aeron_deque_close(&deque);
+}
+
+TEST_F(DequeTest, shouldExpandMultipleAddsWithWrappedBuffer)
+{
+    aeron_deque_t deque = {};
+    uint64_t expected = 0;
+    uint64_t actual = 0;
+    ASSERT_EQ(0, aeron_deque_init(&deque, 8, sizeof(uint64_t)));
+
+    for (int i = 0; i < 13; i++)
+    {
+        expected++;
+        ASSERT_EQ(0, aeron_deque_add_last(&deque, &expected));
+        ASSERT_EQ(0, aeron_deque_remove_first(&deque, &actual));
+    }
+
+    std::cout << deque.first_element << " " << deque.last_element << std::endl;
+
+    expected = 0;
+    for (int i = 0; i < 64; i++)
+    {
+        expected++;
+
+        ASSERT_EQ(0, aeron_deque_add_last(&deque, &expected));
+    }
+
+    expected = 0;
+    for (int i = 0; i < 64; i++)
+    {
+        expected++;
+
+        ASSERT_EQ(0, aeron_deque_remove_first(&deque, &actual)) << "i = " << i;
+        ASSERT_EQ(expected, actual);
+    }
+
+    aeron_deque_close(&deque);
+}
+
+TEST_F(DequeTest, shouldExpandMultipleAdds)
+{
+    aeron_deque_t deque = {};
+    uint64_t expected = 0;
+    uint64_t actual = 0;
+    ASSERT_EQ(0, aeron_deque_init(&deque, 8, sizeof(uint64_t)));
+
+    for (int i = 0; i < 64; i++)
+    {
+        expected++;
+
+        ASSERT_EQ(0, aeron_deque_add_last(&deque, &expected));
+    }
+
+    expected = 0;
+    for (int i = 0; i < 64; i++)
+    {
+        expected++;
+
+        ASSERT_EQ(0, aeron_deque_remove_first(&deque, &actual)) << "i = " << i;
+        ASSERT_EQ(expected, actual);
+    }
+
+    aeron_deque_close(&deque);
+}
+
diff --git a/aeron-driver/src/test/c/aeron_driver_conductor_network_test.cpp b/aeron-driver/src/test/c/aeron_driver_conductor_network_test.cpp
index f1b1828f54..5aa5f84d21 100644
--- a/aeron-driver/src/test/c/aeron_driver_conductor_network_test.cpp
+++ b/aeron-driver/src/test/c/aeron_driver_conductor_network_test.cpp
@@ -288,7 +288,7 @@ TEST_F(DriverConductorNetworkTest, shouldRemoveSubscriptionAfterImageTimeout)
     EXPECT_EQ(aeron_driver_conductor_num_network_subscriptions(&m_conductor.m_conductor), 0u);
 }
 
-TEST_F(DriverConductorNetworkTest, shouldRemoveSubscriptionAfterImageTimeoutAfterRetryingFreeOperation)
+TEST_F(DriverConductorNetworkTest, shouldRetryFreeOperationsAfterSubscrptionIsClosed)
 {
     int64_t client_id = nextCorrelationId();
     int64_t sub_id = nextCorrelationId();
@@ -308,8 +308,7 @@ TEST_F(DriverConductorNetworkTest, shouldRemoveSubscriptionAfterImageTimeoutAfte
     free_map_raw_log = false;
     int64_t *free_fails_counter = aeron_system_counter_addr(
         &m_conductor.m_conductor.system_counters, AERON_SYSTEM_COUNTER_FREE_FAILS);
-    const int64_t free_fails = aeron_counter_get(free_fails_counter);
-    EXPECT_EQ(free_fails, 0);
+    EXPECT_EQ(aeron_counter_get(free_fails_counter), 0);
 
     doWorkForNs(
         image_timeout,
@@ -320,24 +319,16 @@ TEST_F(DriverConductorNetworkTest, shouldRemoveSubscriptionAfterImageTimeoutAfte
         });
 
     readAllBroadcastsFromConductor(null_broadcast_handler);
-    const int64_t free_fails_new = aeron_counter_get(free_fails_counter);
-    EXPECT_GE(free_fails_new, 2);
-    EXPECT_EQ(aeron_driver_conductor_num_images(&m_conductor.m_conductor), 1u);
-    EXPECT_EQ(aeron_driver_conductor_num_active_network_subscriptions(&m_conductor.m_conductor, CHANNEL_1, STREAM_ID_1), 1u);
-
-    const int64_t resource_check_interval = m_context.m_context->timer_interval_ns * 2;
+    const int64_t free_fails = aeron_counter_get(free_fails_counter);
+    EXPECT_GT(free_fails, 0);
+    EXPECT_EQ(aeron_driver_conductor_num_images(&m_conductor.m_conductor), 0u);
+    EXPECT_EQ(aeron_driver_conductor_num_active_network_subscriptions(&m_conductor.m_conductor, CHANNEL_1, STREAM_ID_1), 0u);
     free_map_raw_log = true;
 
-    doWorkForNs(
-        resource_check_interval,
-        100,
-        [&]()
-        {
-            clientKeepalive(client_id);
-        });
+    doWorkUntilDone();
 
     readAllBroadcastsFromConductor(null_broadcast_handler);
-    EXPECT_EQ(aeron_counter_get(free_fails_counter), free_fails_new);
+    EXPECT_EQ(aeron_counter_get(free_fails_counter), free_fails);
     EXPECT_EQ(aeron_driver_conductor_num_images(&m_conductor.m_conductor), 0u);
     EXPECT_EQ(aeron_driver_conductor_num_active_network_subscriptions(&m_conductor.m_conductor, CHANNEL_1, STREAM_ID_1), 0u);
     ASSERT_EQ(removeSubscription(client_id, remove_correlation_id, sub_id), 0);
diff --git a/aeron-driver/src/test/c/aeron_driver_conductor_pub_sub_test.cpp b/aeron-driver/src/test/c/aeron_driver_conductor_pub_sub_test.cpp
index 1a077e09fb..98b7a95bc4 100644
--- a/aeron-driver/src/test/c/aeron_driver_conductor_pub_sub_test.cpp
+++ b/aeron-driver/src/test/c/aeron_driver_conductor_pub_sub_test.cpp
@@ -878,7 +878,7 @@ TEST_P(DriverConductorPubSubTest, shouldBeAbleToTimeoutSendChannelEndpointWithCl
     EXPECT_TRUE(GetParam()->hasSendEndpointCount(&m_conductor.m_conductor, 0u));
 }
 
-TEST_P(DriverConductorPubSubTest, shouldBeAbleToTimeoutSendChannelEndpointWithClientKeepaliveAfterRemovePublicationAfterRetryingFreeOperation)
+TEST_P(DriverConductorPubSubTest, shouldRetryFreeOperationsAfterTimeoutSendChannelEndpointWithClientKeepaliveAfterRemovePublication)
 {
     const char *channel = GetParam()->m_channel;
     int64_t client_id = nextCorrelationId();
@@ -898,8 +898,7 @@ TEST_P(DriverConductorPubSubTest, shouldBeAbleToTimeoutSendChannelEndpointWithCl
     free_map_raw_log = false;
     int64_t *free_fails_counter = aeron_system_counter_addr(
         &m_conductor.m_conductor.system_counters, AERON_SYSTEM_COUNTER_FREE_FAILS);
-    const int64_t free_fails = aeron_counter_get(free_fails_counter);
-    EXPECT_EQ(free_fails, 0);
+    EXPECT_EQ(aeron_counter_get(free_fails_counter), 0);
 
     doWorkForNs(
         timeout,
@@ -909,11 +908,11 @@ TEST_P(DriverConductorPubSubTest, shouldBeAbleToTimeoutSendChannelEndpointWithCl
             clientKeepalive(client_id);
         });
 
-    const int64_t free_fails_new = aeron_counter_get(free_fails_counter);
-    EXPECT_GE(free_fails_new, 1);
+    const int64_t free_fails = aeron_counter_get(free_fails_counter);
+    EXPECT_GT(free_fails, 1);
     EXPECT_EQ(aeron_driver_conductor_num_clients(&m_conductor.m_conductor), 1u);
-    EXPECT_EQ(GetParam()->numPublications(&m_conductor.m_conductor), 1u);
-    EXPECT_TRUE(GetParam()->hasSendEndpointCount(&m_conductor.m_conductor, 1u));
+    EXPECT_EQ(GetParam()->numPublications(&m_conductor.m_conductor), 0u);
+    EXPECT_TRUE(GetParam()->hasSendEndpointCount(&m_conductor.m_conductor, 0u));
 
     const int64_t resource_check_interval = m_context.m_context->timer_interval_ns * 2;
     free_map_raw_log = true;
@@ -926,10 +925,8 @@ TEST_P(DriverConductorPubSubTest, shouldBeAbleToTimeoutSendChannelEndpointWithCl
             clientKeepalive(client_id);
         });
 
-    EXPECT_GE(aeron_counter_get(free_fails_counter), free_fails_new);
+    EXPECT_EQ(aeron_counter_get(free_fails_counter), free_fails);
     EXPECT_EQ(aeron_driver_conductor_num_clients(&m_conductor.m_conductor), 1u);
-    EXPECT_EQ(GetParam()->numPublications(&m_conductor.m_conductor), 0u);
-    EXPECT_TRUE(GetParam()->hasSendEndpointCount(&m_conductor.m_conductor, 0u));
 }
 
 TEST_P(DriverConductorPubSubTest, shouldBeAbleToTimeoutReceiveChannelEndpointWithClientKeepaliveAfterRemoveSubscription)
diff --git a/aeron-driver/src/test/c/aeron_ipc_publication_test.cpp b/aeron-driver/src/test/c/aeron_ipc_publication_test.cpp
index d048dc57ae..1817ffc680 100644
--- a/aeron-driver/src/test/c/aeron_ipc_publication_test.cpp
+++ b/aeron-driver/src/test/c/aeron_ipc_publication_test.cpp
@@ -56,6 +56,7 @@ class IpcPublicationTest : public testing::Test
         for (auto publication : m_publications)
         {
             aeron_ipc_publication_close(&m_counters_manager, publication);
+            aeron_ipc_publication_free(publication);
         }
 
         aeron_system_counters_close(&m_system_counters);
diff --git a/aeron-driver/src/test/c/aeron_network_publication_test.cpp b/aeron-driver/src/test/c/aeron_network_publication_test.cpp
index 904546a527..de16875bf7 100644
--- a/aeron-driver/src/test/c/aeron_network_publication_test.cpp
+++ b/aeron-driver/src/test/c/aeron_network_publication_test.cpp
@@ -70,6 +70,7 @@ class NetworkPublicationTest : public testing::Test
         for (auto publication : m_publications)
         {
             aeron_network_publication_close(&m_counters_manager, publication);
+            aeron_network_publication_free(publication);
         }
 
         for (auto endpoint : m_endpoints)
diff --git a/aeron-driver/src/test/c/aeron_receiver_test.h b/aeron-driver/src/test/c/aeron_receiver_test.h
index 4ecee77d8c..99a905442b 100644
--- a/aeron-driver/src/test/c/aeron_receiver_test.h
+++ b/aeron-driver/src/test/c/aeron_receiver_test.h
@@ -109,6 +109,7 @@ class ReceiverTestBase : public testing::Test
         for (auto image : m_images)
         {
             aeron_publication_image_close(&m_counters_manager, image);
+            aeron_publication_image_free(image);
         }
 
         for (auto endpoint : m_endpoints)