1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #ifndef TENSORFLOW_CORE_FRAMEWORK_RESOURCE_MGR_H_
17 #define TENSORFLOW_CORE_FRAMEWORK_RESOURCE_MGR_H_
18
19 #include <memory>
20 #include <string>
21 #include <typeindex>
22 #include <typeinfo>
23 #include <unordered_map>
24
25 #include "tensorflow/core/framework/common_shape_fns.h"
26 #include "tensorflow/core/framework/op_kernel.h"
27 #include "tensorflow/core/framework/resource_handle.h"
28 #include "tensorflow/core/framework/tensor.h"
29 #include "tensorflow/core/framework/tensor_shape.h"
30 #include "tensorflow/core/framework/tensor_types.h"
31 #include "tensorflow/core/framework/type_index.h"
32 #include "tensorflow/core/lib/core/errors.h"
33 #include "tensorflow/core/lib/core/refcount.h"
34 #include "tensorflow/core/lib/core/status.h"
35 #include "tensorflow/core/lib/hash/hash.h"
36 #include "tensorflow/core/platform/logging.h"
37 #include "tensorflow/core/platform/macros.h"
38 #include "tensorflow/core/platform/mutex.h"
39 #include "tensorflow/core/platform/thread_annotations.h"
40
41 namespace tensorflow {
42
43 // A ResourceMgr instance keeps track of named and typed resources
44 // grouped into containers.
45 //
46 // Each resource must be represented as a sub-class of ResourceBase,
47 // which is reference counted explicitly. Each named resource is
48 // registered with ResourceMgr under a named "container" name. At any
49 // time, there is at most one instance of a resource given the container
50 // name, the resource type and the resource name.
51 //
52 // All resources for a given container can be dropped by one call of
53 // Cleanup().
54 //
55 // E.g.,
56 // struct MyVar : public ResourceBase {
57 // mutex mu;
58 // Tensor val;
59 // }
60 //
61 // ResourceMgr rm;
62 //
63 // // Create a var.
64 // MyVar* my_var = new MyVar;
65 // my_var->val = Tensor(DT_FLOAT, my_shape);
66 // my_var->val.flat<float>().setZeros(); // 0 initialized.
67 // ctx->SetStatus(rm.Create("my_container", "my_name", my_var));
68 //
69 // // += a variable.
70 // MyVar* my_var = nullptr;
71 // Status s = rm.Lookup("my_container", "my_name", &my_var);
72 // if (s.ok()) {
73 // my_var->val.flat<float>() += grad;
74 // }
75 // my_var->Unref(); // Or use ScopedUnref().
76 // ctx->SetStatus(s);
77 class ResourceBase : public core::RefCounted {
78 public:
79 // Returns a debug string for *this.
80 virtual string DebugString() const = 0;
81
82 // Returns memory used by this resource.
MemoryUsed()83 virtual int64 MemoryUsed() const { return 0; }
84 };
85
86 // Container used for per-step resources.
87 class ScopedStepContainer {
88 public:
89 // step_id: the unique ID of this step. Doesn't have to be sequential, just
90 // has to be unique.
91 // cleanup: callback to delete a container of this name.
92 // prefix: optional string prefix to disambiguate step containers.
ScopedStepContainer(const int64 step_id,std::function<void (const string &)> cleanup)93 ScopedStepContainer(const int64 step_id,
94 std::function<void(const string&)> cleanup)
95 : name_(strings::StrCat("__per_step_", step_id)), cleanup_(cleanup) {}
96
ScopedStepContainer(const int64 step_id,std::function<void (const string &)> cleanup,const string & prefix)97 ScopedStepContainer(const int64 step_id,
98 std::function<void(const string&)> cleanup,
99 const string& prefix)
100 : name_(strings::StrCat("__", prefix, "_per_step_", step_id)),
101 cleanup_(cleanup) {}
102
~ScopedStepContainer()103 ~ScopedStepContainer() { cleanup_(name_); }
104
name()105 const string& name() const { return name_; }
106
107 private:
108 const string name_;
109 const std::function<void(const string&)> cleanup_;
110 };
111
112 class ResourceMgr {
113 public:
114 ResourceMgr();
115 explicit ResourceMgr(const string& default_container);
116 ~ResourceMgr();
117
118 // Returns the default container name for *this.
default_container()119 const string& default_container() const { return default_container_; }
120
121 // Creates a resource "name" in the "container". The caller transfers
122 // the ownership of one ref on "resource" to *this
123 //
124 // REQUIRES: std::is_base_of<ResourceBase, T>
125 // REQUIRES: resource != nullptr.
126 template <typename T>
127 Status Create(const string& container, const string& name,
128 T* resource) TF_MUST_USE_RESULT;
129
130 // If "container" has a resource "name", returns it in "*resource" and
131 // the caller takes the ownership of one ref on "*resource".
132 //
133 // REQUIRES: std::is_base_of<ResourceBase, T>
134 // REQUIRES: resource != nullptr
135 template <typename T, bool use_dynamic_cast = false>
136 Status Lookup(const string& container, const string& name,
137 T** resource) const TF_MUST_USE_RESULT;
138
139 // Similar to Lookup, but looks up multiple resources at once, with only a
140 // single lock acquisition. If containers_and_names[i] is uninitialized
141 // then this function does not modify resources[i].
142 template <typename T, bool use_dynamic_cast = false>
143 Status LookupMany(absl::Span<std::pair<const string*, const string*> const>
144 containers_and_names,
145 std::vector<std::unique_ptr<T, core::RefCountDeleter>>*
146 resources) const TF_MUST_USE_RESULT;
147
148 // If "container" has a resource "name", returns it in
149 // "*resource". Otherwise, invokes creator() to create the resource.
150 // The caller takes the ownership of one ref on "*resource".
151 //
152 // WARNING: creator() must not call any methods on ResourceMgr during its
153 // execution, because a non-reentrant lock is held during the creator() call
154 // in order to guarantee atomicity of LookupOrCreate().
155 //
156 // REQUIRES: std::is_base_of<ResourceBase, T>
157 // REQUIRES: resource != nullptr
158 template <typename T, bool use_dynamic_cast = false>
159 Status LookupOrCreate(const string& container, const string& name,
160 T** resource,
161 std::function<Status(T**)> creator) TF_MUST_USE_RESULT;
162
163 // Deletes the resource "name" from the "container".
164 //
165 // REQUIRES: std::is_base_of<ResourceBase, T>
166 template <typename T>
167 Status Delete(const string& container, const string& name) TF_MUST_USE_RESULT;
168
169 // Deletes the resource pointed by "handle".
170 Status Delete(const ResourceHandle& handle) TF_MUST_USE_RESULT;
171
172 // Deletes all resources from the "container" and removes the container.
173 Status Cleanup(const string& container) TF_MUST_USE_RESULT;
174
175 // Deletes all resources in all containers.
176 void Clear();
177
178 // Returns a text description for all resources.
179 string DebugString() const;
180
181 private:
182 typedef std::pair<uint64, string> Key;
183 struct KeyHash {
operatorKeyHash184 std::size_t operator()(const Key& k) const {
185 return Hash64(k.second.data(), k.second.size(), k.first);
186 }
187 };
188 struct KeyEqual {
operatorKeyEqual189 bool operator()(const Key& x, const Key& y) const {
190 return (x.second == y.second) && (x.first == y.first);
191 }
192 };
193 typedef std::unordered_map<Key, ResourceBase*, KeyHash, KeyEqual> Container;
194
195 const string default_container_;
196 mutable mutex mu_;
197 std::unordered_map<string, Container*> containers_ GUARDED_BY(mu_);
198
199 template <typename T, bool use_dynamic_cast = false>
200 Status LookupInternal(const string& container, const string& name,
201 T** resource) const
202 SHARED_LOCKS_REQUIRED(mu_) TF_MUST_USE_RESULT;
203
204 Status DoCreate(const string& container, TypeIndex type, const string& name,
205 ResourceBase* resource)
206 EXCLUSIVE_LOCKS_REQUIRED(mu_) TF_MUST_USE_RESULT;
207
208 Status DoLookup(const string& container, TypeIndex type, const string& name,
209 ResourceBase** resource) const
210 SHARED_LOCKS_REQUIRED(mu_) TF_MUST_USE_RESULT;
211
212 Status DoDelete(const string& container, uint64 type_hash_code,
213 const string& resource_name,
214 const string& type_name) TF_MUST_USE_RESULT;
215 Status DoDelete(const string& container, TypeIndex type,
216 const string& resource_name) TF_MUST_USE_RESULT;
217
218 // Inserts the type name for 'hash_code' into the hash_code to type name map.
219 Status InsertDebugTypeName(uint64 hash_code, const string& type_name)
220 EXCLUSIVE_LOCKS_REQUIRED(mu_) TF_MUST_USE_RESULT;
221
222 // Returns the type name for the 'hash_code'.
223 // Returns "<unknown>" if a resource with such a type was never inserted into
224 // the container.
225 const char* DebugTypeName(uint64 hash_code) const
226 EXCLUSIVE_LOCKS_REQUIRED(mu_);
227
228 // Map from type hash_code to type name.
229 std::unordered_map<uint64, string> debug_type_names_ GUARDED_BY(mu_);
230
231 TF_DISALLOW_COPY_AND_ASSIGN(ResourceMgr);
232 };
233
234 // Makes a resource handle with the specified type for a given container /
235 // name.
236 ResourceHandle MakeResourceHandle(OpKernelContext* ctx, const string& container,
237 const string& name,
238 const TypeIndex& type_index);
239
240 template <typename T>
MakeResourceHandle(OpKernelContext * ctx,const string & container,const string & name)241 ResourceHandle MakeResourceHandle(OpKernelContext* ctx, const string& container,
242 const string& name) {
243 return MakeResourceHandle(ctx, container, name, MakeTypeIndex<T>());
244 }
245
246 Status MakeResourceHandleToOutput(OpKernelContext* context, int output_index,
247 const string& container, const string& name,
248 const TypeIndex& type_index);
249
250 template <typename T>
251 ResourceHandle MakePerStepResourceHandle(OpKernelContext* ctx,
252 const string& name);
253
254 // Returns a resource handle from a numbered op input.
255 const ResourceHandle& HandleFromInput(OpKernelContext* ctx, int input);
256 Status HandleFromInput(OpKernelContext* ctx, StringPiece input,
257 ResourceHandle* handle);
258
259 // Create a resource pointed by a given resource handle.
260 //
261 // If successful, the caller transfers the ownership of one ref on `resource` to
262 // `ctx->resource_mgr()`.
263 template <typename T>
264 Status CreateResource(OpKernelContext* ctx, const ResourceHandle& p, T* value);
265
266 // Looks up a resource pointed by a given resource handle.
267 //
268 // If the lookup is successful, the caller takes the ownership of one ref on
269 // `*value`, and must call its `Unref()` method when it has finished using it.
270 template <typename T, bool use_dynamic_cast = false>
271 Status LookupResource(OpKernelContext* ctx, const ResourceHandle& p, T** value);
272
273 // Looks up multiple resources pointed by a sequence of resource handles. If
274 // p[i] is uninitialized then values[i] is unmodified.
275 template <typename T>
276 Status LookupResources(
277 OpKernelContext* ctx, absl::Span<ResourceHandle const> p,
278 std::vector<std::unique_ptr<T, core::RefCountDeleter>>* values);
279
280 // Looks up or creates a resource.
281 //
282 // If successful, the caller takes the ownership of one ref on `*value`, and
283 // must call its `Unref()` method when it has finished using it. If the
284 // `creator` is invoked, its reference on the created resource is transferred
285 // to `ctx->resource_mgr()`.
286 template <typename T>
287 Status LookupOrCreateResource(OpKernelContext* ctx, const ResourceHandle& p,
288 T** value, std::function<Status(T**)> creator);
289
290 // Destroys a resource pointed by a given resource handle.
291 template <typename T>
292 Status DeleteResource(OpKernelContext* ctx, const ResourceHandle& p);
293
294 // Same as above, but uses the hash code of the type directly.
295 // The type name information will be missing in the debug output when the
296 // resource is not present in the container.
297 Status DeleteResource(OpKernelContext* ctx, const ResourceHandle& p);
298
299 // Policy helper to decide which container/shared_name to use for a
300 // stateful kernel that accesses shared resource.
301 class ContainerInfo {
302 public:
303 // Analyze the node attribute of 'ndef' and decides the container and
304 // resource name the kernel should use for accessing the shared
305 // resource.
306 //
307 // 'ndef' is expected to have node attribute "container" and
308 // "shared_name". Returns non-OK if they are not provided or they are
309 // invalid.
310 //
311 // The policy is as following:
312 // * If the attribute "container" is non-empty, it is used as is.
313 // Otherwise, uses the resource manager's default container.
314 // * If the attribute "shared_name" is non-empty, it is used as is.
315 // Otherwise, if "use_node_name_as_default" is true, the kernel's
316 // node name is used as the resource name. Otherwise, a string
317 // unique to this process is used.
318 Status Init(ResourceMgr* rmgr, const NodeDef& ndef,
319 bool use_node_name_as_default);
Init(ResourceMgr * rmgr,const NodeDef & ndef)320 Status Init(ResourceMgr* rmgr, const NodeDef& ndef) {
321 return Init(rmgr, ndef, false);
322 }
323
324 // The policy decides that the kernel should access the resource in
325 // resource_manager(), the resource is in the container() and its
326 // name is name(). If resource_is_private_to_kernel() is true, the
327 // kernel should delete the resource when the kernel is deleted.
resource_manager()328 ResourceMgr* resource_manager() const { return rmgr_; }
container()329 const string& container() const { return container_; }
name()330 const string& name() const { return name_; }
resource_is_private_to_kernel()331 bool resource_is_private_to_kernel() const {
332 return resource_is_private_to_kernel_;
333 }
334
335 // Returns a readable string for *this.
336 string DebugString() const;
337
338 private:
339 ResourceMgr* rmgr_ = nullptr;
340 string container_;
341 string name_;
342 bool resource_is_private_to_kernel_ = false;
343 };
344
345 // Helper for kernels to obtain 'resource' from the
346 // ctx->resource_manager().
347 //
348 // "input_name" specifies the kernel's ref input which gives a string
349 // tensor with two elements, which specifies the container and
350 // resource name.
351 //
352 // Returns OK if the resource is found and transfers one ref of
353 // *resource to the caller. Otherwise, returns an error.
354 template <typename T>
355 Status GetResourceFromContext(OpKernelContext* ctx, const string& input_name,
356 T** resource);
357
358 // Utility op kernel to check if a handle to resource type T is initialized.
359 template <typename T>
360 class IsResourceInitialized : public OpKernel {
361 public:
IsResourceInitialized(OpKernelConstruction * c)362 explicit IsResourceInitialized(OpKernelConstruction* c) : OpKernel(c) {}
363
364 void Compute(OpKernelContext* ctx) override;
365 };
366
367 // Registers an op which produces just a resource handle to a resource of the
368 // specified type. The type will be a part of the generated op name.
369 // TODO(apassos): figure out how to get non-cpu-allocated tensors to work
370 // through constant folding so this doesn't have to be marked as stateful.
371 #define REGISTER_RESOURCE_HANDLE_OP(Type) \
372 REGISTER_OP(#Type "HandleOp") \
373 .Attr("container: string = ''") \
374 .Attr("shared_name: string = ''") \
375 .Output("resource: resource") \
376 .SetIsStateful() \
377 .SetShapeFn(tensorflow::shape_inference::ScalarShape)
378
379 // Utility op kernel to produce a handle to a resource of type T.
380 template <typename T>
381 class ResourceHandleOp : public OpKernel {
382 public:
383 explicit ResourceHandleOp(OpKernelConstruction* context);
384
385 void Compute(OpKernelContext* ctx) override;
386
IsExpensive()387 bool IsExpensive() override { return false; }
388
389 private:
390 string container_;
391 string name_;
392 mutex mutex_;
393 Tensor resource_;
394 std::atomic<bool> initialized_{false};
395 };
396
397 // Utility op kernel to produce a handle to a resource of type T.
398 template <typename T>
399 class ResourceHandlesOp : public OpKernel {
400 public:
401 explicit ResourceHandlesOp(OpKernelConstruction* context);
402
403 void Compute(OpKernelContext* ctx) override;
404
IsExpensive()405 bool IsExpensive() override { return false; }
406
407 private:
408 std::vector<string> containers_;
409 std::vector<string> names_;
410 mutex mutex_;
411 std::vector<Tensor> resources_;
412 std::atomic<bool> initialized_{false};
413 };
414
415 Status ResourceHandlesShape(shape_inference::InferenceContext* c);
416
417 // Registers a kernel for an op which produces a handle to a resource of the
418 // specified type.
419 #define REGISTER_RESOURCE_HANDLE_KERNEL(Type) \
420 REGISTER_KERNEL_BUILDER(Name(#Type "HandleOp").Device(DEVICE_CPU), \
421 ResourceHandleOp<Type>)
422
423 // Implementation details below.
424
425 template <typename T>
CheckDeriveFromResourceBase()426 void CheckDeriveFromResourceBase() {
427 static_assert(std::is_base_of<ResourceBase, T>::value,
428 "T must derive from ResourceBase");
429 }
430
431 template <typename T>
Create(const string & container,const string & name,T * resource)432 Status ResourceMgr::Create(const string& container, const string& name,
433 T* resource) {
434 CheckDeriveFromResourceBase<T>();
435 CHECK(resource != nullptr);
436 mutex_lock l(mu_);
437 return DoCreate(container, MakeTypeIndex<T>(), name, resource);
438 }
439
440 template <typename T, bool use_dynamic_cast>
Lookup(const string & container,const string & name,T ** resource)441 Status ResourceMgr::Lookup(const string& container, const string& name,
442 T** resource) const {
443 CheckDeriveFromResourceBase<T>();
444 tf_shared_lock l(mu_);
445 return LookupInternal<T, use_dynamic_cast>(container, name, resource);
446 }
447
448 template <typename T, bool use_dynamic_cast>
LookupMany(absl::Span<std::pair<const string *,const string * > const> containers_and_names,std::vector<std::unique_ptr<T,core::RefCountDeleter>> * resources)449 Status ResourceMgr::LookupMany(
450 absl::Span<std::pair<const string*, const string*> const>
451 containers_and_names,
452 std::vector<std::unique_ptr<T, core::RefCountDeleter>>* resources) const {
453 CheckDeriveFromResourceBase<T>();
454 tf_shared_lock l(mu_);
455 resources->resize(containers_and_names.size());
456 for (size_t i = 0; i < containers_and_names.size(); ++i) {
457 T* resource;
458 Status s = LookupInternal<T, use_dynamic_cast>(
459 *containers_and_names[i].first, *containers_and_names[i].second,
460 &resource);
461 if (s.ok()) {
462 (*resources)[i].reset(resource);
463 }
464 }
465 return Status::OK();
466 }
467
468 // Simple wrapper to allow conditional dynamic / static casts.
469 template <typename T, bool use_dynamic_cast>
470 struct TypeCastFunctor {
CastTypeCastFunctor471 static T* Cast(ResourceBase* r) { return static_cast<T*>(r); }
472 };
473
474 template <typename T>
475 struct TypeCastFunctor<T, true> {
476 static T* Cast(ResourceBase* r) { return dynamic_cast<T*>(r); }
477 };
478
479 template <typename T, bool use_dynamic_cast>
480 Status ResourceMgr::LookupInternal(const string& container, const string& name,
481 T** resource) const {
482 ResourceBase* found = nullptr;
483 Status s = DoLookup(container, MakeTypeIndex<T>(), name, &found);
484 if (s.ok()) {
485 // It's safe to down cast 'found' to T* since
486 // typeid(T).hash_code() is part of the map key.
487 *resource = TypeCastFunctor<T, use_dynamic_cast>::Cast(found);
488 }
489 return s;
490 }
491
492 template <typename T, bool use_dynamic_cast>
493 Status ResourceMgr::LookupOrCreate(const string& container, const string& name,
494 T** resource,
495 std::function<Status(T**)> creator) {
496 CheckDeriveFromResourceBase<T>();
497 *resource = nullptr;
498 Status s;
499 {
500 tf_shared_lock l(mu_);
501 s = LookupInternal<T, use_dynamic_cast>(container, name, resource);
502 if (s.ok()) return s;
503 }
504 mutex_lock l(mu_);
505 s = LookupInternal<T, use_dynamic_cast>(container, name, resource);
506 if (s.ok()) return s;
507 TF_RETURN_IF_ERROR(creator(resource));
508 s = DoCreate(container, MakeTypeIndex<T>(), name, *resource);
509 if (!s.ok()) {
510 return errors::Internal("LookupOrCreate failed unexpectedly");
511 }
512 (*resource)->Ref();
513 return s;
514 }
515
516 template <typename T>
517 Status ResourceMgr::Delete(const string& container, const string& name) {
518 CheckDeriveFromResourceBase<T>();
519 return DoDelete(container, MakeTypeIndex<T>(), name);
520 }
521
522 template <typename T>
523 Status GetResourceFromContext(OpKernelContext* ctx, const string& input_name,
524 T** resource) {
525 DataType dtype;
526 TF_RETURN_IF_ERROR(ctx->input_dtype(input_name, &dtype));
527 if (dtype == DT_RESOURCE) {
528 const Tensor* handle;
529 TF_RETURN_IF_ERROR(ctx->input(input_name, &handle));
530 return LookupResource(ctx, handle->scalar<ResourceHandle>()(), resource);
531 }
532 string container;
533 string shared_name;
534 {
535 mutex* mu;
536 TF_RETURN_IF_ERROR(ctx->input_ref_mutex(input_name, &mu));
537 mutex_lock l(*mu);
538 Tensor tensor;
539 TF_RETURN_IF_ERROR(ctx->mutable_input(input_name, &tensor, true));
540 if (tensor.NumElements() != 2) {
541 return errors::InvalidArgument(
542 "Resource handle must have 2 elements, but had shape: ",
543 tensor.shape().DebugString());
544 }
545 container = tensor.flat<string>()(0);
546 shared_name = tensor.flat<string>()(1);
547 }
548 return ctx->resource_manager()->Lookup(container, shared_name, resource);
549 }
550
551 template <typename T>
552 ResourceHandle MakePerStepResourceHandle(OpKernelContext* ctx,
553 const string& name) {
554 return MakeResourceHandle<T>(ctx, ctx->step_container()->name(), name);
555 }
556
557 namespace internal {
558
559 Status ValidateDevice(OpKernelContext* ctx, const ResourceHandle& p);
560
561 template <typename T>
562 Status ValidateDeviceAndType(OpKernelContext* ctx, const ResourceHandle& p) {
563 TF_RETURN_IF_ERROR(internal::ValidateDevice(ctx, p));
564 auto type_index = MakeTypeIndex<T>();
565 if (type_index.hash_code() != p.hash_code()) {
566 return errors::InvalidArgument(
567 "Trying to access resource using the wrong type. Expected ",
568 p.maybe_type_name(), " got ", type_index.name());
569 }
570 return Status::OK();
571 }
572
573 } // namespace internal
574
575 template <typename T>
576 Status CreateResource(OpKernelContext* ctx, const ResourceHandle& p, T* value) {
577 TF_RETURN_IF_ERROR(internal::ValidateDeviceAndType<T>(ctx, p));
578 return ctx->resource_manager()->Create(p.container(), p.name(), value);
579 }
580
581 template <typename T, bool use_dynamic_cast>
582 Status LookupResource(OpKernelContext* ctx, const ResourceHandle& p,
583 T** value) {
584 TF_RETURN_IF_ERROR(internal::ValidateDeviceAndType<T>(ctx, p));
585 return ctx->resource_manager()->Lookup<T, use_dynamic_cast>(p.container(),
586 p.name(), value);
587 }
588
589 template <typename T>
590 Status LookupResources(
591 OpKernelContext* ctx, absl::Span<ResourceHandle const* const> p,
592 std::vector<std::unique_ptr<T, core::RefCountDeleter>>* values) {
593 std::vector<std::pair<const string*, const string*>> containers_and_names(
594 p.size());
595 for (size_t i = 0; i < p.size(); ++i) {
596 TF_RETURN_IF_ERROR(internal::ValidateDeviceAndType<T>(ctx, *p[i]));
597 containers_and_names[i] = {&p[i]->container(), &p[i]->name()};
598 }
599 return ctx->resource_manager()->LookupMany(containers_and_names, values);
600 }
601
602 template <typename T>
603 Status LookupOrCreateResource(OpKernelContext* ctx, const ResourceHandle& p,
604 T** value, std::function<Status(T**)> creator) {
605 TF_RETURN_IF_ERROR(internal::ValidateDeviceAndType<T>(ctx, p));
606 return ctx->resource_manager()->LookupOrCreate(p.container(), p.name(), value,
607 creator);
608 }
609
610 template <typename T>
611 Status DeleteResource(OpKernelContext* ctx, const ResourceHandle& p) {
612 TF_RETURN_IF_ERROR(internal::ValidateDeviceAndType<T>(ctx, p));
613 return ctx->resource_manager()->Delete<T>(p.container(), p.name());
614 }
615
616 Status DeleteResource(OpKernelContext* ctx, const ResourceHandle& p);
617
618 template <typename T>
619 void IsResourceInitialized<T>::Compute(OpKernelContext* ctx) {
620 Tensor* output;
621 OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {}, &output));
622 T* object;
623 bool found;
624 if (LookupResource(ctx, HandleFromInput(ctx, 0), &object).ok()) {
625 found = true;
626 object->Unref();
627 } else {
628 found = false;
629 }
630
631 output->flat<bool>()(0) = found;
632 }
633
634 template <typename T>
635 ResourceHandleOp<T>::ResourceHandleOp(OpKernelConstruction* context)
636 : OpKernel(context) {
637 OP_REQUIRES_OK(context, context->GetAttr("container", &container_));
638 OP_REQUIRES_OK(context, context->GetAttr("shared_name", &name_));
639 }
640
641 template <typename T>
642 void ResourceHandleOp<T>::Compute(OpKernelContext* ctx) {
643 if (name_ == ResourceHandle::ANONYMOUS_NAME) {
644 AllocatorAttributes attr;
645 attr.set_on_host(true);
646 Tensor handle;
647 OP_REQUIRES_OK(
648 ctx, ctx->allocate_temp(DT_RESOURCE, TensorShape({}), &handle, attr));
649 handle.scalar<ResourceHandle>()() =
650 MakeResourceHandle<T>(ctx, container_, name_);
651 ctx->set_output(0, handle);
652 } else {
653 if (!initialized_.load()) {
654 mutex_lock ml(mutex_);
655 // Checking again to see if another thread has initialized the resource.
656 if (!initialized_.load()) {
657 AllocatorAttributes attr;
658 attr.set_on_host(true);
659 OP_REQUIRES_OK(ctx, ctx->allocate_temp(DT_RESOURCE, TensorShape({}),
660 &resource_, attr));
661 resource_.scalar<ResourceHandle>()() =
662 MakeResourceHandle<T>(ctx, container_, name_);
663 initialized_.store(true);
664 }
665 }
666 ctx->set_output(0, resource_);
667 }
668 }
669
670 template <typename T>
671 ResourceHandlesOp<T>::ResourceHandlesOp(OpKernelConstruction* context)
672 : OpKernel(context) {
673 int n;
674 OP_REQUIRES_OK(context, context->GetAttr("N", &n));
675 OP_REQUIRES_OK(context, context->GetAttr("containers", &containers_));
676 OP_REQUIRES_OK(context, context->GetAttr("shared_names", &names_));
677 OP_REQUIRES(
678 context, containers_.size() == n,
679 errors::InvalidArgument("Number of containers (", containers_.size(),
680 ") must be equal to N (", n, ")"));
681 OP_REQUIRES(context, names_.size() == n,
682 errors::InvalidArgument("Number of names (", containers_.size(),
683 ") must be equal to N (", n, ")"));
684 resources_.resize(n);
685 }
686
687 template <typename T>
688 void ResourceHandlesOp<T>::Compute(OpKernelContext* ctx) {
689 if (!initialized_.load()) {
690 mutex_lock ml(mutex_);
691 // Checking again to see if another thread has initialized the resource.
692 if (!initialized_.load()) {
693 AllocatorAttributes attr;
694 attr.set_on_host(true);
695 for (size_t i = 0; i < resources_.size(); ++i) {
696 OP_REQUIRES_OK(ctx, ctx->allocate_temp(DT_RESOURCE, TensorShape({}),
697 &resources_[i], attr));
698 ResourceHandle h =
699 MakeResourceHandle<T>(ctx, containers_[i], names_[i]);
700 resources_[i].template scalar<ResourceHandle>()() = h;
701 }
702 initialized_.store(true);
703 }
704 }
705 for (size_t i = 0; i < resources_.size(); ++i) {
706 ctx->set_output(i, resources_[i]);
707 }
708 }
709
710 } // end namespace tensorflow
711
712 #endif // TENSORFLOW_CORE_FRAMEWORK_RESOURCE_MGR_H_
713