1 // 2 // Copyright 2016 Pixar 3 // 4 // Licensed under the Apache License, Version 2.0 (the "Apache License") 5 // with the following modification; you may not use this file except in 6 // compliance with the Apache License and the following modification to it: 7 // Section 6. Trademarks. is deleted and replaced with: 8 // 9 // 6. Trademarks. This License does not grant permission to use the trade 10 // names, trademarks, service marks, or product names of the Licensor 11 // and its affiliates, except as required to comply with Section 4(c) of 12 // the License and to reproduce the content of the NOTICE file. 13 // 14 // You may obtain a copy of the Apache License at 15 // 16 // http://www.apache.org/licenses/LICENSE-2.0 17 // 18 // Unless required by applicable law or agreed to in writing, software 19 // distributed under the Apache License with the above modification is 20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21 // KIND, either express or implied. See the Apache License for the specific 22 // language governing permissions and limitations under the Apache License. 23 // 24 #ifndef PXR_BASE_TF_ERROR_MARK_H 25 #define PXR_BASE_TF_ERROR_MARK_H 26 27 /// \file tf/errorMark.h 28 29 #include "pxr/pxr.h" 30 #include "pxr/base/tf/diagnosticMgr.h" 31 #include "pxr/base/tf/errorTransport.h" 32 #include "pxr/base/tf/api.h" 33 34 #include <boost/noncopyable.hpp> 35 36 PXR_NAMESPACE_OPEN_SCOPE 37 38 /// \class TfErrorMark 39 /// \ingroup group_tf_TfError 40 /// 41 /// Class used to record the end of the error-list. 42 /// 43 /// See \ref page_tf_TfError for a detailed description. 44 /// 45 /// A \c TfErrorMark is used as follows: 46 /// \code 47 /// TfErrorMark m; 48 /// 49 /// m.SetMark(); // (A) 50 /// ... ; 51 /// ... ; 52 /// // (B) 53 /// if (!m.IsClean()) { 54 /// // errors occurred between (A) and (B) 55 /// } 56 /// \endcode 57 /// 58 /// Another common pattern is 59 /// \code 60 /// TfErrorMark m; 61 /// if (TF_HAS_ERRORS(m, expr)) { 62 /// // handle errors; 63 /// } 64 /// \endcode 65 /// 66 class TfErrorMark : boost::noncopyable 67 { 68 public: 69 70 typedef TfDiagnosticMgr::ErrorIterator Iterator; 71 72 /// Default constructor. 73 /// 74 /// The default constructor automatically calls \c SetMark() at the point 75 /// of declaration. 76 TF_API TfErrorMark(); 77 78 /// Destroy this ErrorMark. 79 /// 80 /// If this is the last ErrorMark on this thread of execution and there 81 /// are pending errors, this will report them via the diagnostic delegate 82 /// (if one is installed) otherwise by printing to stderr. 83 TF_API ~TfErrorMark(); 84 85 /// Record future errors. 86 /// 87 /// \c SetMark() arranges to record future errors in \c *this. SetMark()88 inline void SetMark() { 89 _mark = TfDiagnosticMgr::GetInstance()._nextSerial; 90 } 91 92 /// Return true if no new errors were posted in this thread since the last 93 /// call to \c SetMark(), false otherwise. 94 /// 95 /// When no threads are issuing errors the cost of this function is an 96 /// atomic integer read and comparison. Otherwise thread-specific data is 97 /// accessed to make the determination. Thus, this function is fast when 98 /// diagnostics are not being issued. IsClean()99 inline bool IsClean() const { 100 TfDiagnosticMgr &mgr = TfDiagnosticMgr::GetInstance(); 101 return _mark >= mgr._nextSerial || _IsCleanImpl(mgr); 102 } 103 104 /// Remove all errors in this mark from the error system. Return true if 105 /// any errors were cleared, false if there were no errors in this mark. 106 /// 107 /// Clear all errors contained in this mark from the error system. 108 /// Subsequently, these errors will be considered handled. Clear()109 inline bool Clear() const { 110 TfDiagnosticMgr &mgr = TfDiagnosticMgr::GetInstance(); 111 auto b = GetBegin(), e = mgr.GetErrorEnd(); 112 if (b != e) { 113 mgr.EraseRange(b, e); 114 return true; 115 } 116 return false; 117 } 118 119 /// Remove all errors in this mark fom the error system and return them in 120 /// a TfErrorTransport. 121 /// 122 /// This can be used to transfer errors from one thread to another. See 123 /// TfErrorTransport for more information. As with Clear(), all the 124 /// removed errors are considered handled for this thread. See also 125 /// TransportTo(). Transport()126 inline TfErrorTransport Transport() const { 127 TfDiagnosticMgr &mgr = TfDiagnosticMgr::GetInstance(); 128 return TfErrorTransport(mgr._errorList.local(), 129 GetBegin(), mgr.GetErrorEnd()); 130 } 131 132 /// Remove all errors in this mark fom the error system and return them in 133 /// a TfErrorTransport. 134 /// 135 /// This is a variant of Transport(). Instead of returning a new 136 /// TfErrorTransport object it fills an existing one. TransportTo(TfErrorTransport & dest)137 inline void TransportTo(TfErrorTransport &dest) const { 138 Transport().swap(dest); 139 } 140 141 /// Return an iterator to the first error added to the error list after 142 /// \c SetMark(). 143 /// 144 /// If there are no errors on the error list that were not already present 145 /// when \c SetMark() was called, the iterator returned is equal to the 146 /// iterator returned by \c TfDiagnosticMgr::GetErrorEnd(). Otherwise, the 147 /// iterator points to the earliest error added to the list since 148 /// \c SetMark() was called. 149 /// 150 /// This function takes O(n) time where n is the number of errors from the 151 /// end of the list to the mark i.e. \c GetMark() walks the list from the 152 /// end until it finds the mark and then returns an iterator to that spot. 153 /// 154 /// If \c nErrors is non-NULL, then \c *nErrors is set to the number of 155 /// errors between the returned iterator and the end of the list. 156 Iterator GetBegin(size_t *nErrors = 0) const { 157 return 158 TfDiagnosticMgr::GetInstance()._GetErrorMarkBegin(_mark, nErrors); 159 } 160 161 /// Return an iterator past the last error in the error system. 162 /// 163 /// This iterator is always equivalent to the iterator returned by \c 164 /// TfDiagnosticMgr::GetErrorEnd(). GetEnd()165 Iterator GetEnd() const { 166 return TfDiagnosticMgr::GetInstance().GetErrorEnd(); 167 } 168 169 /// Equivalent to GetBegin() begin()170 Iterator begin() const { return GetBegin(); } 171 172 /// Equivalent to GetEnd() end()173 Iterator end() const { return GetEnd(); } 174 175 private: 176 friend class TfDiagnosticMgr; 177 178 // Helper to check if the _mark identifies any errors present on the 179 // thread-local error list. 180 TF_API bool _IsCleanImpl(TfDiagnosticMgr &mgr) const; 181 182 void _ReportErrors(TfDiagnosticMgr &mgr) const; 183 184 size_t _mark; 185 }; 186 187 188 /// Convenience macro to check if errors occurred. 189 /// 190 /// This macro is equivalent to 191 /// \code 192 /// (marker.SetMark(), (expr), !marker.IsClean()) 193 /// \endcode 194 /// 195 /// which enables it to be used as an expression: 196 /// \code 197 /// if (TF_HAS_ERRORS(m, expr)) 198 /// // cope! 199 /// \endcode 200 /// 201 /// \ingroup group_tf_TfError 202 /// \hideinitializer 203 #define TF_HAS_ERRORS(marker, expr) \ 204 (marker.SetMark(), (expr), !marker.IsClean()) 205 206 /// Report current TfErrorMark instances and the stack traces that created 207 /// them to stdout for debugging purposes. 208 /// 209 /// To call this function, set _enableTfErrorMarkStackTraces in errorMark.cpp 210 /// and enable the TF_ERROR_MARK_TRACKING TfDebug code. 211 /// 212 /// \ingroup group_tf_TfError 213 TF_API 214 void TfReportActiveErrorMarks(); 215 216 PXR_NAMESPACE_CLOSE_SCOPE 217 218 #endif // PXR_BASE_TF_ERROR_MARK_H 219