diff --git a/coreruntime/nimblenet/nimble_net/include/nimble_net/c_tensor.h b/coreruntime/nimblenet/nimble_net/include/nimble_net/c_tensor.h index 0273756c..04e3691b 100644 --- a/coreruntime/nimblenet/nimble_net/include/nimble_net/c_tensor.h +++ b/coreruntime/nimblenet/nimble_net/include/nimble_net/c_tensor.h @@ -77,16 +77,48 @@ char* c_tensor_get_string_data(void* data); // ================================================================================================= /** - * @brief Function pointer type for invoking a frontend function as a callback from delitepy script. + * @brief A marker type for the user-defined context for a foreign function. + */ +typedef struct CForeignFunctionContext CForeignFunctionContext; + +/** + * @brief Function pointer type for deleting a foreign function context. + */ +typedef void (*CForeignFunctionContextDeleter)(CForeignFunctionContext* context); + +/** + * @brief Function pointer type for a foreign function that can be invoked as a callback from a + * DelitePy script. + * + * @param context The user-provided context pointer. + * @param input A struct containing the input tensors. + * @param output Pointer to a struct where the output tensors should be stored. + * @return NimbleNetStatus* Status of invoking the foreign function. + */ +typedef NimbleNetStatus* (*CForeignFunctionPtr)(CForeignFunctionContext* context, + const CTensors input, CTensors* output); + +/** + * @brief An opaque type encapsulating a `CForeignFunctionPtr`, a `CForeignFunctionContext*`, + * and a `CForeignFunctionContextDeleter`. + */ +typedef struct CForeignFunctionObject CForeignFunctionObject; + +/** + * @brief Creates a CForeignFunctionObject instance encapsulating the provided inputs. * - * @param context Pointer to user-defined context. - * @param input Struct containing input tensors. - * @param output Pointer to struct where output tensors will be stored. + * The return value can be assigned to `CTensor::data` + * when the corresponding `CTensor::dataType` value is `DATATYPE::FUNCTION`. + * It can be deleted using `c_tensor_delete_data(CTensor*)`. * - * @return NimbleNetStatus* Status pointer indicating the result of event handling. + * @param fn A foreign function pointer. + * @param context A user-defined context pointer to be passed along to the foreign function. + * @param context_deleter A function pointer to delete the provided context. + * @return Pointer to the created CForeignFunctionObject instance. */ -typedef NimbleNetStatus* (*FrontendFunctionPtr)(void* context, const CTensors input, - CTensors* output); +CForeignFunctionObject* c_tensor_create_function_data( + CForeignFunctionPtr fn, CForeignFunctionContext* context, + CForeignFunctionContextDeleter context_deleter); // ================================================================================================= diff --git a/coreruntime/nimblenet/nimble_net/src/c_tensor.cpp b/coreruntime/nimblenet/nimble_net/src/c_tensor.cpp index 39a3d230..6f7ee5ae 100644 --- a/coreruntime/nimblenet/nimble_net/src/c_tensor.cpp +++ b/coreruntime/nimblenet/nimble_net/src/c_tensor.cpp @@ -8,7 +8,11 @@ #include #include +#include +#include +#include "custom_func_data_variable.hpp" +#include "map_data_variable.hpp" #include "nimble_net_util.hpp" namespace { @@ -45,6 +49,58 @@ void c_tensor_delete_string_data(CTensor* tensor) { free(str_arr); } +// ================================================================================================= + +std::shared_ptr create_foreign_function_arg_map( + const std::vector& args) { + if (args.size() != 1) { + THROW("calling foreign function: num args: expected = 1, actual = %zu", args.size()); + } + + const auto& arg = args[0]; + if (arg->get_containerType() != CONTAINERTYPE::MAP) { + THROW("calling foreign function: arg container type: expected = \"Map\", actual = \"%s\"", + arg->get_containerType_string()); + } + return std::dynamic_pointer_cast(arg); +} + +CustomFuncDataVariable create_foreign_function_data_variable( + CForeignFunctionPtr fn, CForeignFunctionContext* context, + CForeignFunctionContextDeleter context_deleter) { + auto sContext = std::shared_ptr( + context, [context_deleter](CForeignFunctionContext* ctx) { + if (context_deleter) { + context_deleter(ctx); + } + }); + return CustomFuncDataVariable([fn, context = sContext](const std::vector& args, + CallStack& stack) -> OpReturnType { + std::shared_ptr fnInput = create_foreign_function_arg_map(args); + CTensors fnInTensors; + fnInput->convert_to_cTensors(&fnInTensors); + + CTensors fnOutTensors; + auto status = fn(context.get(), fnInTensors, &fnOutTensors); + delete[] fnInTensors.tensors; + + if (status != nullptr) { + auto fmtString = ne::fmt("Callback function failed with status code: '%d', error: '%s'", + status->code, status->message); + deallocate_nimblenet_status(status); + throw std::runtime_error(fmtString.str); + } + + auto fnOutput = std::make_shared(fnOutTensors); + deallocate_frontend_tensors(fnOutTensors); + return fnOutput; + }); +} + +void c_tensor_delete_function_data(CForeignFunctionObject* data) { + delete reinterpret_cast(data); +} + } // namespace // ================================================================================================= @@ -121,6 +177,16 @@ char* c_tensor_get_string_data(void* data) { return static_cast(data)[0] // ================================================================================================= +CForeignFunctionObject* c_tensor_create_function_data( + CForeignFunctionPtr fn, CForeignFunctionContext* context, + CForeignFunctionContextDeleter context_deleter) { + auto fn_data_var = std::make_shared( + create_foreign_function_data_variable(fn, context, context_deleter)); + return reinterpret_cast(new OpReturnType(fn_data_var)); +} + +// ================================================================================================= + bool c_tensor_delete_data(CTensor* tensor) { if (!tensor->data) { return true; @@ -141,6 +207,11 @@ bool c_tensor_delete_data(CTensor* tensor) { return true; } + case DATATYPE::FUNCTION: { + c_tensor_delete_function_data(static_cast(tensor->data)); + return true; + } + default: break; } diff --git a/coreruntime/nimblenet/simulation_manager/input_data_extractor/include/task_input_structs.hpp b/coreruntime/nimblenet/simulation_manager/input_data_extractor/include/task_input_structs.hpp index 88be77c4..2ffeea13 100644 --- a/coreruntime/nimblenet/simulation_manager/input_data_extractor/include/task_input_structs.hpp +++ b/coreruntime/nimblenet/simulation_manager/input_data_extractor/include/task_input_structs.hpp @@ -44,16 +44,6 @@ struct TaskInputData { */ static nlohmann::json get_json_from_OpReturnType(void* data); - /** - * @brief Create a function data variable for use in simulation. - * - * @param context Pointer to the function context. - * @param frontEndFunctionPtr Function pointer to the frontend callback. - * @return Pointer to the created function data variable. - */ - static void* create_function_data_variable(void* context, - FrontendFunctionPtr frontEndFunctionPtr); - /** * @brief Deallocate an OpReturnType pointer. * diff --git a/coreruntime/nimblenet/simulation_manager/input_data_extractor/src/c_tensors_data_extractor.cpp b/coreruntime/nimblenet/simulation_manager/input_data_extractor/src/c_tensors_data_extractor.cpp index b6a90e52..1216711b 100644 --- a/coreruntime/nimblenet/simulation_manager/input_data_extractor/src/c_tensors_data_extractor.cpp +++ b/coreruntime/nimblenet/simulation_manager/input_data_extractor/src/c_tensors_data_extractor.cpp @@ -13,9 +13,7 @@ #include "nlohmann/json.hpp" #include "client.h" -#include "custom_func_data_variable.hpp" #include "data_variable.hpp" -#include "map_data_variable.hpp" using json = nlohmann::json; @@ -323,51 +321,6 @@ nlohmann::json TaskInputData::get_json_from_OpReturnType(void* data) { return (static_cast(data))->get()->to_json(); } -namespace { -std::shared_ptr create_foreign_function_arg_map( - const std::vector& args) { - if (args.size() != 1) { - THROW("calling foreign function: num args: expected = 1, actual = %zu", args.size()); - } - - const auto& arg = args[0]; - if (arg->get_containerType() != CONTAINERTYPE::MAP) { - THROW("calling foreign function: arg container type: expected = \"Map\", actual = \"%s\"", - arg->get_containerType_string()); - } - return std::dynamic_pointer_cast(arg); -} -} // anonymous namespace - -void* TaskInputData::create_function_data_variable(void* context, - FrontendFunctionPtr frontEndFunctionPtr) { - auto sContext = - std::shared_ptr(context, [](void* ctx) { free_frontend_function_context(ctx); }); - - return (void*)new OpReturnType(new CustomFuncDataVariable( - [context = sContext, frontEndFunctionPtr](const std::vector& arguments, - CallStack& stack) -> OpReturnType { - std::shared_ptr fnInput = create_foreign_function_arg_map(arguments); - CTensors fnInTensors; - fnInput->convert_to_cTensors(&fnInTensors); - - CTensors fnOutTensors; - auto status = frontEndFunctionPtr(context.get(), fnInTensors, &fnOutTensors); - delete[] fnInTensors.tensors; - - if (status != nullptr) { - auto fmtString = ne::fmt("Callback function failed with status code %d error %s", - status->code, status->message); - deallocate_nimblenet_status(status); - throw std::runtime_error(fmtString.str); - } - - auto fnOutput = std::make_shared(fnOutTensors); - deallocate_frontend_tensors(fnOutTensors); - return fnOutput; - })); -} - void TaskInputData::deallocate_OpReturnType(void* data) { delete (OpReturnType*)data; } #endif diff --git a/coreruntime/platform/android/include/client.h b/coreruntime/platform/android/include/client.h index 936b5c65..2520db78 100644 --- a/coreruntime/platform/android/include/client.h +++ b/coreruntime/platform/android/include/client.h @@ -174,14 +174,6 @@ bool schedule_logs_upload(long repeatIntervalInMinutes, long retryIntervalInMinu */ bool deallocate_frontend_tensors(CTensors cTensors); -/** - * @brief Frees the memory for a frontend function context. (No-op on Android) - * - * @param context Pointer to the context to free. - * @return bool Always returns true. - */ -bool free_frontend_function_context(void* context); - /** * @brief Return phonemes of the given string. * diff --git a/coreruntime/platform/android/src/client.cpp b/coreruntime/platform/android/src/client.cpp index 3de61238..965e2297 100644 --- a/coreruntime/platform/android/src/client.cpp +++ b/coreruntime/platform/android/src/client.cpp @@ -310,8 +310,6 @@ bool schedule_logs_upload(long repeatIntervalInMinutes, long retryIntervalInMinu bool deallocate_frontend_tensors(CTensors cTensors) { return true; } -bool free_frontend_function_context(void *context) { return true; } - namespace { /** diff --git a/coreruntime/platform/ios/include/frontend_layer.h b/coreruntime/platform/ios/include/frontend_layer.h index a01a810b..19c09c64 100644 --- a/coreruntime/platform/ios/include/frontend_layer.h +++ b/coreruntime/platform/ios/include/frontend_layer.h @@ -135,11 +135,3 @@ extern get_keys_ios_object_type get_keys_ios_object_global; /**< Global function * @return bool True if successful. */ bool deallocate_frontend_tensors(CTensors cTensors); - -/** - * @brief Frees the memory for a frontend function context (iOS implementation). - * - * @param context Pointer to the context to free. - * @return bool True if successful. - */ -bool free_frontend_function_context(void* context); diff --git a/coreruntime/platform/ios/src/frontend_layer.cpp b/coreruntime/platform/ios/src/frontend_layer.cpp index 5604239b..3d335f58 100644 --- a/coreruntime/platform/ios/src/frontend_layer.cpp +++ b/coreruntime/platform/ios/src/frontend_layer.cpp @@ -20,5 +20,3 @@ release_ios_object_type release_ios_object_global = nullptr; get_keys_ios_object_type get_keys_ios_object_global = nullptr; bool deallocate_frontend_tensors(CTensors cTensors) { return true; } - -bool free_frontend_function_context(void *context) { return true; } diff --git a/coreruntime/platform/unix/include/client.h b/coreruntime/platform/unix/include/client.h index b6196ee2..9ecfd360 100644 --- a/coreruntime/platform/unix/include/client.h +++ b/coreruntime/platform/unix/include/client.h @@ -136,16 +136,8 @@ static inline bool schedule_logs_upload(long repeatIntervalInMinutes, * @return bool True if successful. */ typedef bool (*DeallocateFrontendType)(CTensors cTensors); -/** - * @brief Function pointer type for freeing frontend function context. - * - * @param context Pointer to the context to free. - * @return bool True if successful. - */ -typedef bool (*FreeFrontendContextType)(void *context); extern DeallocateFrontendType globalDeallocate; /**< Global function pointer for tensor deallocation. */ -extern FreeFrontendContextType globalFrontendContextFree; /**< Global function pointer for context free. */ /** * @brief Deallocates memory for frontend tensors (Unix implementation). @@ -154,11 +146,3 @@ extern FreeFrontendContextType globalFrontendContextFree; /**< Global function p * @return bool True if successful. */ bool deallocate_frontend_tensors(CTensors cTensors); - -/** - * @brief Frees the memory for a frontend function context (Unix implementation). - * - * @param context Pointer to the context to free. - * @return bool True if successful. - */ -bool free_frontend_function_context(void *context); diff --git a/coreruntime/platform/unix/src/client.cpp b/coreruntime/platform/unix/src/client.cpp index ed8a294e..00dd3f81 100644 --- a/coreruntime/platform/unix/src/client.cpp +++ b/coreruntime/platform/unix/src/client.cpp @@ -212,7 +212,7 @@ CNetworkResponse send_request(const char *body, const char *headers_c, const cha response.body = s.ptr; response.bodyLength = s.len; response.headers = headerPointer; - + #ifndef NDEBUG printf("%s status_code=%ld\n", buffer, http_code); #endif @@ -270,10 +270,3 @@ bool deallocate_frontend_tensors(CTensors cTensors) { } return false; } - -bool free_frontend_function_context(void *context) { - if (globalFrontendContextFree) { - return globalFrontendContextFree(context); - } - return false; -} diff --git a/coreruntime/platform/unix/src/frontend_layer.cpp b/coreruntime/platform/unix/src/frontend_layer.cpp index c3302270..c227671c 100644 --- a/coreruntime/platform/unix/src/frontend_layer.cpp +++ b/coreruntime/platform/unix/src/frontend_layer.cpp @@ -7,4 +7,3 @@ #include "client.h" DeallocateFrontendType globalDeallocate __attribute__((visibility("default"))) = nullptr; -FreeFrontendContextType globalFrontendContextFree __attribute__((visibility("default"))) = nullptr; \ No newline at end of file diff --git a/nimblenet_py/simulator_binder/binder.cpp b/nimblenet_py/simulator_binder/binder.cpp index c4251dc1..6c955490 100644 --- a/nimblenet_py/simulator_binder/binder.cpp +++ b/nimblenet_py/simulator_binder/binder.cpp @@ -210,7 +210,6 @@ bool deallocFrontendTensors(CTensors cTensors) { switch (tensor.dataType) { case DATATYPE::JSON: case DATATYPE::JSON_ARRAY: - case DATATYPE::FUNCTION: TaskInputData::deallocate_OpReturnType(tensor.data); break; default: { @@ -225,11 +224,6 @@ bool deallocFrontendTensors(CTensors cTensors) { return true; } -bool freeFrontendContext(void* context) { - delete (py::object*)context; - return true; -} - nlohmann::json convert_py_list_to_json(py::list& list) { nlohmann::json jsonArray = nlohmann::json(nlohmann::json::array()); for (const auto& item : list) { @@ -257,7 +251,6 @@ int initialize_simulator_nimblenet(const char* configInput, py::list moduleConfi // setting global cleanup functions globalDeallocate = deallocFrontendTensors; - globalFrontendContextFree = freeFrontendContext; // If no config provided prepare a default config if (configInput == nullptr) { diff --git a/nimblenet_py/simulator_binder/binder_v2.cpp b/nimblenet_py/simulator_binder/binder_v2.cpp index 759c3cd5..bbfff857 100644 --- a/nimblenet_py/simulator_binder/binder_v2.cpp +++ b/nimblenet_py/simulator_binder/binder_v2.cpp @@ -270,19 +270,20 @@ CTensor construct_singleVariable_input(const std::string& name, py::object item) } else if (py::isinstance(item)) { cTensor.dataType = DATATYPE::FUNCTION; - // This lambda should be able to run the frontend Function. - // Context should store info related to running the frontend function, or the function itself - FrontendFunctionPtr myLambda = [](void* context, const CTensors input, - CTensors* output) -> NimbleNetStatus* { - auto pythonFunction = py::cast(*(py::handle*)context); + auto context = reinterpret_cast(new py::object(item)); + CForeignFunctionContextDeleter context_deleter = [](CForeignFunctionContext* context) { + delete reinterpret_cast(context); + }; + CForeignFunctionPtr fn = [](CForeignFunctionContext* context, const CTensors input, + CTensors* output) -> NimbleNetStatus* { + auto pythonFunction = py::cast(*reinterpret_cast(context)); auto inputForPythonFunction = convert_CTensors_to_pymap(input); auto returnObject = pythonFunction(inputForPythonFunction); *output = convert_pydict_to_CTensors(returnObject); return nullptr; }; - void* context = new py::object(item); - cTensor.data = TaskInputData::create_function_data_variable(context, myLambda); + cTensor.data = c_tensor_create_function_data(fn, context, context_deleter); } return cTensor; }