diff --git a/include/mach_detours.h b/include/mach_detours.h
index 5fa69a0..d6af642 100644
--- a/include/mach_detours.h
+++ b/include/mach_detours.h
@@ -205,11 +205,20 @@ bool detour_set_ignore_too_small(bool value);
/// @brief Toggle whether to free memory allocated for trampoline regions when they are empty.
///
/// Default is `true` - free unused regions upon commit.
-/// `false` means they are not freed upon commit. Note that there is no way to manually free unused regions.
+/// `false` means they are not freed upon commit. See also `detour_free_unused_regions`.
///
/// @returns the previous state of the flag
bool detour_set_retain_regions(bool value);
+/// @brief Frees unused trampoline memory regions
+///
+/// @note Must be called from within a transaction to maintain thread saefty.
+///
+/// @returns `err_none` on success, else:
+/// * `detour_err_wrong_thread` if the calling thread is not the owner of the transaction, or if there is no
+/// transaction. See `detour_transaction_begin`.
+mach_error_t detour_free_unused_regions();
+
/// @brief Sets the lower bound of the memory area that will not be used for detours trampoline regions
///
/// Default is `0x70000000`
diff --git a/src/mach_detours.c b/src/mach_detours.c
index d459486..6999286 100644
--- a/src/mach_detours.c
+++ b/src/mach_detours.c
@@ -1001,6 +1001,16 @@ bool detour_set_retain_regions(const bool value)
return previous;
}
+mach_error_t detour_free_unused_regions()
+{
+ if (s_transaction_thread != mach_thread_self()) {
+ return detour_err_wrong_thread;
+ }
+
+ internal_detour_free_unused_trampoline_regions();
+ return err_none;
+}
+
void* detour_set_system_region_lower_bound(void* value)
{
void* previous = s_system_region_lower_bound;