From: Piotr Kosko/Tizen API (PLT) /SRPOL/Engineer/Samsung Electronics Date: Wed, 23 Jun 2021 11:32:22 +0000 (+0200) Subject: [ML][Single] Added InvokeAsync() implementation X-Git-Tag: submit/tizen/20210628.105256~1^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3e12d4f1adea2c66e27494061757da3ddc33e7cb;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [ML][Single] Added InvokeAsync() implementation [ACR] https://code.sec.samsung.net/jira/browse/TWDAPI-278 [Verification] Code compiles without errors. TCT passrate of existing APIs didn't change. Checked in chrome console with below snippets: // initialize test data model = tizen.ml.single.openModel("documents/model.tflite"); var tensorsInfo = new tizen.ml.TensorsInfo(); tensorsInfo.addTensorInfo("tensor", "UINT8", [3, 224, 224]); var tensorsData = tensorsInfo.getTensorsData(); var tensorsInfoInvalid = new tizen.ml.TensorsInfo(); tensorsInfoInvalid.addTensorInfo("tensor", "UINT8", [3, 125, 125]); var tensorsDataInvalid = tensorsInfoInvalid.getTensorsData(); function errorCallback(error) { console.log(error); } function successCallback(tensorsDataOut) { console.log("Inference finished successfully"); console.log(tensorsDataOut.getTensorRawData(0)); tensorsDataOut.dispose(); } // success // test1 model.invokeAsync(tensorsData, successCallback, errorCallback); // test2 model.invokeAsync(tensorsData, successCallback); // errors // test3 model.invokeAsync(tensorsData); // TypeMismatchError - sync // test4 model.invokeAsync(null, successCallback); // TypeMismatchError - sync // test5 model.invokeAsync(tensorsDataInvalid, successCallback, errorCallback); // AbortError - async // test6 model.setTimeout(1) model.invokeAsync(tensorsData, successCallback, errorCallback); // TimeoutError - async // clear tensorsData tensorsData.dispose(); tensorsInfo.dispose(); // test7 - use of disposed tesnsorsData model.invokeAsync(tensorsData, successCallback, errorCallback); // AbortError - sync // clear other data tensorsDataInvalid.dispose(); tensorsInfoInvalid.dispose(); model.close(); Change-Id: I59900d7bab9a76939e27d68cb2bcd5434f446b3d --- diff --git a/src/ml/js/ml_single.js b/src/ml/js/ml_single.js index b9598d79..1ecbaf6e 100755 --- a/src/ml/js/ml_single.js +++ b/src/ml/js/ml_single.js @@ -331,6 +331,57 @@ SingleShot.prototype.invoke = function() { return new TensorsData(result.tensorsDataId, result.tensorsInfoId); }; +var ValidInvokeAsyncErrors = ['TimeoutError', 'NotSupportedError', 'AbortError']; +SingleShot.prototype.invokeAsync = function() { + var args = validator_.validateArgs(arguments, [ + { + name: 'inTensorsData', + type: types_.PLATFORM_OBJECT, + values: TensorsData + }, + { + name: 'successCallback', + type: types_.FUNCTION + }, + { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true } + ]); + + var callback = function(result) { + if (native_.isFailure(result)) { + setTimeout(function() { + native_.callIfPossible( + args.errorCallback, + native_.getErrorObjectAndValidate( + result, + ValidInvokeAsyncErrors, + AbortError + ) + ); + }, 0); + } else { + native_.callIfPossible( + args.successCallback, + new TensorsData(result.tensorsDataId, result.tensorsInfoId) + ); + } + }; + + var nativeArgs = { + id: this._id, + tensorsDataId: args.inTensorsData._id, + async: true + }; + + var result = native_.call('MLSingleShotInvoke', nativeArgs, callback); + if (native_.isFailure(result)) { + throw native_.getErrorObjectAndValidate( + result, + ValidInvokeAsyncErrors, + AbortError + ); + } +}; + var GetSetValueValidExceptions = [ 'AbortError', 'InvalidValuesError', diff --git a/src/ml/ml_instance.cc b/src/ml/ml_instance.cc index c6be5415..5cd456c6 100644 --- a/src/ml/ml_instance.cc +++ b/src/ml/ml_instance.cc @@ -889,24 +889,56 @@ void MlInstance::MLSingleShotInvoke(const picojson::value& args, picojson::objec int id = static_cast(args.get(kId).get()); int tensors_data_id = static_cast(args.get(kTensorsDataId).get()); + bool async = + (args.contains(kAsync) && args.get(kAsync).is()) ? args.get(kAsync).get() : false; TensorsData* in_tensors_data = GetTensorsDataManager().GetTensorsData(tensors_data_id); + if (async && in_tensors_data) { + // in case of async flow need to prevent destroying entry data during invoke + // from JS, creation of a copy + in_tensors_data = GetTensorsInfoManager().CreateTensorsData(in_tensors_data->GetTensorsInfo()); + } if (nullptr == in_tensors_data) { LogAndReportError(PlatformResult(ErrorCode::ABORT_ERR, "Internal TensorsData error"), &out, ("Could not find TensorsData handle with given id: %d", tensors_data_id)); return; } - TensorsData* out_tensors_data = nullptr; - auto ret = single_manager_.Invoke(id, in_tensors_data, &out_tensors_data); - if (!ret) { - ReportError(ret, &out); - return; - } + auto logic = [this, id, tensors_data_id, in_tensors_data, async](decltype(out) out) { + TensorsData* out_tensors_data = nullptr; + auto ret = single_manager_.Invoke(id, in_tensors_data, &out_tensors_data); + if (async) { + // in case of async flow, the in_tensor_data with underlying TensorsInfo + // was copied, thus need to be released here + GetTensorsInfoManager().DisposeTensorsInfo(in_tensors_data->GetTensorsInfo()); + GetTensorsDataManager().DisposeTensorsData(in_tensors_data); + } + if (!ret) { + ReportError(ret, &out); + return; + } - out[kTensorsDataId] = picojson::value(static_cast(out_tensors_data->Id())); - out[kTensorsInfoId] = picojson::value(static_cast(out_tensors_data->TensorsInfoId())); - ReportSuccess(out); + out[kTensorsDataId] = picojson::value(static_cast(out_tensors_data->Id())); + out[kTensorsInfoId] = picojson::value(static_cast(out_tensors_data->TensorsInfoId())); + ReportSuccess(out); + }; + + if (!async) { + logic(out); + } else { + CHECK_ARGS(args, kCallbackId, double, out); + double callback_id = args.get(kCallbackId).get(); + this->worker_.add_job([this, callback_id, logic] { + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + async_out[kCallbackId] = picojson::value(callback_id); + logic(async_out); + this->PostMessage(response.serialize().c_str()); + }); + + // Sync return + ReportSuccess(out); + } } void MlInstance::MLSingleShotGetValue(const picojson::value& args, picojson::object& out) {