Repository: oneapi-src/unified-memory-framework (language: python)

Issue:
Backward compatibility for configs of pools/providers

## Rationale

UMF should provide backward compatible interfaces.

## Description

### Background

MPI team experienced the issue after the PR #692 extends `level_zero_memory_provider_params_t` config structure. The root cause of the issue relates to how MPI instantiate/initialize the L0 provider config. They did the following:
```c
level_zero_memory_provider_params_t l0_config = {
    .level_zero_context_handle = hContext;
    .level_zero_device_handle = hDevice;
    .memory_type = UMF_MEMORY_TYPE_DEVICE;
};

umf_memory_provider_handle_t hProvider = NULL;
umf_memory_provider_ops_t *l0_ops = umfLevelZeroMemoryProviderOps();
umfMemoryProviderCreate(l0_ops, &l0_config, &hProvider);
```

After PR #692 the code above start crashing because two new fields were added to the `level_zero_memory_provider_params_t` data structure and the example above does not initialize these new fields:
```c
typedef struct level_zero_memory_provider_params_t {
    ze_context_handle_t level_zero_context_handle;
    ze_device_handle_t level_zero_device_handle;
    umf_usm_memory_type_t memory_type;

    // New fields
    ze_device_handle_t *resident_device_handles;
    uint32_t resident_device_count;
} level_zero_memory_provider_params_t;
```

A quick fix for the issue was to init the config data structure with `0` and than assign the required fields:
```c
level_zero_memory_provider_params_t l0_config = { 0 }; // zero-initialize
l0_config.level_zero_context_handle = hContext;
l0_config.level_zero_device_handle = hDevice;
l0_config.memory_type = UMF_MEMORY_TYPE_DEVICE;
```

### Open Questions

The quick fix above works only because it is OK to init fields of the `level_zero_memory_provider_params_t` structure with zeroes. But there are 2 related major question we should address:

#### 1. How to initialize the configs with default values?

In general case, not every field of the config data structure could/should be initialized with zeroes.

#### 2. How to support backward compatibility?

Config data structures are defined in the headers. If application was built with old version of UMF but on the system there is a newer version than even the application initialize all config's fields properly it initializes only fields that exists in the old version of UMF.
Consider an example. There is a `provider_foo` and corresponding `foo_config_t` structure that contains an int field in the 1st version:

```c
// provider_foo.h:
typedef struct foo_params_t {
    int field 1;
} foo_config_t;

// provider_foo.c:
umf_result_t foo_provider_initialize(void *params, void **provider) {
    foo_params_t *foo_params = (foo_params_t *)params;
    // expected sizeof(foo_params_t) is equal sizeof(int) that is 4 bytes

    assert(foo_params->field1 == 0);
}

// application code:
int main() {
    foo_params_t foo_params = {0};

    umf_memory_provider_handle_t hProvider = NULL;
    umf_memory_provider_ops_t *foo_ops = umfFooMemoryProviderOps();
    umfMemoryProviderCreate(foo_ops, &foo_params, &hProvider);
}
```

Now in the 2nd version of UMF we extend the `foo_params_t` structure with additional field:

```c
// provider_foo.h:
typedef struct foo_params_t {
    int field 1;
    int field2;
} foo_config_t;

// provider_foo.c:
umf_result_t foo_provider_initialize(void *params, void **provider) {
    foo_params_t *foo_params = (foo_params_t *)params;
    // expected sizeof(foo_params_t) is equal 2*sizeof(int) that is 8 bytes

    assert(foo_params->field1 == 0);
    assert(foo_params->field2 == 0); // ERROR: if application was compiled with 1st version of the UMF lib then the size of memory pointed by `params` is 4 bytes
}
```

## Possible API Changes

### Option 1: handle-based approach
Do not expose the config structure in interfaces. The config object is allocated inside libumf.so and handle to the config is returned to the client code. Setter/getter APIs are used to setup config parameters. For example:

```c
foo_params_handle_t foo_params = umfCreateFooParams();
umfFooParamsSetField1(foo_params, 7);

umf_memory_provider_handle_t hProvider = NULL;
umf_memory_provider_ops_t *foo_ops = umfFooMemoryProviderOps();
umfMemoryProviderCreate(foo_ops, foo_params, &hProvider);
```

Client code does not depend on the layout of the `foo_params_t` structure only UMF knows it and can change in different versions.

### Option 2: explicitly pass the size of params 

Data structures that define configs for the pools/providers remains in headers (the same as today). The new fields can be added only to the end. Application explicitly passes the size of the config data structure. Provider/pool implementation determine the version of the config based on the size. To init the config data structure we need to introduce a special macros or header-based inline functions. For example:

```c
// Version 1 provider_foo.h:
typedef struct foo_params_t {
    int field 1;
} foo_config_t;

inline void umfFooParamsInit(foo_params_t *params) {
     params->field1 = 0;
}

// Version 2 provider_foo.h:
typedef struct foo_params_t {
    int field 1;
    int field2;
} foo_config_t;

inline void umfFooParamsInit(foo_params_t *params) {
     params->field1 = 0;
     params->field2 = 0;
}

// Version 2 provider_foo.c:
umf_result_t foo_provider_initialize(void *params, size_t params_size, void **provider) {
    foo_params_t foo_params;
    umfFooParamsInit(& foo_params);

    if(sizeof(foo_params_t) == params_size) {
         // current version, just copy input params as is.
        foo_params = *(foo_params_t*)params;
    } else {
        // old version, copy only first `params_size` bytes and keep the rest default initialized
        memcpy(& foo_params, params, params_size);
    }

    assert(foo_params->field1 == 0);
    assert(foo_params->field2 == 0);
}

// application code:
int main() {
    foo_params_t foo_params;
    umfFooParamsInit(&params);

    umf_memory_provider_handle_t hProvider = NULL;
    umf_memory_provider_ops_t *foo_ops = umfFooMemoryProviderOps();
    umfMemoryProviderCreate(foo_ops, &foo_params, sizeof(foo_params_t), &hProvider);
}
```

