ResourceOpKernel是个有状态OP.是用来提供资源的。这个OP放在图中,能提供资源输出。
因此有创建资源和获取资源两个接口。 OP必须有container和shared_name两个属性
空container就使用默认container, 空shared_name意思是私有
CreateResource(T** resource)由子类实现。真正创建资源
// ResourceOpKernel<T> is a virtual base class for resource op implementing
// interface type T. The inherited op looks up the resource name (determined by
// ContainerInfo), and creates a new resource if necessary.
//
// Requirements:
// - Op must be marked as stateful.
// - Op must have `container` and `shared_name` attributes. Empty `container`
// means using the default container. Empty `shared_name` means private
// resource.
// - Subclass must override CreateResource().
// - Subclass is encouraged to override VerifyResource().
template <typename T>
class ResourceOpKernel : public OpKernel {
public:
explicit ResourceOpKernel(OpKernelConstruction* context) : OpKernel(context) {
has_resource_type_ = (context->output_type(0) == DT_RESOURCE);
if (!has_resource_type_) {
// The resource variant of the op may be placed on non-CPU devices, but
// this allocation is always on the host. Fortunately we don't need it in
// the resource case.
OP_REQUIRES_OK(context, context->allocate_temp(
DT_STRING, TensorShape({2}), &tensor_));
}
}
// The resource is deleted from the resource manager only when it is private
// to kernel. Ideally the resource should be deleted when it is no longer held
// by anyone, but it would break backward compatibility.
~ResourceOpKernel() override {
if (resource_ != nullptr) {
resource_->Unref();
if (cinfo_.resource_is_private_to_kernel()) {
if (!cinfo_.resource_manager()
->template Delete<T>(cinfo_.container(), cinfo_.name())
.ok()) {
// Do nothing; the resource can have been deleted by session resets.
}
}
}
}
void Compute(OpKernelContext* context) override TF_LOCKS_EXCLUDED(mu_) {
mutex_lock l(mu_);
//资源还没创建,就创建一个,插入到resource manager里
if (resource_ == nullptr) {
ResourceMgr* mgr = context->resource_manager();
OP_REQUIRES_OK(context, cinfo_.Init(mgr, def()));
T* resource;
OP_REQUIRES_OK(context,
mgr->LookupOrCreate<T>(
cinfo_.container(), cinfo_.name(), &resource,
[this](T** ret) TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
Status s = CreateResource(ret);
if (!s.ok() && *ret != nullptr) {
CHECK((*ret)->Unref());
}
return s;
}));
Status s = VerifyResource(resource);
if (TF_PREDICT_FALSE(!s.ok())) {
resource->Unref();
context->SetStatus(s);
return;
}
if (!has_resource_type_) {
auto h = tensor_.template flat<tstring>();
h(0) = cinfo_.container();
h(1) = cinfo_.name();
}
resource_ = resource;
}
//实际返回资源Handle
if (has_resource_type_) {
/*
Status MakeResourceHandleToOutput(OpKernelContext* context, int output_index,
const string& container, const string& name,
const TypeIndex& type_index) {
Tensor* handle;
TF_RETURN_IF_ERROR(
context->allocate_output(output_index, TensorShape({}), &handle));
handle->scalar<ResourceHandle>()() =
MakeResourceHandle(container, name, *context->device(), type_index);
return Status::OK();
}*/
OP_REQUIRES_OK(context, MakeResourceHandleToOutput(
context, 0, cinfo_.container(), cinfo_.name(),
TypeIndex::Make<T>()));
} else {
context->set_output_ref(0, &mu_, &tensor_);
}
}
protected:
// Variables accessible from subclasses.
mutex mu_;
ContainerInfo cinfo_ TF_GUARDED_BY(mu_);
T* resource_ TF_GUARDED_BY(mu_) = nullptr;
private:
// Must return a T descendant allocated with new that ResourceOpKernel will
// take ownership of.
virtual Status CreateResource(T** resource)
TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) = 0;
// During the first Compute(), resource is either created or looked up using
// shared_name. In the latter case, the resource found should be verified if
// it is compatible with this op's configuration. The verification may fail in
// cases such as two graphs asking queues of the same shared name to have
// inconsistent capacities.
virtual Status VerifyResource(T* resource) { return Status::OK(); }
Tensor tensor_ TF_GUARDED_BY(mu_);
// Is the output of the operator of type DT_RESOURCE?
bool has_resource_type_;
};
用法
作为图中的一个结点,执行时能创建资源,并插入到resource mgr中
TEST_F(ResourceOpKernelTest, SharedResource) {
const string shared_name = "shared_stub";
const int code = -201;
auto op = CreateOp(code, shared_name); //创建了一个资源OP。且有shared_name,所以op销毁不会销毁资源
ASSERT_TRUE(op != nullptr);
TF_EXPECT_OK(RunOpKernel(op.get())); //运行kernel,会创建资源
StubResource* resource; //mgr_里已经有创建好的资源了
TF_ASSERT_OK(mgr_.Lookup<StubResource>(mgr_.default_container(), shared_name,
&resource));
EXPECT_EQ(op->resource(), resource); // Check resource identity.
EXPECT_EQ(code, resource->code); // Check resource stored information.
resource->Unref();
// Destroy the op kernel. Expect the resource not to be released.
op = nullptr;
TF_ASSERT_OK(mgr_.Lookup<StubResource>(mgr_.default_container(), shared_name,
&resource));
resource->Unref();
}