• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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