### Option 3: store version in the params data structure

Similar to Option 2, but instead of using the size of params data structure to determine the version store it explicitly as a first field in the params data structure.

```c
// Version 1 provider_foo.h:
typedef struct foo_params_t {
    int version;
    int field 1;
} foo_config_t;

inline void umfFooParamsInit(foo_params_t *params) {
    version = 1;
    params->field1 = 0;
}

// Version 2 provider_foo.h:
typedef struct foo_params_t {
    int field 1;
    int field2;
} foo_config_t;

inline void umfFooParamsInit(foo_params_t *params) {
    version = 2;
    params->field1 = 0;
    params->field2 = 0;
}

// Version 2 provider_foo.c:
umf_result_t foo_provider_initialize(void *params, size_t params_size, void **provider) {
    foo_params_t foo_params;
    umfFooParamsInit(& foo_params);

    int params_version = *(int *)params;

    switch(params_version) {
        case 1:
            ...
            break;
        case 2:
            ...
            break;
        default:
            LOG_ERR("Wrong version");
    }

    assert(foo_params->field1 == 0);
    assert(foo_params->field2 == 0);
}

// application code:
int main() {
    foo_params_t foo_params;
    umfFooParamsInit(&params);

    umf_memory_provider_handle_t hProvider = NULL;
    umf_memory_provider_ops_t *foo_ops = umfFooMemoryProviderOps();
    umfMemoryProviderCreate(foo_ops, &foo_params, &hProvider);
}
```

### Design Considerations

I prefer Option 1 over Option 2 and Option 3. I think it provides best ABI compatibility. Another advantage is that the config data structure always initialized when user calls `umfCreateFooParams()`. There is no way to get non-initialized fields in the config, while in case of Option 2 or 3 user might forget to call `umfFooParamsInit(&params)` function.

Between Options 2 and 3, I prefer Option 3 because of two things:

- it stores version explicitly.
- the `umfMemoryProviderCreate` API remains unchanged.

## Implementation details

TBD




Discussion / hints:
We must address this issue before the first stable release because all three options require non-backward compatible changes.
:+1: to option 1.

Another option is to do what UR does and create a "chain" (linked list) of parameters.
```
struct foo_param {
  param_type type;
  int foo;
  void *next;
}

struct bar_param {
  param_type type;
  int bar;
  void *next;
}

struct foo_param foo = { PARAM_FOO, 5, nullptr };
struct bar_param bar = { PARAM_BAR, 10, &foo };

someApi(&bar); // this would apply all options in the chain.

```

This is super verbose, but really flexible. One downside is that it's on the api function to validate the parameters, and users need to remember to correctly set param type.

I also vote for option 1. What UR does is also OK but from my experience it's quite easy to forget setting the type (and we even had a bug in UMF where we set the type incorrectly).

One question: how's responsible for destroying the params - user or UMF?
> One question: how's responsible for destroying the params - user or UMF?

In the case of option 1, User calls `umfCreateFooParams()` function which internally allocates required data structure. After pool/provider is created user should call the appropriate `umfDestroyFooParams` function to tell UMF library that params data structure should be destroyed. 
Option 1 > Option 3 >>>> Option 2

~The same issue we have with other structures in public header. Also we have the same issue with other structures, like memprovider ops - in case of those structures probably option 3 (version field) is the way.~

Edit. I checked code and i found that we already have version in ops structure so it is fine. 

Fix patch:
diff --git a/examples/cuda_shared_memory/cuda_shared_memory.c b/examples/cuda_shared_memory/cuda_shared_memory.c
index 22e5e3f10..50c8f9240 100644
--- a/examples/cuda_shared_memory/cuda_shared_memory.c
+++ b/examples/cuda_shared_memory/cuda_shared_memory.c
@@ -43,24 +43,51 @@ int main(void) {
     // Create a context on the device
     cuCtxCreate(&cuContext, 0, cuDevice);
 
-    // Setup parameters for the CUDA memory provider. It will be used for
+    // Setup parameters for the CUDA Memory Provider. It will be used for
     // allocating memory from CUDA devices.
-    cuda_memory_provider_params_t cu_memory_provider_params;
-    cu_memory_provider_params.cuda_context_handle = cuContext;
-    cu_memory_provider_params.cuda_device_handle = cuDevice;
+    umf_cuda_memory_provider_params_handle_t cu_memory_provider_params = NULL;
+    res = umfCUDAMemoryProviderParamsCreate(&cu_memory_provider_params);
+    if (res != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to create memory provider params!\n");
+        ret = -1;
+        goto cuda_destroy;
+    }
+
+    res = umfCUDAMemoryProviderParamsSetContext(cu_memory_provider_params,
+                                                cuContext);
+    if (res != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to set context in memory provider params!\n");
+        ret = -1;
+        goto provider_params_destroy;
+    }
+
+    res = umfCUDAMemoryProviderParamsSetDevice(cu_memory_provider_params,
+                                               cuDevice);
+    if (res != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to set device in memory provider params!\n");
+        ret = -1;
+        goto provider_params_destroy;
+    }
     // Set the memory type to shared to allow the memory to be accessed on both
     // CPU and GPU.
-    cu_memory_provider_params.memory_type = UMF_MEMORY_TYPE_SHARED;
+    res = umfCUDAMemoryProviderParamsSetMemoryType(cu_memory_provider_params,
+                                                   UMF_MEMORY_TYPE_SHARED);
+    if (res != UMF_RESULT_SUCCESS) {
+        fprintf(stderr,
+                "Failed to set memory type in memory provider params!\n");
+        ret = -1;
+        goto provider_params_destroy;
+    }
 
     // Create CUDA memory provider
     umf_memory_provider_handle_t cu_memory_provider;
-    res = umfMemoryProviderCreate(umfCUDAMemoryProviderOps(),
-                                  &cu_memory_provider_params,
-                                  &cu_memory_provider);
+    res =
+        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(),
+                                cu_memory_provider_params, &cu_memory_provider);
     if (res != UMF_RESULT_SUCCESS) {
         fprintf(stderr, "Failed to create a memory provider!\n");
         ret = -1;
-        goto cuda_destroy;
+        goto provider_params_destroy;
     }
 
     printf("CUDA memory provider created at %p\n", (void *)cu_memory_provider);
