Skip to content
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
46 changes: 39 additions & 7 deletions coreruntime/nimblenet/nimble_net/include/nimble_net/c_tensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

// =================================================================================================

Expand Down
71 changes: 71 additions & 0 deletions coreruntime/nimblenet/nimble_net/src/c_tensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

#include <cstdlib>
#include <cstring>
#include <memory>
#include <stdexcept>

#include "custom_func_data_variable.hpp"
#include "map_data_variable.hpp"
#include "nimble_net_util.hpp"

namespace {
Expand Down Expand Up @@ -45,6 +49,58 @@ void c_tensor_delete_string_data(CTensor* tensor) {
free(str_arr);
}

// =================================================================================================

std::shared_ptr<MapDataVariable> create_foreign_function_arg_map(
const std::vector<OpReturnType>& 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<MapDataVariable>(arg);
}

CustomFuncDataVariable create_foreign_function_data_variable(
CForeignFunctionPtr fn, CForeignFunctionContext* context,
CForeignFunctionContextDeleter context_deleter) {
auto sContext = std::shared_ptr<CForeignFunctionContext>(
context, [context_deleter](CForeignFunctionContext* ctx) {
if (context_deleter) {
context_deleter(ctx);
}
});
return CustomFuncDataVariable([fn, context = sContext](const std::vector<OpReturnType>& args,
CallStack& stack) -> OpReturnType {
std::shared_ptr<MapDataVariable> 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<MapDataVariable>(fnOutTensors);
deallocate_frontend_tensors(fnOutTensors);
return fnOutput;
});
}

void c_tensor_delete_function_data(CForeignFunctionObject* data) {
delete reinterpret_cast<OpReturnType*>(data);
}

} // namespace

// =================================================================================================
Expand Down Expand Up @@ -121,6 +177,16 @@ char* c_tensor_get_string_data(void* data) { return static_cast<char**>(data)[0]

// =================================================================================================

CForeignFunctionObject* c_tensor_create_function_data(
CForeignFunctionPtr fn, CForeignFunctionContext* context,
CForeignFunctionContextDeleter context_deleter) {
auto fn_data_var = std::make_shared<CustomFuncDataVariable>(
create_foreign_function_data_variable(fn, context, context_deleter));
return reinterpret_cast<CForeignFunctionObject*>(new OpReturnType(fn_data_var));
}

// =================================================================================================

bool c_tensor_delete_data(CTensor* tensor) {
if (!tensor->data) {
return true;
Expand All @@ -141,6 +207,11 @@ bool c_tensor_delete_data(CTensor* tensor) {
return true;
}

case DATATYPE::FUNCTION: {
c_tensor_delete_function_data(static_cast<CForeignFunctionObject*>(tensor->data));
return true;
}

default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -323,51 +321,6 @@ nlohmann::json TaskInputData::get_json_from_OpReturnType(void* data) {
return (static_cast<OpReturnType*>(data))->get()->to_json();
}

namespace {
std::shared_ptr<MapDataVariable> create_foreign_function_arg_map(
const std::vector<OpReturnType>& 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<MapDataVariable>(arg);
}
} // anonymous namespace

void* TaskInputData::create_function_data_variable(void* context,
FrontendFunctionPtr frontEndFunctionPtr) {
auto sContext =
std::shared_ptr<void>(context, [](void* ctx) { free_frontend_function_context(ctx); });

return (void*)new OpReturnType(new CustomFuncDataVariable(
[context = sContext, frontEndFunctionPtr](const std::vector<OpReturnType>& arguments,
CallStack& stack) -> OpReturnType {
std::shared_ptr<MapDataVariable> 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<MapDataVariable>(fnOutTensors);
deallocate_frontend_tensors(fnOutTensors);
return fnOutput;
}));
}

void TaskInputData::deallocate_OpReturnType(void* data) { delete (OpReturnType*)data; }

#endif
8 changes: 0 additions & 8 deletions coreruntime/platform/android/include/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
2 changes: 0 additions & 2 deletions coreruntime/platform/android/src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

/**
Expand Down
8 changes: 0 additions & 8 deletions coreruntime/platform/ios/include/frontend_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 0 additions & 2 deletions coreruntime/platform/ios/src/frontend_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
16 changes: 0 additions & 16 deletions coreruntime/platform/unix/include/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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);
9 changes: 1 addition & 8 deletions coreruntime/platform/unix/src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
1 change: 0 additions & 1 deletion coreruntime/platform/unix/src/frontend_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@
#include "client.h"

DeallocateFrontendType globalDeallocate __attribute__((visibility("default"))) = nullptr;
FreeFrontendContextType globalFrontendContextFree __attribute__((visibility("default"))) = nullptr;
7 changes: 0 additions & 7 deletions nimblenet_py/simulator_binder/binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
15 changes: 8 additions & 7 deletions nimblenet_py/simulator_binder/binder_v2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,19 +270,20 @@ CTensor construct_singleVariable_input(const std::string& name, py::object item)
} else if (py::isinstance<py::function>(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::function>(*(py::handle*)context);
auto context = reinterpret_cast<CForeignFunctionContext*>(new py::object(item));
CForeignFunctionContextDeleter context_deleter = [](CForeignFunctionContext* context) {
delete reinterpret_cast<py::object*>(context);
};
CForeignFunctionPtr fn = [](CForeignFunctionContext* context, const CTensors input,
CTensors* output) -> NimbleNetStatus* {
auto pythonFunction = py::cast<py::function>(*reinterpret_cast<py::handle*>(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;
}
Expand Down