HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyLock.h
Go to the documentation of this file.
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_PY_LOCK_H
25 #define PXR_BASE_TF_PY_LOCK_H
26 
27 #include "pxr/pxr.h"
28 
29 #ifdef PXR_PYTHON_SUPPORT_ENABLED
30 
32 
33 #include "pxr/base/tf/api.h"
34 
36 
37 /// \class TfPyLock
38 ///
39 /// Convenience class for accessing the Python Global Interpreter Lock.
40 ///
41 /// The Python API is not thread-safe. Accessing the Python API from outside
42 /// the context of Python execution requires extra care when multiple threads
43 /// may be present. There are various schemes for how this should be done,
44 /// and the conventions have been changing with Python versions through 2.X.
45 ///
46 /// This class provides a convenient and centralized location for managing the
47 /// Python Global Interpreter Lock and related Python Thread State.
48 ///
49 /// The easiest way to use this class is to simply create a local variable in
50 /// any function that will access the Python API. Upon construction, this will
51 /// acquire the Python lock and establish the correct thread state for the
52 /// caller. Upon exit from the current scope, when the instance is destroyed,
53 /// the thread state will be restored and the lock will be released.
54 ///
55 /// \code
56 /// void MyFunc()
57 /// {
58 /// TfPyLock dummy;
59 /// ...(access Python API)...
60 /// }
61 /// \endcode
62 ///
63 /// If you need to temporarily release the lock during execution, (to perform
64 /// blocking I/O for example), you can call Release() explicitly, then call
65 /// Acquire() again to reclaim the lock.
66 ///
67 /// \code
68 /// void MyFunc()
69 /// {
70 /// TfPyLock pyLock;
71 /// ...(access Python API)...
72 /// pyLock.Release(); // let other threads run while we're blocked
73 /// ...(some blocking I/O or long running operation)...
74 /// pyLock.Acquire();
75 /// ...(more access to Python API)...
76 /// }
77 /// \endcode
78 ///
79 /// Note that it IS EXPLICITLY OK to recursively create instances of this
80 /// class, and thus recursively acquire the GIL and thread state. It is NOT OK
81 /// to recursively attempt to Acquire() the same instance, that will have no
82 /// effect and will generate a diagnostic warning.
83 ///
84 /// This class also provides an exception-safe way to release the GIL
85 /// temporarily for blocking calls, like Py_BEGIN/END_ALLOW_THREADS in the
86 /// Python C API.
87 ///
88 /// \code
89 /// void MyFunc()
90 /// {
91 /// TfPyLock lock;
92 /// ...(access Python API)...
93 /// lock.BeginAllowThreads(); // totally unlock the GIL temporarily.
94 /// ...(some blocking I/O or long running operation)...
95 /// lock.EndAllowThreads();
96 /// ...(more access to Python API)...
97 /// }
98 /// \endcode
99 ///
100 /// This looks similar to the above example using \a Release(), but it is
101 /// different. The Python lock is recursive, so the call to \a Release() is
102 /// not guaranteed to actually release the lock, it just releases the deepest
103 /// lock. In contrast \a BeginAllowThreads() will fully unlock the GIL so that
104 /// other threads can run temporarily regardless of how many times the lock is
105 /// recursively taken.
106 ///
107 /// The valid states and transitions for this class are as follows.
108 ///
109 /// State Valid Transitions
110 /// --------------------------------------------------------------
111 /// Released Acquire() -> Acquired
112 /// Acquired Release() -> Released, BeginAllowThreads() -> AllowsThreads
113 /// AllowsThreads EndAllowThreads() -> Acquired
114 ///
115 /// Note that upon construction the class is in the Acquired state. Upon
116 /// destruction, the class will move to the Released state.
117 ///
118 /// \warning Instances of this class should only be used as automatic (stack)
119 /// variables, or in thread local storage. DO NOT create a single instance
120 /// that could be shared across multiple threads.
121 ///
122 class TfPyLock {
123 public:
124  /// Acquires the Python GIL and swaps in callers thread state.
125  TF_API TfPyLock();
126 
127  /// Releases Python GIL and restores prior threads state.
128  TF_API ~TfPyLock();
129 
130  /// (Re)acquires GIL and thread state, if previously released.
131  TF_API void Acquire();
132 
133  /// Explicitly releases GIL and thread state.
134  TF_API void Release();
135 
136  /// Unlock the GIL temporarily to allow other threads to use python.
137  /// Typically this is used to unblock threads during operations like
138  /// blocking I/O. The lock must be acquired when called.
139  TF_API void BeginAllowThreads();
140 
141  /// End allowing other threads, reacquiring the lock state.
142  /// \a BeginAllowThreads must have been successfully called first.
143  TF_API void EndAllowThreads();
144 
145 private:
146  // Non-acquiring constructor for TfPyEnsureGILUnlockedObj's use.
147  friend struct TfPyEnsureGILUnlockedObj;
148  enum _UnlockedTag { _ConstructUnlocked };
149  explicit TfPyLock(_UnlockedTag);
150 
151  PyGILState_STATE _gilState;
152  PyThreadState *_savedState;
153  bool _acquired:1;
154  bool _allowingThreads:1;
155 };
156 
157 // Helper class for TF_PY_ALLOW_THREADS_IN_SCOPE()
158 struct TfPyEnsureGILUnlockedObj
159 {
160  // Do nothing if the current thread does not have the GIL, otherwise unlock
161  // the GIL, and relock upon destruction.
162  TF_API TfPyEnsureGILUnlockedObj();
163 private:
164  TfPyLock _lock;
165 };
166 
167 /// If the current thread of execution has the python GIL, release it,
168 /// allowing python threads to run, then upon leaving the current scope
169 /// reacquire the python GIL. Otherwise, do nothing.
170 ///
171 /// For example:
172 /// \code
173 /// {
174 /// TF_PY_ALLOW_THREADS_IN_SCOPE();
175 /// // ... long running or blocking operation ...
176 /// }
177 /// \endcode
178 ///
179 /// This is functionally similar to the following, except that it does nothing
180 /// in case the current thread of execution does not have the GIL.
181 ///
182 /// \code
183 /// {
184 /// TfPyLock lock; lock.BeginAllowThreads();
185 /// // ... long running or blocking operation ...
186 /// }
187 /// \endcode
188 ///
189 /// \hideinitializer
190 #define TF_PY_ALLOW_THREADS_IN_SCOPE() \
191  TfPyEnsureGILUnlockedObj __py_lock_allow_threads__
192 
194 
195 #else
196 
197 // When python is disabled, we stub this macro out to nothing.
198 #define TF_PY_ALLOW_THREADS_IN_SCOPE()
199 
200 #endif // PXR_PYTHON_SUPPORT_ENABLED
201 
202 #endif // PXR_BASE_TF_PY_LOCK_H
#define TF_API
Definition: api.h:40
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91