/* Copyright 2015 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #ifndef TENSORFLOW_CORE_FRAMEWORK_OP_H_ #define TENSORFLOW_CORE_FRAMEWORK_OP_H_ #include #include #include #include "tensorflow/core/framework/op_def_builder.h" #include "tensorflow/core/framework/op_def_util.h" #include "tensorflow/core/framework/selective_registration.h" #include "tensorflow/core/lib/core/errors.h" #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/lib/strings/str_util.h" #include "tensorflow/core/lib/strings/strcat.h" #include "tensorflow/core/platform/logging.h" #include "tensorflow/core/platform/macros.h" #include "tensorflow/core/platform/mutex.h" #include "tensorflow/core/platform/thread_annotations.h" #include "tensorflow/core/platform/types.h" namespace tensorflow { // Users that want to look up an OpDef by type name should take an // OpRegistryInterface. Functions accepting a // (const) OpRegistryInterface* may call LookUp() from multiple threads. class OpRegistryInterface { public: virtual ~OpRegistryInterface(); // Returns an error status and sets *op_reg_data to nullptr if no OpDef is // registered under that name, otherwise returns the registered OpDef. // Caller must not delete the returned pointer. virtual Status LookUp(const string& op_type_name, const OpRegistrationData** op_reg_data) const = 0; // Shorthand for calling LookUp to get the OpDef. Status LookUpOpDef(const string& op_type_name, const OpDef** op_def) const; }; // The standard implementation of OpRegistryInterface, along with a // global singleton used for registering ops via the REGISTER // macros below. Thread-safe. // // Example registration: // OpRegistry::Global()->Register( // [](OpRegistrationData* op_reg_data)->Status { // // Populate *op_reg_data here. // return Status::OK(); // }); class OpRegistry : public OpRegistryInterface { public: typedef std::function OpRegistrationDataFactory; OpRegistry(); ~OpRegistry() override; void Register(const OpRegistrationDataFactory& op_data_factory); Status LookUp(const string& op_type_name, const OpRegistrationData** op_reg_data) const override; // Fills *ops with all registered OpDefs (except those with names // starting with '_' if include_internal == false) sorted in // ascending alphabetical order. void Export(bool include_internal, OpList* ops) const; // Returns ASCII-format OpList for all registered OpDefs (except // those with names starting with '_' if include_internal == false). string DebugString(bool include_internal) const; // A singleton available at startup. static OpRegistry* Global(); // Get all registered ops. void GetRegisteredOps(std::vector* op_defs); // Get all `OpRegistrationData`s. void GetOpRegistrationData(std::vector* op_data); // Watcher, a function object. // The watcher, if set by SetWatcher(), is called every time an op is // registered via the Register function. The watcher is passed the Status // obtained from building and adding the OpDef to the registry, and the OpDef // itself if it was successfully built. A watcher returns a Status which is in // turn returned as the final registration status. typedef std::function Watcher; // An OpRegistry object has only one watcher. This interface is not thread // safe, as different clients are free to set the watcher any time. // Clients are expected to atomically perform the following sequence of // operations : // SetWatcher(a_watcher); // Register some ops; // op_registry->ProcessRegistrations(); // SetWatcher(nullptr); // Returns a non-OK status if a non-null watcher is over-written by another // non-null watcher. Status SetWatcher(const Watcher& watcher); // Process the current list of deferred registrations. Note that calls to // Export, LookUp and DebugString would also implicitly process the deferred // registrations. Returns the status of the first failed op registration or // Status::OK() otherwise. Status ProcessRegistrations() const; // Defer the registrations until a later call to a function that processes // deferred registrations are made. Normally, registrations that happen after // calls to Export, LookUp, ProcessRegistrations and DebugString are processed // immediately. Call this to defer future registrations. void DeferRegistrations(); // Clear the registrations that have been deferred. void ClearDeferredRegistrations(); private: // Ensures that all the functions in deferred_ get called, their OpDef's // registered, and returns with deferred_ empty. Returns true the first // time it is called. Prints a fatal log if any op registration fails. bool MustCallDeferred() const EXCLUSIVE_LOCKS_REQUIRED(mu_); // Calls the functions in deferred_ and registers their OpDef's // It returns the Status of the first failed op registration or Status::OK() // otherwise. Status CallDeferred() const EXCLUSIVE_LOCKS_REQUIRED(mu_); // Add 'def' to the registry with additional data 'data'. On failure, or if // there is already an OpDef with that name registered, returns a non-okay // status. Status RegisterAlreadyLocked(const OpRegistrationDataFactory& op_data_factory) const EXCLUSIVE_LOCKS_REQUIRED(mu_); mutable mutex mu_; // Functions in deferred_ may only be called with mu_ held. mutable std::vector deferred_ GUARDED_BY(mu_); // Values are owned. mutable std::unordered_map registry_ GUARDED_BY(mu_); mutable bool initialized_ GUARDED_BY(mu_); // Registry watcher. mutable Watcher watcher_ GUARDED_BY(mu_); }; // An adapter to allow an OpList to be used as an OpRegistryInterface. // // Note that shape inference functions are not passed in to OpListOpRegistry, so // it will return an unusable shape inference function for every op it supports; // therefore, it should only be used in contexts where this is okay. class OpListOpRegistry : public OpRegistryInterface { public: // Does not take ownership of op_list, *op_list must outlive *this. OpListOpRegistry(const OpList* op_list); ~OpListOpRegistry() override; Status LookUp(const string& op_type_name, const OpRegistrationData** op_reg_data) const override; private: // Values are owned. std::unordered_map index_; }; // Support for defining the OpDef (specifying the semantics of the Op and how // it should be created) and registering it in the OpRegistry::Global() // registry. Usage: // // REGISTER_OP("my_op_name") // .Attr(":") // .Attr(":=") // .Input(":") // .Input(":Ref()") // .Output(":") // .Doc(R"( // <1-line summary> // // : // : // )"); // // Note: .Doc() should be last. // For details, see the OpDefBuilder class in op_def_builder.h. namespace register_op { // OpDefBuilderWrapper is a templated class that is used in the REGISTER_OP // calls. This allows the result of REGISTER_OP to be used in chaining, as in // REGISTER_OP(a).Attr("...").Input("...");, while still allowing selective // registration to turn the entire call-chain into a no-op. template class OpDefBuilderWrapper; // Template specialization that forwards all calls to the contained builder. template <> class OpDefBuilderWrapper { public: OpDefBuilderWrapper(const char name[]) : builder_(name) {} OpDefBuilderWrapper& Attr(string spec) { builder_.Attr(std::move(spec)); return *this; } OpDefBuilderWrapper& Input(string spec) { builder_.Input(std::move(spec)); return *this; } OpDefBuilderWrapper& Output(string spec) { builder_.Output(std::move(spec)); return *this; } OpDefBuilderWrapper& SetIsCommutative() { builder_.SetIsCommutative(); return *this; } OpDefBuilderWrapper& SetIsAggregate() { builder_.SetIsAggregate(); return *this; } OpDefBuilderWrapper& SetIsStateful() { builder_.SetIsStateful(); return *this; } OpDefBuilderWrapper& SetAllowsUninitializedInput() { builder_.SetAllowsUninitializedInput(); return *this; } OpDefBuilderWrapper& Deprecated(int version, string explanation) { builder_.Deprecated(version, std::move(explanation)); return *this; } OpDefBuilderWrapper& Doc(string text) { builder_.Doc(std::move(text)); return *this; } OpDefBuilderWrapper& SetShapeFn( Status (*fn)(shape_inference::InferenceContext*)) { builder_.SetShapeFn(fn); return *this; } const ::tensorflow::OpDefBuilder& builder() const { return builder_; } private: mutable ::tensorflow::OpDefBuilder builder_; }; // Template specialization that turns all calls into no-ops. template <> class OpDefBuilderWrapper { public: constexpr OpDefBuilderWrapper(const char name[]) {} OpDefBuilderWrapper& Attr(StringPiece spec) { return *this; } OpDefBuilderWrapper& Input(StringPiece spec) { return *this; } OpDefBuilderWrapper& Output(StringPiece spec) { return *this; } OpDefBuilderWrapper& SetIsCommutative() { return *this; } OpDefBuilderWrapper& SetIsAggregate() { return *this; } OpDefBuilderWrapper& SetIsStateful() { return *this; } OpDefBuilderWrapper& SetAllowsUninitializedInput() { return *this; } OpDefBuilderWrapper& Deprecated(int, StringPiece) { return *this; } OpDefBuilderWrapper& Doc(StringPiece text) { return *this; } OpDefBuilderWrapper& SetShapeFn( Status (*fn)(shape_inference::InferenceContext*)) { return *this; } }; struct OpDefBuilderReceiver { // To call OpRegistry::Global()->Register(...), used by the // REGISTER_OP macro below. // Note: These are implicitly converting constructors. OpDefBuilderReceiver( const OpDefBuilderWrapper& wrapper); // NOLINT(runtime/explicit) constexpr OpDefBuilderReceiver(const OpDefBuilderWrapper&) { } // NOLINT(runtime/explicit) }; } // namespace register_op #define REGISTER_OP(name) REGISTER_OP_UNIQ_HELPER(__COUNTER__, name) #define REGISTER_OP_UNIQ_HELPER(ctr, name) REGISTER_OP_UNIQ(ctr, name) #define REGISTER_OP_UNIQ(ctr, name) \ static ::tensorflow::register_op::OpDefBuilderReceiver register_op##ctr \ TF_ATTRIBUTE_UNUSED = \ ::tensorflow::register_op::OpDefBuilderWrapper(name) // The `REGISTER_SYSTEM_OP()` macro acts as `REGISTER_OP()` except // that the op is registered unconditionally even when selective // registration is used. #define REGISTER_SYSTEM_OP(name) \ REGISTER_SYSTEM_OP_UNIQ_HELPER(__COUNTER__, name) #define REGISTER_SYSTEM_OP_UNIQ_HELPER(ctr, name) \ REGISTER_SYSTEM_OP_UNIQ(ctr, name) #define REGISTER_SYSTEM_OP_UNIQ(ctr, name) \ static ::tensorflow::register_op::OpDefBuilderReceiver register_op##ctr \ TF_ATTRIBUTE_UNUSED = \ ::tensorflow::register_op::OpDefBuilderWrapper(name) } // namespace tensorflow #endif // TENSORFLOW_CORE_FRAMEWORK_OP_H_