MachineLearningPipeline.prototype.createPipeline = CreatePipeline;
//Pipeline::registerCustomFilter() begin
-var CustomFilterOutput = function() {
- validator_.isConstructorCall(this, CustomFilterOutput);
-
- var args = validator_.validateArgs(arguments, [
- {
- name: 'status',
- type: validator_.Types.LONG
- },
- {
- name: 'data',
- type: types_.PLATFORM_OBJECT,
- values: TensorsData,
- optional: true,
- nullable: true
- }
- ]);
-
- if (!args.has.data) {
- args.data = null;
- }
-
- if (args.status > 0 && args.status !== 1) {
- throw new WebAPIException(
- WebAPIException.INVALID_VALUES_ERR,
- 'CustomFilterOutput.status === 1 is the only legal positive value'
- );
- }
-
- if (args.status === 0 && args.data === null) {
- throw new WebAPIException(
- WebAPIException.INVALID_VALUES_ERR,
- 'CustomFilterOutput.data === null is illegal when ' +
- 'CustomFilterOutput.status === 0'
- );
- }
-
- Object.defineProperties(this, {
- status: {
- enumerable: true,
- value: args.status
- },
- data: {
- enumerable: true,
- value: args.data
- }
- });
-};
-
var ValidRegisterCustomFilterExceptions = [
'InvalidValuesError',
'NotSupportedError',
];
var ValidCustomFilterOutputErrors = ['InvalidValuesError', 'AbortError'];
+
MachineLearningPipeline.prototype.registerCustomFilter = function() {
var args = validator_.validateArgs(arguments, [
{
/*
* CustomFilter processing has 4 stages (the description below assumes
* the typical scenario with no errors):
- * 1. (C++) C++ callback is called by the native API with input data.
- * The C++ callback clones the tensors data and associated info and
- * sends it to JS.
- * 2. (JS) customFilterWrapper is called with the input data from C++
- * as one of its arguments. User-provided callback processes the data
- * and the output is sent to C++ by a call of asynchronous function.
- * 3. (C++) C++ callback is woken up and clones the output from user
- * callback to native tensors data. It replies to JS with success/error.
- * 4. (JS) If C++ responded with success, the operation stops.
+ * 1. (C++; non-main thread) C++ callback is called by the native API with input data.
+ * The C++ callback wraps native ml_tensors_data_h handles in TensorsData
+ * objects and sends them together with associated TensorsInfo to JS.
+ * 2. (JS; main thread) customFilterWrapper is called with the input data from C++
+ * as one of its arguments. User-provided callback processes the data.
+ * The input/output TensorsData that arrive to JS as CustomFilter arguments
+ * are unique in that they:
+ * - cannot be disposed, i.e. calling {input, output}.dispose() is no-op
+ * - input is immutable, i.e. calling input.setTensorRawData() is no-op
+ * output.setTensorRawData() modify the native nnstreamer object directly.
+ * 3. (C++; main thread) Sleeping callback thread is notified. If anything
+ * goes wrong, C++ function returns an error synchronously to stage 4.
+ * 4. (JS; main thread) If C++ returned a success, the operation stops.
* Otherwise, the error callback provided by the user is called.
+ * 5. (C++; non-main thread) C++ callback is woken up and returns the status
+ * received from user to pipeline.
*/
var customFilterWrapper = function(msg) {
/*
return;
}
- var inputData = new TensorsData(msg.tensorsDataId, msg.tensorsInfoId);
+ var inputData = new TensorsData(
+ msg.inputTensorsDataId,
+ msg.inputTensorsInfoId,
+ false
+ );
+ var outputData = new TensorsData(
+ msg.outputTensorsDataId,
+ msg.outputTensorsInfoId,
+ false
+ );
+
/*
* customFilterErrorInJs records errors caused by the CustomFilter callback
* provided by the user.
var customFilterErrorInJs = null;
var jsResponse = {
status: -1,
- dataId: -1,
name: nativeArgs.name,
requestId: msg.requestId
};
- var output = null;
try {
- output = args.customFilter(inputData);
+ jsResponse.status = converter_.toLong(
+ args.customFilter(inputData, outputData)
+ );
} catch (exception) {
+ var exceptionString =
+ typeof exception.toString === 'function'
+ ? exception.toString()
+ : JSON.stringify(exception);
customFilterErrorInJs = new WebAPIException(
WebAPIException.ABORT_ERR,
- 'CustomFilter has thrown exception: ' + xwalk.JSON.stringify(exception)
+ 'CustomFilter has thrown exception: ' + exceptionString
);
}
- if (output instanceof CustomFilterOutput) {
- jsResponse.status = output.status;
- jsResponse.dataId = type_.isNullOrUndefined(output.data)
- ? -1
- : output.data._id;
- } else if (customFilterErrorInJs === null) {
+ if (!customFilterErrorInJs && jsResponse.status > 0 && jsResponse.status !== 1) {
customFilterErrorInJs = new WebAPIException(
- WebAPIException.TYPE_MISMATCH_ERR,
- 'The value returned from CustomFilter is not a CustomFilterOutput object'
+ WebAPIException.INVALID_VALUES_ERR,
+ 'The only legal positive value of status returned from CustomFilter is 1'
);
+ jsResponse.status = -1;
}
/*
- * Callback called in stage 4.
- *
- * It is used to process success/error messages that come from
- * C++ (stage 3).
- * It does not handle errors caused by the user-provided CustomFilter
- * which we detect in JS.
+ * Entering stage 3.
*/
- function filterOutputCallback(result) {
- if (native_.isSuccess(result)) {
- return;
- }
-
- var error = native_.getErrorObjectAndValidate(
- result,
- ValidCustomFilterOutputErrors,
- AbortError
- );
-
- native_.callIfPossible(args.errorCallback, error);
- }
+ var result = native_.callSync('MLPipelineManagerCustomFilterOutput', jsResponse);
/*
- * Entering stage 3.
+ * Stage 4.
*/
- var result = native_.call(
- 'MLPipelineManagerCustomFilterOutput',
- jsResponse,
- filterOutputCallback
- );
-
if (customFilterErrorInJs) {
/*
* If we detect that user-provided CustomFilter callback caused
* any errors in JS, the C++ layer gets the message to stop the
* pipeline (status == -1) and does not reply to JS with errors.
- * Thus, filterOutputCallback is not called and this is why we
- * call the user-provided error callback from JS.
+ * Thus, "result" is a success we call the user-provided error
+ * callback here.
*/
native_.callIfPossible(args.errorCallback, customFilterErrorInJs);
} else if (native_.isFailure(result)) {
- filterOutputCallback(result);
+ var error = native_.getErrorObjectAndValidate(
+ result,
+ ValidCustomFilterOutputErrors,
+ AbortError
+ );
+
+ native_.callIfPossible(args.errorCallback, error);
}
};