Skip to content

Split memory provider ops to mandatory and optional fields #322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 62 additions & 47 deletions include/umf/memory_provider_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,66 @@
extern "C" {
#endif

///
/// @brief This structure comprises optional function pointers used
/// by corresponding umfMemoryProvider* calls. A memory provider implementation
/// can keep them NULL.
///
typedef struct umf_memory_provider_ext_ops_t {
///
/// @brief Discard physical pages within the virtual memory mapping associated at the given addr
/// and \p size. This call is asynchronous and may delay purging the pages indefinitely.
/// @param provider pointer to the memory provider
/// @param ptr beginning of the virtual memory range
/// @param size size of the virtual memory range
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
/// UMF_RESULT_ERROR_INVALID_ALIGNMENT if ptr or size is not page-aligned.
/// UMF_RESULT_ERROR_NOT_SUPPORTED if operation is not supported by this provider.
///
umf_result_t (*purge_lazy)(void *provider, void *ptr, size_t size);

///
/// @brief Discard physical pages within the virtual memory mapping associated at the given addr and \p size.
/// This call is synchronous and if it succeeds, pages are guaranteed to be zero-filled on the next access.
/// @param provider pointer to the memory provider
/// @param ptr beginning of the virtual memory range
/// @param size size of the virtual memory range
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure
/// UMF_RESULT_ERROR_INVALID_ALIGNMENT if ptr or size is not page-aligned.
/// UMF_RESULT_ERROR_NOT_SUPPORTED if operation is not supported by this provider.
///
umf_result_t (*purge_force)(void *provider, void *ptr, size_t size);

///
/// @brief Merges two coarse grain allocations into a single allocation that
/// can be managed (freed) as a whole.
/// allocation_split and allocation_merge should be both set or both NULL.
/// @param hProvider handle to the memory provider
/// @param lowPtr pointer to the first allocation
/// @param highPtr pointer to the second allocation (should be > lowPtr)
/// @param totalSize size of a new merged allocation. Should be equal
/// to the sum of sizes of allocations beginning at lowPtr and highPtr
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure
///
umf_result_t (*allocation_merge)(void *hProvider, void *lowPtr,
void *highPtr, size_t totalSize);

///
/// @brief Splits a coarse grain allocation into 2 adjacent allocations that
/// can be managed (freed) separately.
/// allocation_split and allocation_merge should be both set or both NULL.
/// @param hProvider handle to the memory provider
/// @param ptr pointer to the beginning of the allocation
/// @param totalSize total size of the allocation to be split
/// @param firstSize size of the first new allocation, second allocation
// has a size equal to totalSize - firstSize
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure
///
umf_result_t (*allocation_split)(void *hProvider, void *ptr,
size_t totalSize, size_t firstSize);

} umf_memory_provider_ext_ops_t;

///
/// @brief This structure comprises function pointers used by corresponding
/// umfMemoryProvider* calls. Each memory provider implementation should
Expand Down Expand Up @@ -109,30 +169,6 @@ typedef struct umf_memory_provider_ops_t {
umf_result_t (*get_min_page_size)(void *provider, void *ptr,
size_t *pageSize);

///
/// @brief Discard physical pages within the virtual memory mapping associated at the given addr
/// and \p size. This call is asynchronous and may delay purging the pages indefinitely.
/// @param provider pointer to the memory provider
/// @param ptr beginning of the virtual memory range
/// @param size size of the virtual memory range
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
/// UMF_RESULT_ERROR_INVALID_ALIGNMENT if ptr or size is not page-aligned.
/// UMF_RESULT_ERROR_NOT_SUPPORTED if operation is not supported by this provider.
///
umf_result_t (*purge_lazy)(void *provider, void *ptr, size_t size);

///
/// @brief Discard physical pages within the virtual memory mapping associated at the given addr and \p size.
/// This call is synchronous and if it succeeds, pages are guaranteed to be zero-filled on the next access.
/// @param provider pointer to the memory provider
/// @param ptr beginning of the virtual memory range
/// @param size size of the virtual memory range
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure
/// UMF_RESULT_ERROR_INVALID_ALIGNMENT if ptr or size is not page-aligned.
/// UMF_RESULT_ERROR_NOT_SUPPORTED if operation is not supported by this provider.
///
umf_result_t (*purge_force)(void *provider, void *ptr, size_t size);

///
/// @brief Retrieve name of a given memory \p provider.
/// @param provider pointer to the memory provider
Expand All @@ -141,30 +177,9 @@ typedef struct umf_memory_provider_ops_t {
const char *(*get_name)(void *provider);

///
/// @brief Splits a coarse grain allocation into 2 adjacent allocations that
/// can be managed (freed) separately.
/// @param hProvider handle to the memory provider
/// @param ptr pointer to the beginning of the allocation
/// @param totalSize total size of the allocation to be split
/// @param firstSize size of the first new allocation, second allocation
// has a size equal to totalSize - firstSize
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure
/// @brief Optional ops
///
umf_result_t (*allocation_split)(void *hProvider, void *ptr,
size_t totalSize, size_t firstSize);

///
/// @brief Merges two coarse grain allocations into a single allocation that
/// can be managed (freed) as a whole.
/// @param hProvider handle to the memory provider
/// @param lowPtr pointer to the first allocation
/// @param highPtr pointer to the second allocation (should be > lowPtr)
/// @param totalSize size of a new merged allocation. Should be equal
/// to the sum of sizes of allocations beginning at lowPtr and highPtr
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure
///
umf_result_t (*allocation_merge)(void *hProvider, void *lowPtr,
void *highPtr, size_t totalSize);
umf_memory_provider_ext_ops_t ext;
} umf_memory_provider_ops_t;

#ifdef __cplusplus
Expand Down
6 changes: 4 additions & 2 deletions src/cpp_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ template <typename T> umf_memory_provider_ops_t providerOpsBase() {
UMF_ASSIGN_OP_NORETURN(ops, T, get_last_native_error);
UMF_ASSIGN_OP(ops, T, get_recommended_page_size, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops, T, get_min_page_size, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops, T, purge_lazy, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops, T, purge_force, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops, T, get_name, "");
UMF_ASSIGN_OP(ops.ext, T, purge_lazy, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops.ext, T, purge_force, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops.ext, T, allocation_merge, UMF_RESULT_ERROR_UNKNOWN);
UMF_ASSIGN_OP(ops.ext, T, allocation_split, UMF_RESULT_ERROR_UNKNOWN);
return ops;
}
} // namespace detail
Expand Down
90 changes: 80 additions & 10 deletions src/memory_provider.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

Expand All @@ -24,11 +25,78 @@ typedef struct umf_memory_provider_t {
void *provider_priv;
} umf_memory_provider_t;

static umf_result_t umfDefaultPurgeLazy(void *provider, void *ptr,
size_t size) {
(void)provider;
(void)ptr;
(void)size;
return UMF_RESULT_ERROR_NOT_SUPPORTED;
}

static umf_result_t umfDefaultPurgeForce(void *provider, void *ptr,
size_t size) {
(void)provider;
(void)ptr;
(void)size;
return UMF_RESULT_ERROR_NOT_SUPPORTED;
}

static umf_result_t umfDefaultAllocationSplit(void *provider, void *ptr,
size_t totalSize,
size_t firstSize) {
(void)provider;
(void)ptr;
(void)totalSize;
(void)firstSize;
return UMF_RESULT_ERROR_NOT_SUPPORTED;
}

static umf_result_t umfDefaultAllocationMerge(void *provider, void *lowPtr,
void *highPtr, size_t totalSize) {
(void)provider;
(void)lowPtr;
(void)highPtr;
(void)totalSize;
return UMF_RESULT_ERROR_NOT_SUPPORTED;
}

void assignOpsExtDefaults(umf_memory_provider_ops_t *ops) {
if (!ops->ext.purge_lazy) {
ops->ext.purge_lazy = umfDefaultPurgeLazy;
}
if (!ops->ext.purge_force) {
ops->ext.purge_force = umfDefaultPurgeForce;
}
if (!ops->ext.allocation_split) {
ops->ext.allocation_split = umfDefaultAllocationSplit;
}
if (!ops->ext.allocation_merge) {
ops->ext.allocation_merge = umfDefaultAllocationMerge;
}
}

static bool validateOps(const umf_memory_provider_ops_t *ops) {
// Mandatory ops should be non-NULL
if (!ops->alloc || !ops->free || !ops->get_recommended_page_size ||
!ops->get_min_page_size || !ops->initialize || !ops->finalize ||
!ops->get_last_native_error || !ops->get_name) {
return false;
}

// allocation_split and allocation_merge should be both set or both NULL
if ((ops->ext.allocation_split && !ops->ext.allocation_merge) ||
(!ops->ext.allocation_split && ops->ext.allocation_merge)) {
return false;
}

return true;
}

umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops,
void *params,
umf_memory_provider_handle_t *hProvider) {
libumfInit();
if (!ops || !hProvider) {
if (!ops || !hProvider || !validateOps(ops)) {
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
}

Expand All @@ -42,6 +110,8 @@ umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops,

provider->ops = *ops;

assignOpsExtDefaults(&(provider->ops));

void *provider_priv;
umf_result_t ret = ops->initialize(params, &provider_priv);
if (ret != UMF_RESULT_SUCCESS) {
Expand Down Expand Up @@ -119,11 +189,16 @@ umfMemoryProviderGetMinPageSize(umf_memory_provider_handle_t hProvider,
return res;
}

const char *umfMemoryProviderGetName(umf_memory_provider_handle_t hProvider) {
UMF_CHECK((hProvider != NULL), NULL);
return hProvider->ops.get_name(hProvider->provider_priv);
}

umf_result_t umfMemoryProviderPurgeLazy(umf_memory_provider_handle_t hProvider,
void *ptr, size_t size) {
UMF_CHECK((hProvider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
umf_result_t res =
hProvider->ops.purge_lazy(hProvider->provider_priv, ptr, size);
hProvider->ops.ext.purge_lazy(hProvider->provider_priv, ptr, size);
checkErrorAndSetLastProvider(res, hProvider);
return res;
}
Expand All @@ -132,16 +207,11 @@ umf_result_t umfMemoryProviderPurgeForce(umf_memory_provider_handle_t hProvider,
void *ptr, size_t size) {
UMF_CHECK((hProvider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
umf_result_t res =
hProvider->ops.purge_force(hProvider->provider_priv, ptr, size);
hProvider->ops.ext.purge_force(hProvider->provider_priv, ptr, size);
checkErrorAndSetLastProvider(res, hProvider);
return res;
}

const char *umfMemoryProviderGetName(umf_memory_provider_handle_t hProvider) {
UMF_CHECK((hProvider != NULL), NULL);
return hProvider->ops.get_name(hProvider->provider_priv);
}

umf_memory_provider_handle_t umfGetLastFailedMemoryProvider(void) {
return *umfGetLastFailedMemoryProviderPtr();
}
Expand All @@ -160,7 +230,7 @@ umfMemoryProviderAllocationSplit(umf_memory_provider_handle_t hProvider,
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
}

umf_result_t res = hProvider->ops.allocation_split(
umf_result_t res = hProvider->ops.ext.allocation_split(
hProvider->provider_priv, ptr, totalSize, firstSize);
checkErrorAndSetLastProvider(res, hProvider);
return res;
Expand All @@ -183,7 +253,7 @@ umfMemoryProviderAllocationMerge(umf_memory_provider_handle_t hProvider,
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
}

umf_result_t res = hProvider->ops.allocation_merge(
umf_result_t res = hProvider->ops.ext.allocation_merge(
hProvider->provider_priv, lowPtr, highPtr, totalSize);
checkErrorAndSetLastProvider(res, hProvider);
return res;
Expand Down
8 changes: 4 additions & 4 deletions src/provider/provider_level_zero.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,11 @@ static struct umf_memory_provider_ops_t UMF_LEVEL_ZERO_MEMORY_PROVIDER_OPS = {
.get_last_native_error = ze_memory_provider_get_last_native_error,
.get_recommended_page_size = ze_memory_provider_get_recommended_page_size,
.get_min_page_size = ze_memory_provider_get_min_page_size,
.purge_lazy = ze_memory_provider_purge_lazy,
.purge_force = ze_memory_provider_purge_force,
.get_name = ze_memory_provider_get_name,
.allocation_split = ze_memory_provider_allocation_split,
.allocation_merge = ze_memory_provider_allocation_merge,
.ext.purge_lazy = ze_memory_provider_purge_lazy,
.ext.purge_force = ze_memory_provider_purge_force,
.ext.allocation_merge = ze_memory_provider_allocation_merge,
.ext.allocation_split = ze_memory_provider_allocation_split,
};

umf_memory_provider_ops_t *umfLevelZeroMemoryProviderOps(void) {
Expand Down
8 changes: 4 additions & 4 deletions src/provider/provider_os_memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,11 +662,11 @@ static umf_memory_provider_ops_t UMF_OS_MEMORY_PROVIDER_OPS = {
.get_last_native_error = os_get_last_native_error,
.get_recommended_page_size = os_get_recommended_page_size,
.get_min_page_size = os_get_min_page_size,
.purge_lazy = os_purge_lazy,
.purge_force = os_purge_force,
.get_name = os_get_name,
.allocation_split = os_allocation_split,
.allocation_merge = os_allocation_merge};
.ext.purge_lazy = os_purge_lazy,
.ext.purge_force = os_purge_force,
.ext.allocation_merge = os_allocation_merge,
.ext.allocation_split = os_allocation_split};

umf_memory_provider_ops_t *umfOsMemoryProviderOps(void) {
return &UMF_OS_MEMORY_PROVIDER_OPS;
Expand Down
8 changes: 4 additions & 4 deletions src/provider/provider_tracking.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,11 @@ umf_memory_provider_ops_t UMF_TRACKING_MEMORY_PROVIDER_OPS = {
.get_last_native_error = trackingGetLastError,
.get_min_page_size = trackingGetMinPageSize,
.get_recommended_page_size = trackingGetRecommendedPageSize,
.purge_force = trackingPurgeForce,
.purge_lazy = trackingPurgeLazy,
.get_name = trackingName,
.allocation_split = trackingAllocationSplit,
.allocation_merge = trackingAllocationMerge};
.ext.purge_force = trackingPurgeForce,
.ext.purge_lazy = trackingPurgeLazy,
.ext.allocation_split = trackingAllocationSplit,
.ext.allocation_merge = trackingAllocationMerge};

umf_result_t umfTrackingMemoryProviderCreate(
umf_memory_provider_handle_t hUpstream, umf_memory_pool_handle_t hPool,
Expand Down
14 changes: 13 additions & 1 deletion test/common/provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ typedef struct provider_base_t {
[[maybe_unused]] size_t *pageSize) noexcept {
return UMF_RESULT_ERROR_UNKNOWN;
}
const char *get_name() noexcept { return "base"; }
umf_result_t purge_lazy([[maybe_unused]] void *ptr,
[[maybe_unused]] size_t size) noexcept {
return UMF_RESULT_ERROR_UNKNOWN;
Expand All @@ -60,7 +61,18 @@ typedef struct provider_base_t {
[[maybe_unused]] size_t size) noexcept {
return UMF_RESULT_ERROR_UNKNOWN;
}
const char *get_name() noexcept { return "base"; }

umf_result_t allocation_merge([[maybe_unused]] void *lowPtr,
[[maybe_unused]] void *highPtr,
[[maybe_unused]] size_t totalSize) {
return UMF_RESULT_ERROR_UNKNOWN;
}

umf_result_t allocation_split([[maybe_unused]] void *ptr,
[[maybe_unused]] size_t totalSize,
[[maybe_unused]] size_t firstSize) {
return UMF_RESULT_ERROR_UNKNOWN;
}
} provider_base_t;

umf_memory_provider_ops_t BASE_PROVIDER_OPS =
Expand Down
Loading