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