@@ -147,6 +174,9 @@ int main(void) {
 memory_provider_destroy:
     umfMemoryProviderDestroy(cu_memory_provider);
 
+provider_params_destroy:
+    umfCUDAMemoryProviderParamsDestroy(cu_memory_provider_params);
+
 cuda_destroy:
     ret = cuCtxDestroy(cuContext);
     return ret;
diff --git a/include/umf/providers/provider_cuda.h b/include/umf/providers/provider_cuda.h
index 2f6a07d81..5f1d5a6e2 100644
--- a/include/umf/providers/provider_cuda.h
+++ b/include/umf/providers/provider_cuda.h
@@ -14,12 +14,44 @@
 extern "C" {
 #endif
 
-/// @brief CUDA Memory Provider settings struct
-typedef struct cuda_memory_provider_params_t {
-    void *cuda_context_handle;         ///< Handle to the CUDA context
-    int cuda_device_handle;            ///< Handle to the CUDA device
-    umf_usm_memory_type_t memory_type; ///< Allocation memory type
-} cuda_memory_provider_params_t;
+struct umf_cuda_memory_provider_params_t;
+
+typedef struct umf_cuda_memory_provider_params_t
+    *umf_cuda_memory_provider_params_handle_t;
+
+/// @brief  Create a struct to store parameters of the CUDA Memory Provider.
+/// @param  hParams [out] handle to the newly created parameters struct.
+/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
+umf_result_t umfCUDAMemoryProviderParamsCreate(
+    umf_cuda_memory_provider_params_handle_t *hParams);
+
+/// @brief  Destroy parameters struct.
+/// @param  hParams handle to the parameters of the CUDA Memory Provider.
+/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
+umf_result_t umfCUDAMemoryProviderParamsDestroy(
+    umf_cuda_memory_provider_params_handle_t hParams);
+
+/// @brief  Set the CUDA context handle in the parameters struct.
+/// @param  hParams handle to the parameters of the CUDA Memory Provider.
+/// @param  hContext handle to the CUDA context.
+/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
+umf_result_t umfCUDAMemoryProviderParamsSetContext(
+    umf_cuda_memory_provider_params_handle_t hParams, void *hContext);
+
+/// @brief  Set the CUDA device handle in the parameters struct.
+/// @param  hParams handle to the parameters of the CUDA Memory Provider.
+/// @param  hDevice handle to the CUDA device.
+/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
+umf_result_t umfCUDAMemoryProviderParamsSetDevice(
+    umf_cuda_memory_provider_params_handle_t hParams, int hDevice);
+
+/// @brief  Set the memory type in the parameters struct.
+/// @param  hParams handle to the parameters of the CUDA Memory Provider.
+/// @param  memoryType memory type.
+/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
+umf_result_t umfCUDAMemoryProviderParamsSetMemoryType(
+    umf_cuda_memory_provider_params_handle_t hParams,
+    umf_usm_memory_type_t memoryType);
 
 umf_memory_provider_ops_t *umfCUDAMemoryProviderOps(void);
 
diff --git a/src/libumf.def b/src/libumf.def
index 56e26050c..8f29f4579 100644
--- a/src/libumf.def
+++ b/src/libumf.def
@@ -17,6 +17,11 @@ EXPORTS
     umfCoarseMemoryProviderGetStats
     umfCoarseMemoryProviderOps
     umfCUDAMemoryProviderOps
+    umfCUDAMemoryProviderParamsCreate
+    umfCUDAMemoryProviderParamsDestroy
+    umfCUDAMemoryProviderParamsSetContext
+    umfCUDAMemoryProviderParamsSetDevice
+    umfCUDAMemoryProviderParamsSetMemoryType
     umfDevDaxMemoryProviderOps
     umfFree
     umfFileMemoryProviderOps
diff --git a/src/libumf.map b/src/libumf.map
index 19235705c..f70d247b5 100644
--- a/src/libumf.map
+++ b/src/libumf.map
@@ -11,6 +11,11 @@ UMF_1.0 {
         umfCoarseMemoryProviderGetStats;
         umfCoarseMemoryProviderOps;
         umfCUDAMemoryProviderOps;
+        umfCUDAMemoryProviderParamsCreate;
+        umfCUDAMemoryProviderParamsDestroy;
+        umfCUDAMemoryProviderParamsSetContext;
+        umfCUDAMemoryProviderParamsSetDevice;
+        umfCUDAMemoryProviderParamsSetMemoryType;
         umfDevDaxMemoryProviderOps;
         umfFree;
         umfFileMemoryProviderOps;
diff --git a/src/provider/provider_cuda.c b/src/provider/provider_cuda.c
index 5a686d857..3c4e39451 100644
--- a/src/provider/provider_cuda.c
+++ b/src/provider/provider_cuda.c
@@ -14,6 +14,40 @@
 
 #if defined(UMF_NO_CUDA_PROVIDER)
 
+umf_result_t umfCUDAMemoryProviderParamsCreate(
+    umf_cuda_memory_provider_params_handle_t *hParams) {
+    (void)hParams;
+    return UMF_RESULT_ERROR_NOT_SUPPORTED;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsDestroy(
+    umf_cuda_memory_provider_params_handle_t hParams) {
+    (void)hParams;
+    return UMF_RESULT_ERROR_NOT_SUPPORTED;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsSetContext(
+    umf_cuda_memory_provider_params_handle_t hParams, void *hContext) {
+    (void)hParams;
+    (void)hContext;
+    return UMF_RESULT_ERROR_NOT_SUPPORTED;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsSetDevice(
+    umf_cuda_memory_provider_params_handle_t hParams, int hDevice) {
+    (void)hParams;
+    (void)hDevice;
+    return UMF_RESULT_ERROR_NOT_SUPPORTED;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsSetMemoryType(
+    umf_cuda_memory_provider_params_handle_t hParams,
+    umf_usm_memory_type_t memoryType) {
+    (void)hParams;
+    (void)memoryType;
+    return UMF_RESULT_ERROR_NOT_SUPPORTED;
+}
+
 umf_memory_provider_ops_t *umfCUDAMemoryProviderOps(void) {
     // not supported
     return NULL;
@@ -48,6 +82,13 @@ typedef struct cu_memory_provider_t {
     size_t min_alignment;
 } cu_memory_provider_t;
 
+// CUDA Memory Provider settings struct
+typedef struct umf_cuda_memory_provider_params_t {
+    void *cuda_context_handle;         ///< Handle to the CUDA context
+    int cuda_device_handle;            ///< Handle to the CUDA device
+    umf_usm_memory_type_t memory_type; ///< Allocation memory type
+} umf_cuda_memory_provider_params_t;
+
 typedef struct cu_ops_t {
     CUresult (*cuMemGetAllocationGranularity)(
         size_t *granularity, const CUmemAllocationProp *prop,
@@ -158,14 +199,81 @@ static void init_cu_global_state(void) {
     }
 }
 
+umf_result_t umfCUDAMemoryProviderParamsCreate(
+    umf_cuda_memory_provider_params_handle_t *hParams) {
+    if (!hParams) {
+        LOG_ERR("CUDA Memory Provider params handle is NULL");
+        return UMF_RESULT_ERROR_INVALID_ARGUMENT;
+    }
+
+    umf_cuda_memory_provider_params_handle_t params_data =
+        umf_ba_global_alloc(sizeof(umf_cuda_memory_provider_params_t));
+    if (!params_data) {
+        LOG_ERR("Cannot allocate memory for CUDA Memory Provider params");
+        return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
+    }
+
+    params_data->cuda_context_handle = NULL;
+    params_data->cuda_device_handle = -1;
+    params_data->memory_type = UMF_MEMORY_TYPE_UNKNOWN;
+
+    *hParams = params_data;
+
+    return UMF_RESULT_SUCCESS;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsDestroy(
+    umf_cuda_memory_provider_params_handle_t hParams) {
+    umf_ba_global_free(hParams);
+
+    return UMF_RESULT_SUCCESS;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsSetContext(
+    umf_cuda_memory_provider_params_handle_t hParams, void *hContext) {
+    if (!hParams) {
+        LOG_ERR("CUDA Memory Provider params handle is NULL");
+        return UMF_RESULT_ERROR_INVALID_ARGUMENT;
+    }
+
+    hParams->cuda_context_handle = hContext;
+
+    return UMF_RESULT_SUCCESS;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsSetDevice(
+    umf_cuda_memory_provider_params_handle_t hParams, int hDevice) {
+    if (!hParams) {
+        LOG_ERR("CUDA Memory Provider params handle is NULL");
+        return UMF_RESULT_ERROR_INVALID_ARGUMENT;
+    }
+
+    hParams->cuda_device_handle = hDevice;
+
+    return UMF_RESULT_SUCCESS;
+}
+
+umf_result_t umfCUDAMemoryProviderParamsSetMemoryType(
+    umf_cuda_memory_provider_params_handle_t hParams,
+    umf_usm_memory_type_t memoryType) {
+    if (!hParams) {
+        LOG_ERR("CUDA Memory Provider params handle is NULL");
+        return UMF_RESULT_ERROR_INVALID_ARGUMENT;
+    }
+
+    hParams->memory_type = memoryType;
+
+    return UMF_RESULT_SUCCESS;
+}
+
 static umf_result_t cu_memory_provider_initialize(void *params,
                                                   void **provider) {
     if (params == NULL) {
         return UMF_RESULT_ERROR_INVALID_ARGUMENT;
     }
 
-    cuda_memory_provider_params_t *cu_params =
-        (cuda_memory_provider_params_t *)params;
+    umf_cuda_memory_provider_params_handle_t cu_params =
+        (umf_cuda_memory_provider_params_handle_t)params;
 
     if (cu_params->memory_type == UMF_MEMORY_TYPE_UNKNOWN ||
         cu_params->memory_type > UMF_MEMORY_TYPE_SHARED) {


Test patch:
diff --git a/test/providers/cuda_helpers.cpp b/test/providers/cuda_helpers.cpp
index 37e71bd6a..9c41d9382 100644
--- a/test/providers/cuda_helpers.cpp
+++ b/test/providers/cuda_helpers.cpp
@@ -39,9 +39,63 @@ struct libcu_ops {
 } libcu_ops;
 
 #if USE_DLOPEN
+// Generic no-op stub function for all callbacks
+template <typename... Args> CUresult noop_stub(Args &&...) {
+    return CUDA_SUCCESS; // Always return CUDA_SUCCESS
+}
+
 struct DlHandleCloser {
     void operator()(void *dlHandle) {
         if (dlHandle) {
+            libcu_ops.cuInit = [](auto... args) { return noop_stub(args...); };
+            libcu_ops.cuCtxCreate = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuCtxDestroy = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuCtxGetCurrent = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuCtxSetCurrent = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuDeviceGet = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemAlloc = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemFree = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemAllocHost = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemAllocManaged = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemFreeHost = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemsetD32 = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuMemcpy = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuPointerGetAttribute = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuPointerGetAttributes = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuStreamSynchronize = [](auto... args) {
+                return noop_stub(args...);
+            };
+            libcu_ops.cuCtxSynchronize = [](auto... args) {
+                return noop_stub(args...);
+            };
             utils_close_library(dlHandle);
         }
     }
@@ -355,38 +409,40 @@ int init_cuda() {
     return InitResult;
 }
 
-cuda_memory_provider_params_t
-create_cuda_prov_params(umf_usm_memory_type_t memory_type) {
-    cuda_memory_provider_params_t params = {NULL, 0, UMF_MEMORY_TYPE_UNKNOWN};
-    int ret = -1;
+int get_cuda_device(CUdevice *device) {
+    CUdevice cuDevice = -1;
 
-    ret = init_cuda();
+    int ret = init_cuda();
     if (ret != 0) {
-        // Return empty params. Test will be skipped.
-        return params;
+        fprintf(stderr, "init_cuda() failed!\n");
+        return ret;
     }
 
-    // Get the first CUDA device
-    CUdevice cuDevice = -1;
     CUresult res = libcu_ops.cuDeviceGet(&cuDevice, 0);
     if (res != CUDA_SUCCESS || cuDevice < 0) {
-        // Return empty params. Test will be skipped.
-        return params;
+        return -1;
     }
 
-    // Create a CUDA context
+    *device = cuDevice;
+    return 0;
+}
+
+int create_context(CUdevice device, CUcontext *context) {
     CUcontext cuContext = nullptr;
-    res = libcu_ops.cuCtxCreate(&cuContext, 0, cuDevice);
-    if (res != CUDA_SUCCESS || cuContext == nullptr) {
-        // Return empty params. Test will be skipped.
-        return params;
+
+    int ret = init_cuda();
+    if (ret != 0) {
+        fprintf(stderr, "init_cuda() failed!\n");
+        return ret;
     }
 
-    params.cuda_context_handle = cuContext;
-    params.cuda_device_handle = cuDevice;
-    params.memory_type = memory_type;
+    CUresult res = libcu_ops.cuCtxCreate(&cuContext, 0, device);
+    if (res != CUDA_SUCCESS || cuContext == nullptr) {
+        return -1;
+    }
 
-    return params;
+    *context = cuContext;
+    return 0;
 }
 
 int destroy_context(CUcontext context) {
diff --git a/test/providers/cuda_helpers.h b/test/providers/cuda_helpers.h
index fc349fc14..fc06c1fcf 100644
--- a/test/providers/cuda_helpers.h
+++ b/test/providers/cuda_helpers.h
@@ -26,6 +26,10 @@
 extern "C" {
 #endif
 
+int get_cuda_device(CUdevice *device);
+
+int create_context(CUdevice device, CUcontext *context);
+
 int destroy_context(CUcontext context);
 
 int cuda_fill(CUcontext context, CUdevice device, void *ptr, size_t size,
@@ -40,9 +44,6 @@ CUcontext get_mem_context(void *ptr);
 
 CUcontext get_current_context();
 
-cuda_memory_provider_params_t
-create_cuda_prov_params(umf_usm_memory_type_t memory_type);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/test/providers/ipc_cuda_prov_common.c b/test/providers/ipc_cuda_prov_common.c
index ac00bb01b..a38e4d061 100644
--- a/test/providers/ipc_cuda_prov_common.c
+++ b/test/providers/ipc_cuda_prov_common.c
@@ -13,10 +13,9 @@
 #include "ipc_cuda_prov_common.h"
 
 void memcopy(void *dst, const void *src, size_t size, void *context) {
-    cuda_memory_provider_params_t *cu_params =
-        (cuda_memory_provider_params_t *)context;
-    int ret = cuda_copy(cu_params->cuda_context_handle,
-                        cu_params->cuda_device_handle, dst, (void *)src, size);
+    cuda_copy_ctx_t *cu_params = (cuda_copy_ctx_t *)context;
+    int ret = cuda_copy(cu_params->context, cu_params->device, dst, (void *)src,
+                        size);
     if (ret != 0) {
         fprintf(stderr, "cuda_copy failed with error %d\n", ret);
     }
diff --git a/test/providers/ipc_cuda_prov_common.h b/test/providers/ipc_cuda_prov_common.h
index cecdc2bb8..e50546efd 100644
--- a/test/providers/ipc_cuda_prov_common.h
+++ b/test/providers/ipc_cuda_prov_common.h
@@ -10,6 +10,11 @@
 
 #include <stddef.h>
 
+typedef struct cuda_copy_ctx_t {
+    CUcontext context;
+    CUdevice device;
+} cuda_copy_ctx_t;
+
 void memcopy(void *dst, const void *src, size_t size, void *context);
 
 #endif // UMF_TEST_IPC_CUDA_PROV_COMMON_H
diff --git a/test/providers/ipc_cuda_prov_consumer.c b/test/providers/ipc_cuda_prov_consumer.c
index 3286cee28..1aeb5b15c 100644
--- a/test/providers/ipc_cuda_prov_consumer.c
+++ b/test/providers/ipc_cuda_prov_consumer.c
@@ -22,23 +22,76 @@ int main(int argc, char *argv[]) {
     }
 
     int port = atoi(argv[1]);
+    CUdevice hDevice = -1;
+    CUcontext hContext = NULL;
 
-    cuda_memory_provider_params_t cu_params =
-        create_cuda_prov_params(UMF_MEMORY_TYPE_DEVICE);
+    int ret = get_cuda_device(&hDevice);
+    if (ret != 0) {
+        fprintf(stderr, "get_cuda_device() failed!\n");
+        return -1;
+    }
+
+    ret = create_context(hDevice, &hContext);
+    if (ret != 0) {
+        fprintf(stderr, "create_context() failed!\n");
+        return -1;
+    }
+
+    umf_cuda_memory_provider_params_handle_t cu_params = NULL;
+    umf_result_t umf_result = umfCUDAMemoryProviderParamsCreate(&cu_params);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to create CUDA params!\n");
+        ret = -1;
+        goto destroy_context;
+    }
+
+    umf_result = umfCUDAMemoryProviderParamsSetContext(cu_params, hContext);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr,
+                "Failed to set context in CUDA Memory Provider params!\n");
+        ret = -1;
+        goto destroy_provider_params;
+    }
+
+    umf_result = umfCUDAMemoryProviderParamsSetDevice(cu_params, hDevice);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr,
+                "Failed to set device in CUDA Memory Provider params!\n");
+        ret = -1;
+        goto destroy_provider_params;
+    }
+
+    umf_result = umfCUDAMemoryProviderParamsSetMemoryType(
+        cu_params, UMF_MEMORY_TYPE_DEVICE);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to set memory type in CUDA memory "
+                        "provider params!\n");
+        ret = -1;
+        goto destroy_provider_params;
+    }
 
     umf_disjoint_pool_params_handle_t pool_params = NULL;
 
-    umf_result_t umf_result = umfDisjointPoolParamsCreate(&pool_params);
+    umf_result = umfDisjointPoolParamsCreate(&pool_params);
     if (umf_result != UMF_RESULT_SUCCESS) {
         fprintf(stderr, "Failed to create pool params!\n");
-        return -1;
+        ret = -1;
+        goto destroy_provider_params;
     }
 
-    int ret = run_consumer(port, umfDisjointPoolOps(), pool_params,
-                           umfCUDAMemoryProviderOps(), &cu_params, memcopy,
-                           &cu_params);
+    cuda_copy_ctx_t copy_ctx = {hContext, hDevice};
+
+    ret =
+        run_consumer(port, umfDisjointPoolOps(), pool_params,
+                     umfCUDAMemoryProviderOps(), cu_params, memcopy, &copy_ctx);
 
     umfDisjointPoolParamsDestroy(pool_params);
 
+destroy_provider_params:
+    umfCUDAMemoryProviderParamsDestroy(cu_params);
+
+destroy_context:
+    destroy_context(hContext);
+
     return ret;
 }
diff --git a/test/providers/ipc_cuda_prov_producer.c b/test/providers/ipc_cuda_prov_producer.c
index d11004d6d..c2cd1d132 100644
--- a/test/providers/ipc_cuda_prov_producer.c
+++ b/test/providers/ipc_cuda_prov_producer.c
@@ -22,23 +22,76 @@ int main(int argc, char *argv[]) {
     }
 
     int port = atoi(argv[1]);
+    CUdevice hDevice = -1;
+    CUcontext hContext = NULL;
 
-    cuda_memory_provider_params_t cu_params =
-        create_cuda_prov_params(UMF_MEMORY_TYPE_DEVICE);
+    int ret = get_cuda_device(&hDevice);
+    if (ret != 0) {
+        fprintf(stderr, "get_cuda_device() failed!\n");
+        return -1;
+    }
+
+    ret = create_context(hDevice, &hContext);
+    if (ret != 0) {
+        fprintf(stderr, "create_context() failed!\n");
+        return -1;
+    }
+
+    umf_cuda_memory_provider_params_handle_t cu_params = NULL;
+    umf_result_t umf_result = umfCUDAMemoryProviderParamsCreate(&cu_params);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to create CUDA params!\n");
+        ret = -1;
+        goto destroy_context;
+    }
+
+    umf_result = umfCUDAMemoryProviderParamsSetContext(cu_params, hContext);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr,
+                "Failed to set context in CUDA Memory Provider params!\n");
+        ret = -1;
+        goto destroy_provider_params;
+    }
+
+    umf_result = umfCUDAMemoryProviderParamsSetDevice(cu_params, hDevice);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr,
+                "Failed to set device in CUDA Memory Provider params!\n");
+        ret = -1;
+        goto destroy_provider_params;
+    }
+
+    umf_result = umfCUDAMemoryProviderParamsSetMemoryType(
+        cu_params, UMF_MEMORY_TYPE_DEVICE);
+    if (umf_result != UMF_RESULT_SUCCESS) {
+        fprintf(stderr, "Failed to set memory type in CUDA memory "
+                        "provider params!\n");
+        ret = -1;
+        goto destroy_provider_params;
+    }
 
     umf_disjoint_pool_params_handle_t pool_params = NULL;
 
-    umf_result_t umf_result = umfDisjointPoolParamsCreate(&pool_params);
+    umf_result = umfDisjointPoolParamsCreate(&pool_params);
     if (umf_result != UMF_RESULT_SUCCESS) {
         fprintf(stderr, "Failed to create pool params!\n");
-        return -1;
+        ret = -1;
+        goto destroy_provider_params;
     }
 
-    int ret = run_producer(port, umfDisjointPoolOps(), pool_params,
-                           umfCUDAMemoryProviderOps(), &cu_params, memcopy,
-                           &cu_params);
+    cuda_copy_ctx_t copy_ctx = {hContext, hDevice};
+
+    ret =
+        run_producer(port, umfDisjointPoolOps(), pool_params,
+                     umfCUDAMemoryProviderOps(), cu_params, memcopy, &copy_ctx);
 
     umfDisjointPoolParamsDestroy(pool_params);
 
+destroy_provider_params:
+    umfCUDAMemoryProviderParamsDestroy(cu_params);
+
+destroy_context:
+    destroy_context(hContext);
+
     return ret;
 }
diff --git a/test/providers/provider_cuda.cpp b/test/providers/provider_cuda.cpp
index 58e3beb9e..4f1d35911 100644
--- a/test/providers/provider_cuda.cpp
+++ b/test/providers/provider_cuda.cpp
@@ -19,6 +19,82 @@
 using umf_test::test;
 using namespace umf_test;
 
+class CUDATestHelper {
+  public:
+    CUDATestHelper();
+
+    ~CUDATestHelper() {
+        if (hContext_) {
+            destroy_context(hContext_);
+        }
+    }
+
+    CUcontext get_test_context() const { return hContext_; }
+
+    CUdevice get_test_device() const { return hDevice_; }
+
+  private:
+    CUcontext hContext_ = nullptr;
+    CUdevice hDevice_ = -1;
+};
+
+CUDATestHelper::CUDATestHelper() {
+    int ret = get_cuda_device(&hDevice_);
+    if (ret != 0) {
+        fprintf(stderr, "get_cuda_device() failed!\n");
+        return;
+    }
+
+    ret = create_context(hDevice_, &hContext_);
+    if (ret != 0) {
+        fprintf(stderr, "create_context() failed!\n");
+        return;
+    }
+}
+
+using cuda_params_unique_handle_t =
+    std::unique_ptr<umf_cuda_memory_provider_params_t,
+                    decltype(&umfCUDAMemoryProviderParamsDestroy)>;
+
+cuda_params_unique_handle_t
+create_cuda_prov_params(CUcontext context, CUdevice device,
+                        umf_usm_memory_type_t memory_type) {
+    umf_cuda_memory_provider_params_handle_t params = nullptr;
+
+    umf_result_t res = umfCUDAMemoryProviderParamsCreate(&params);
+    if (res != UMF_RESULT_SUCCESS) {
+        return cuda_params_unique_handle_t(nullptr,
+                                           &umfCUDAMemoryProviderParamsDestroy);
+    }
+
+    res = umfCUDAMemoryProviderParamsSetContext(params, context);
+    if (res != UMF_RESULT_SUCCESS) {
+        umfCUDAMemoryProviderParamsDestroy(params);
+        return cuda_params_unique_handle_t(nullptr,
+                                           &umfCUDAMemoryProviderParamsDestroy);
+        ;
+    }
+
+    res = umfCUDAMemoryProviderParamsSetDevice(params, device);
+    if (res != UMF_RESULT_SUCCESS) {
+        umfCUDAMemoryProviderParamsDestroy(params);
+        return cuda_params_unique_handle_t(nullptr,
+                                           &umfCUDAMemoryProviderParamsDestroy);
+        ;
+    }
+
+    res = umfCUDAMemoryProviderParamsSetMemoryType(params, memory_type);
+    if (res != UMF_RESULT_SUCCESS) {
+        umfCUDAMemoryProviderParamsDestroy(params);
+        return cuda_params_unique_handle_t(nullptr,
+                                           &umfCUDAMemoryProviderParamsDestroy);
+        ;
+    }
+
+    return cuda_params_unique_handle_t(params,
+                                       &umfCUDAMemoryProviderParamsDestroy);
+}
+
 class CUDAMemoryAccessor : public MemoryAccessor {
   public:
     CUDAMemoryAccessor(CUcontext hContext, CUdevice hDevice)
@@ -51,7 +127,8 @@ class CUDAMemoryAccessor : public MemoryAccessor {
 };
 
 using CUDAProviderTestParams =
-    std::tuple<cuda_memory_provider_params_t, MemoryAccessor *>;
+    std::tuple<umf_cuda_memory_provider_params_handle_t, CUcontext,
+               umf_usm_memory_type_t, MemoryAccessor *>;
 
 struct umfCUDAProviderTest
     : umf_test::test,
@@ -60,15 +137,20 @@ struct umfCUDAProviderTest
     void SetUp() override {
         test::SetUp();
 
-        auto [cuda_params, accessor] = this->GetParam();
+        auto [cuda_params, cu_context, memory_type, accessor] =
+            this->GetParam();
         params = cuda_params;
         memAccessor = accessor;
+        expected_context = cu_context;
+        expected_memory_type = memory_type;
     }
 
     void TearDown() override { test::TearDown(); }
 
-    cuda_memory_provider_params_t params;
+    umf_cuda_memory_provider_params_handle_t params;
     MemoryAccessor *memAccessor = nullptr;
+    CUcontext expected_context;
+    umf_usm_memory_type_t expected_memory_type;
 };
 
 TEST_P(umfCUDAProviderTest, basic) {
@@ -79,7 +161,7 @@ TEST_P(umfCUDAProviderTest, basic) {
     // create CUDA provider
     umf_memory_provider_handle_t provider = nullptr;
     umf_result_t umf_result =
-        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), &params, &provider);
+        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider);
     ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
     ASSERT_NE(provider, nullptr);
 
@@ -102,14 +184,14 @@ TEST_P(umfCUDAProviderTest, basic) {
     memAccessor->fill(ptr, size, &pattern, sizeof(pattern));
 
     CUcontext actual_mem_context = get_mem_context(ptr);
-    ASSERT_EQ(actual_mem_context, (CUcontext)params.cuda_context_handle);
+    ASSERT_EQ(actual_mem_context, expected_context);
 
     CUcontext actual_current_context = get_current_context();
     ASSERT_EQ(actual_current_context, expected_current_context);
 
     umf_usm_memory_type_t memoryTypeActual =
-        get_mem_type((CUcontext)params.cuda_context_handle, ptr);
-    ASSERT_EQ(memoryTypeActual, params.memory_type);
+        get_mem_type(actual_current_context, ptr);
+    ASSERT_EQ(memoryTypeActual, expected_memory_type);
 
     // check if the pattern was successfully applied
     uint32_t *hostMemory = (uint32_t *)calloc(1, size);
@@ -128,7 +210,7 @@ TEST_P(umfCUDAProviderTest, basic) {
 TEST_P(umfCUDAProviderTest, getPageSize) {
     umf_memory_provider_handle_t provider = nullptr;
     umf_result_t umf_result =
-        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), &params, &provider);
+        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider);
     ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
     ASSERT_NE(provider, nullptr);
 
@@ -152,7 +234,7 @@ TEST_P(umfCUDAProviderTest, getPageSize) {
 TEST_P(umfCUDAProviderTest, getName) {
     umf_memory_provider_handle_t provider = nullptr;
     umf_result_t umf_result =
-        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), &params, &provider);
+        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider);
     ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
     ASSERT_NE(provider, nullptr);
 
@@ -167,14 +249,14 @@ TEST_P(umfCUDAProviderTest, allocInvalidSize) {
     // create CUDA provider
     umf_memory_provider_handle_t provider = nullptr;
     umf_result_t umf_result =
-        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), &params, &provider);
+        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider);
     ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
     ASSERT_NE(provider, nullptr);
 
     void *ptr = nullptr;
 
     // NOTE: some scenarios are invalid only for the DEVICE allocations
-    if (params.memory_type == UMF_MEMORY_TYPE_DEVICE) {
+    if (expected_memory_type == UMF_MEMORY_TYPE_DEVICE) {
         // try to alloc SIZE_MAX
         umf_result = umfMemoryProviderAlloc(provider, SIZE_MAX, 0, &ptr);
         ASSERT_EQ(ptr, nullptr);
@@ -198,14 +280,14 @@ TEST_P(umfCUDAProviderTest, providerCreateInvalidArgs) {
         umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), nullptr, &provider);
     ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT);
 
-    umf_result = umfMemoryProviderCreate(nullptr, &params, nullptr);
+    umf_result = umfMemoryProviderCreate(nullptr, params, nullptr);
     ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT);
 }
 
 TEST_P(umfCUDAProviderTest, getPageSizeInvalidArgs) {
     umf_memory_provider_handle_t provider = nullptr;
     umf_result_t umf_result =
-        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), &params, &provider);
+        umfMemoryProviderCreate(umfCUDAMemoryProviderOps(), params, &provider);
     ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
     ASSERT_NE(provider, nullptr);
 
