Skip to content

Commit e2fa04b

Browse files
committed
Handle NoGradient in native custom gradient bridge
1 parent 7c7dc54 commit e2fa04b

2 files changed

Lines changed: 81 additions & 10 deletions

File tree

tensorflow-core/tensorflow-core-native/src/main/java/org/tensorflow/internal/c_api/presets/tensorflow.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,19 @@ public void map(InfoMap infoMap) {
213213

214214
// Skip C++ classes
215215
infoMap.put(new Info("tsl::StatusGroup").skip());
216+
217+
// Force correct marshalling of TFJ_RegisterCustomGradient callback argument.
218+
// Without an explicit cast, JavaCPP may pass a NULL function pointer for some FunctionPointer
219+
// instances.
220+
infoMap.put(
221+
new Info("TFJ_RegisterCustomGradient")
222+
.javaText(
223+
"public static native @Cast(\"bool\") boolean TFJ_RegisterCustomGradient("
224+
+ "@Cast(\"const char*\") BytePointer op_type, "
225+
+ "@Cast(\"TFJ_GradFuncAdapter\") TFJ_GradFuncAdapter custom_gradient_adapter);\n"
226+
+ "public static native @Cast(\"bool\") boolean TFJ_RegisterCustomGradient("
227+
+ "@Cast(\"const char*\") String op_type, "
228+
+ "@Cast(\"TFJ_GradFuncAdapter\") TFJ_GradFuncAdapter custom_gradient_adapter);\n"));
216229
}
217230

218231
@Override

tensorflow-core/tensorflow-core-native/src/main/native/org/tensorflow/internal/c_api/tfj_gradients_impl.cc

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ limitations under the License.
1818
#include <stdio.h>
1919
#include <stdlib.h>
2020

21+
// IMPORTANT: explicit STL includes (this .cc is included via tfj_gradients.h in some builds,
22+
// so we must not rely on transitive includes from other headers).
23+
#include <string>
24+
#include <vector>
25+
#include <unordered_map>
26+
2127
#include "tfj_graph.h"
2228
#include "tsl/platform/errors.h"
2329
#include "tensorflow/c/c_api.h"
@@ -32,7 +38,6 @@ namespace tensorflow {
3238
unordered_map<string, TFJ_GradFuncAdapter> g_grad_func_adapters;
3339

3440
/// This method can be used to cast a pointer to/from a C struct that contains only that pointer. It is a bit
35-
3641
/// It has been "inspired" by the TensorFlow C API code, as found at this location when time of writing:
3742
/// https://github.com/tensorflow/tensorflow/blob/9d637f69f699c0c422716b56153a8b27b681891a/tensorflow/c/c_api.cc#L658
3843
template <typename T, typename U> T* struct_cast(U* ptr) {
@@ -53,29 +58,74 @@ namespace tensorflow {
5358
if (found_adapter == g_grad_func_adapters.end()) {
5459
return errors::NotFound("No gradient adapter found for operation ", op_type);
5560
}
56-
int num_inputs = grad_inputs.size();
57-
TF_Output* inputs = (TF_Output*)malloc(num_inputs * sizeof(TF_Output));
61+
62+
const int num_inputs = static_cast<int>(grad_inputs.size());
63+
64+
TF_Output* inputs = nullptr;
65+
if (num_inputs > 0) {
66+
inputs = static_cast<TF_Output*>(malloc(num_inputs * sizeof(TF_Output)));
67+
if (inputs == nullptr) {
68+
return errors::ResourceExhausted(
69+
"Out of memory allocating inputs for custom gradient of op ", op_type);
70+
}
71+
}
72+
5873
for (int i = 0; i < num_inputs; ++i) {
59-
Output grad_input = grad_inputs[i];
74+
const Output& grad_input = grad_inputs[i];
6075
inputs[i].oper = struct_cast<TF_Operation>(grad_input.node());
6176
inputs[i].index = grad_input.index();
6277
}
63-
TF_Output* outputs = NULL;
78+
79+
TF_Output* outputs = nullptr;
6480
LOG(INFO) << "Calling Java gradient function for operation of type " << op_type;
65-
int num_outputs = found_adapter->second(
81+
82+
TFJ_GradFuncAdapter adapter = found_adapter->second;
83+
if (adapter == nullptr) {
84+
if (inputs != nullptr) free(inputs);
85+
return errors::Unknown("Null Java gradient adapter for op ", op_type);
86+
}
87+
LOG(INFO) << "Adapter ptr for " << op_type << " = " << reinterpret_cast<void*>(found_adapter->second);
88+
const int num_outputs = adapter(
6689
static_cast<TFJ_GraphId>(scope.graph()),
6790
struct_cast<TFJ_Scope>(const_cast<Scope*>(&scope)),
6891
struct_cast<TF_Operation>(op.node()),
6992
inputs,
7093
num_inputs,
7194
&outputs
7295
);
96+
97+
// Always free inputs, even on error paths.
98+
if (inputs != nullptr) free(inputs);
99+
100+
// Adapter contract hardening:
101+
// - On Java exception / failure, adapter should return negative or outputs==nullptr.
102+
if (num_outputs < 0) {
103+
if (outputs != nullptr) free(outputs);
104+
return errors::Unknown("Java custom gradient adapter failed for op ", op_type,
105+
" (num_outputs=", num_outputs, ")");
106+
}
107+
if (num_outputs > 0 && outputs == nullptr) {
108+
return errors::Unknown("Java custom gradient adapter returned null outputs for op ",
109+
op_type, " with num_outputs=", num_outputs);
110+
}
111+
112+
grad_outputs->reserve(grad_outputs->size() + static_cast<size_t>(num_outputs));
113+
73114
for (int i = 0; i < num_outputs; ++i) {
74-
TF_Output output = outputs[i];
75-
grad_outputs->push_back(Output(struct_cast<Node>(output.oper), output.index));
115+
const TF_Output out = outputs[i];
116+
117+
// "NoGradient" sentinel from Java: TF_Output.oper == nullptr
118+
if (out.oper == nullptr) {
119+
// Represent "no gradient" as an empty Output.
120+
// TF's gradient builder should tolerate missing gradients for non-differentiable inputs.
121+
grad_outputs->push_back(Output());
122+
continue;
123+
}
124+
125+
grad_outputs->push_back(Output(struct_cast<Node>(out.oper), out.index));
76126
}
77-
free(inputs);
78-
free(outputs); // outputs are allocated from Java but must be freed here
127+
128+
if (outputs != nullptr) free(outputs);
79129
return OkStatus();
80130
}
81131
}
@@ -91,6 +141,14 @@ bool TFJ_HasGradient(const char* op_type) {
91141
}
92142

93143
bool TFJ_RegisterCustomGradient(const char* op_type, TFJ_GradFuncAdapter grad_func_adapter) {
144+
LOG(INFO) << "TFJ_RegisterCustomGradient(" << op_type << ") adapter_ptr="
145+
<< reinterpret_cast<void*>(grad_func_adapter);
146+
147+
if (grad_func_adapter == nullptr) {
148+
LOG(ERROR) << "Refusing to register NULL Java gradient adapter for op " << op_type;
149+
return false;
150+
}
151+
94152
if (TFJ_HasGradient(op_type)) { // Check if gradient already exists otherwise the JVM might abort/crash
95153
LOG(WARNING) << "Tried to register Java gradient function for operation " << op_type
96154
<< ", which has already a registered function";

0 commit comments

Comments
 (0)