Retrieves a Tensor from `eager_tensor` and stores it in `output_tensor`. Validates that `output_tensor` is backed by memory in `expected_device` (which is assumed to be a local device, one on which the kernel was executed.) It may be nice to copy the tensor to the right device instead of failing if it isn't already there. This is left as a future exercise. The required device-copying logic is im
| 135 | // it isn't already there. This is left as a future exercise. The required |
| 136 | // device-copying logic is implemented in Python at the moment. |
| 137 | tensorflow::Status ExtractTensorFromEagerTensor(const PyObject* eager_tensor, |
| 138 | const Device* expected_device, |
| 139 | const Tensor** output_tensor) { |
| 140 | auto handle = EagerTensor_Handle(eager_tensor)->handle; |
| 141 | Device* actual_device = handle->device(); |
| 142 | TF_RETURN_IF_ERROR(handle->Tensor(output_tensor)); |
| 143 | // actual_device may be nullptr, which implies local CPU. |
| 144 | if (expected_device == actual_device) return Status::OK(); |
| 145 | const string& expected_device_name = expected_device->attributes().name(); |
| 146 | if (actual_device == nullptr) { |
| 147 | if (!IsCPUDevice(expected_device)) { |
| 148 | return errors::Internal( |
| 149 | "Expected the py_func to return a Tensor backed by memory in ", |
| 150 | expected_device_name, |
| 151 | ", but is actually backed by local host memory. This is a bug."); |
| 152 | } |
| 153 | return Status::OK(); |
| 154 | } |
| 155 | // NOTE(ebrevdo): Here we could try comparing "actual_device_name" |
| 156 | // (actual_device->attributes()->name()) to expected_device_name and ensure |
| 157 | // they're the same. However, this comparison fails if we create a ClusterDef |
| 158 | // on localhost, mainly because the Device created by Eager code doesn't match |
| 159 | // the device created by a session. In this case, expected_device_name may |
| 160 | // contain "worker" but the Eager device name contains "localhost". Since we |
| 161 | // can't easily access the true underlying device of "worker" here, we are not |
| 162 | // able to perform a proper comparison. Furthermore, we can't check |
| 163 | // IsCPUDevice(actual_device) because the kernel's device may indeed be a |
| 164 | // GPU device (the python interpreter doesn't use it, however). |
| 165 | return Status::OK(); |
| 166 | } |
| 167 | |
| 168 | // Calls the registered py function through the trampoline. |
| 169 | Status DoCallPyFunc(PyCall* call, bool* out_log_on_error) { |
no test coverage detected