@@ -218,26 +300,51 @@ TEST_P(umfCUDAProviderTest, getPageSizeInvalidArgs) {
     umfMemoryProviderDestroy(provider);
 }
 
+TEST_P(umfCUDAProviderTest, cudaProviderNullParams) {
+    umf_result_t res = umfCUDAMemoryProviderParamsCreate(nullptr);
+    EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT);
+
+    res = umfCUDAMemoryProviderParamsSetContext(nullptr, expected_context);
+    EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT);
+
+    res = umfCUDAMemoryProviderParamsSetDevice(nullptr, 1);
+    EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT);
+
+    res =
+        umfCUDAMemoryProviderParamsSetMemoryType(nullptr, expected_memory_type);
+    EXPECT_EQ(res, UMF_RESULT_ERROR_INVALID_ARGUMENT);
+}
+
 // TODO add tests that mixes CUDA Memory Provider and Disjoint Pool
 
-cuda_memory_provider_params_t cuParams_device_memory =
-    create_cuda_prov_params(UMF_MEMORY_TYPE_DEVICE);
-cuda_memory_provider_params_t cuParams_shared_memory =
-    create_cuda_prov_params(UMF_MEMORY_TYPE_SHARED);
-cuda_memory_provider_params_t cuParams_host_memory =
-    create_cuda_prov_params(UMF_MEMORY_TYPE_HOST);
+CUDATestHelper cudaTestHelper;
+
+cuda_params_unique_handle_t cuParams_device_memory = create_cuda_prov_params(
+    cudaTestHelper.get_test_context(), cudaTestHelper.get_test_device(),
+    UMF_MEMORY_TYPE_DEVICE);
+cuda_params_unique_handle_t cuParams_shared_memory = create_cuda_prov_params(
+    cudaTestHelper.get_test_context(), cudaTestHelper.get_test_device(),
+    UMF_MEMORY_TYPE_SHARED);
+cuda_params_unique_handle_t cuParams_host_memory = create_cuda_prov_params(
+    cudaTestHelper.get_test_context(), cudaTestHelper.get_test_device(),
+    UMF_MEMORY_TYPE_HOST);
 
-CUDAMemoryAccessor
-    cuAccessor((CUcontext)cuParams_device_memory.cuda_context_handle,
-               (CUdevice)cuParams_device_memory.cuda_device_handle);
+CUDAMemoryAccessor cuAccessor(cudaTestHelper.get_test_context(),
+                              cudaTestHelper.get_test_device());
 HostMemoryAccessor hostAccessor;
 
 INSTANTIATE_TEST_SUITE_P(
     umfCUDAProviderTestSuite, umfCUDAProviderTest,
     ::testing::Values(
-        CUDAProviderTestParams{cuParams_device_memory, &cuAccessor},
-        CUDAProviderTestParams{cuParams_shared_memory, &hostAccessor},
-        CUDAProviderTestParams{cuParams_host_memory, &hostAccessor}));
+        CUDAProviderTestParams{cuParams_device_memory.get(),
+                               cudaTestHelper.get_test_context(),
+                               UMF_MEMORY_TYPE_DEVICE, &cuAccessor},
+        CUDAProviderTestParams{cuParams_shared_memory.get(),
+                               cudaTestHelper.get_test_context(),
+                               UMF_MEMORY_TYPE_SHARED, &hostAccessor},
+        CUDAProviderTestParams{cuParams_host_memory.get(),
+                               cudaTestHelper.get_test_context(),
+                               UMF_MEMORY_TYPE_HOST, &hostAccessor}));
 
 // TODO: add IPC API
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(umfIpcTest);
@@ -246,5 +353,5 @@ INSTANTIATE_TEST_SUITE_P(umfCUDAProviderTestSuite, umfIpcTest,
                          ::testing::Values(ipcTestParams{
                              umfProxyPoolOps(), nullptr,
                              umfCUDAMemoryProviderOps(),
-                             &cuParams_device_memory, &cuAccessor}));
+                             cuParams_device_memory.get(), &cuAccessor, false}));
 */