Skip to content

Commit 2b7d99f

Browse files
committed
capture the legacy tbb abi modifications as a patch
The bundled oneTBB carries local declarations and a definition of the old ABI's task_scheduler_observer, added in c276b03 so that binaries built against RcppParallel 5.x keep working. Neither had a file in patches/, and tools/tbb/update-tbb.R wipes src/tbb wholesale -- so the next oneTBB bump would have dropped them silently, breaking the Windows tbb.dll stub in a way that is hard to trace back. Verified by applying the patch to a pristine oneTBB 2022.0.0 tree and confirming the result matches what we ship.
1 parent d3deeb3 commit 2b7d99f

1 file changed

Lines changed: 173 additions & 0 deletions

File tree

patches/legacy_tbb_abi.diff

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
--- a/src/tbb/include/oneapi/tbb/task_scheduler_observer.h
2+
+++ b/src/tbb/include/oneapi/tbb/task_scheduler_observer.h
3+
@@ -17,10 +17,13 @@
4+
#ifndef __TBB_task_scheduler_observer_H
5+
#define __TBB_task_scheduler_observer_H
6+
7+
+#include <atomic>
8+
+
9+
#include "detail/_namespace_injection.h"
10+
+
11+
#include "task_arena.h"
12+
-#include <atomic>
13+
14+
+
15+
namespace tbb {
16+
namespace detail {
17+
18+
@@ -112,5 +115,128 @@
19+
}
20+
} // namespace tbb
21+
22+
+
23+
+// Provided for backwards compatibility. This is a local addition, not part of
24+
+// upstream oneTBB, so the tbb.dll stub built by src/install.libs.R must be
25+
+// able to tell whether the TBB headers in use supply these declarations (the
26+
+// bundled copy does) or whether it has to declare them itself (as with the
27+
+// pristine headers shipped by Rtools).
28+
+#define __TBB_LEGACY_TASK_SCHEDULER_OBSERVER_PROVIDED 1
29+
+
30+
+namespace tbb {
31+
+namespace interface6 {
32+
+class task_scheduler_observer;
33+
+}
34+
+namespace internal {
35+
+
36+
+class task_scheduler_observer_v3 {
37+
+ friend class tbb::detail::r1::observer_proxy;
38+
+ friend class tbb::detail::r1::observer_list;
39+
+ friend class interface6::task_scheduler_observer;
40+
+
41+
+ //! Pointer to the proxy holding this observer.
42+
+ /** Observers are proxied by the scheduler to maintain persistent lists of them. **/
43+
+ tbb::detail::r1::observer_proxy* my_proxy;
44+
+
45+
+ //! Counter preventing the observer from being destroyed while in use by the scheduler.
46+
+ /** Valid only when observation is on. **/
47+
+ std::atomic<intptr_t> my_busy_count;
48+
+
49+
+public:
50+
+ //! Enable or disable observation
51+
+ /** For local observers the method can be used only when the current thread
52+
+ has the task scheduler initialized or is attached to an arena.
53+
+
54+
+ Repeated calls with the same state are no-ops. **/
55+
+ void __TBB_EXPORTED_METHOD observe( bool state=true );
56+
+
57+
+ //! Returns true if observation is enabled, false otherwise.
58+
+ bool is_observing() const {return my_proxy!=NULL;}
59+
+
60+
+ //! Construct observer with observation disabled.
61+
+ task_scheduler_observer_v3() : my_proxy(NULL) { my_busy_count.store(0); }
62+
+
63+
+ //! Entry notification
64+
+ /** Invoked from inside observe(true) call and whenever a worker enters the arena
65+
+ this observer is associated with. If a thread is already in the arena when
66+
+ the observer is activated, the entry notification is called before it
67+
+ executes the first stolen task.
68+
+
69+
+ Obsolete semantics. For global observers it is called by a thread before
70+
+ the first steal since observation became enabled. **/
71+
+ virtual void on_scheduler_entry( bool /*is_worker*/ ) {}
72+
+
73+
+ //! Exit notification
74+
+ /** Invoked from inside observe(false) call and whenever a worker leaves the
75+
+ arena this observer is associated with.
76+
+
77+
+ Obsolete semantics. For global observers it is called by a thread before
78+
+ the first steal since observation became enabled. **/
79+
+ virtual void on_scheduler_exit( bool /*is_worker*/ ) {}
80+
+
81+
+ //! Destructor automatically switches observation off if it is enabled.
82+
+ virtual ~task_scheduler_observer_v3() { if(my_proxy) observe(false);}
83+
+};
84+
+
85+
+} // namespace internal
86+
+
87+
+namespace interface6 {
88+
+class task_scheduler_observer : public internal::task_scheduler_observer_v3 {
89+
+ friend class internal::task_scheduler_observer_v3;
90+
+ friend class tbb::detail::r1::observer_proxy;
91+
+ friend class tbb::detail::r1::observer_list;
92+
+
93+
+ /** Negative numbers with the largest absolute value to minimize probability
94+
+ of coincidence in case of a bug in busy count usage. **/
95+
+ // TODO: take more high bits for version number
96+
+ static const intptr_t v6_trait = (intptr_t)((~(uintptr_t)0 >> 1) + 1);
97+
+
98+
+ //! contains task_arena pointer or tag indicating local or global semantics of the observer
99+
+ intptr_t my_context_tag;
100+
+ enum { global_tag = 0, implicit_tag = 1 };
101+
+
102+
+public:
103+
+ //! Construct local or global observer in inactive state (observation disabled).
104+
+ /** For a local observer entry/exit notifications are invoked whenever a worker
105+
+ thread joins/leaves the arena of the observer's owner thread. If a thread is
106+
+ already in the arena when the observer is activated, the entry notification is
107+
+ called before it executes the first stolen task. **/
108+
+ /** TODO: Obsolete.
109+
+ Global observer semantics is obsolete as it violates master thread isolation
110+
+ guarantees and is not composable. Thus the current default behavior of the
111+
+ constructor is obsolete too and will be changed in one of the future versions
112+
+ of the library. **/
113+
+ explicit task_scheduler_observer( bool local = false ) {
114+
+ my_context_tag = local? implicit_tag : global_tag;
115+
+ }
116+
+
117+
+ //! Construct local observer for a given arena in inactive state (observation disabled).
118+
+ /** entry/exit notifications are invoked whenever a thread joins/leaves arena.
119+
+ If a thread is already in the arena when the observer is activated, the entry notification
120+
+ is called before it executes the first stolen task. **/
121+
+ explicit task_scheduler_observer( task_arena & a) {
122+
+ my_context_tag = (intptr_t)&a;
123+
+ }
124+
+
125+
+ /** Destructor protects instance of the observer from concurrent notification.
126+
+ It is recommended to disable observation before destructor of a derived class starts,
127+
+ otherwise it can lead to concurrent notification callback on partly destroyed object **/
128+
+ virtual ~task_scheduler_observer() { if(my_proxy) observe(false); }
129+
+
130+
+ //! Enable or disable observation
131+
+ /** Warning: concurrent invocations of this method are not safe.
132+
+ Repeated calls with the same state are no-ops. **/
133+
+ void observe( bool state=true ) {
134+
+ if( state && !my_proxy ) {
135+
+ __TBB_ASSERT( !my_busy_count, "Inconsistent state of task_scheduler_observer instance");
136+
+ my_busy_count.store(v6_trait);
137+
+ }
138+
+ internal::task_scheduler_observer_v3::observe(state);
139+
+ }
140+
+};
141+
+
142+
+} //namespace interface6
143+
+
144+
+} // namespace tbb
145+
146+
#endif /* __TBB_task_scheduler_observer_H */
147+
--- a/src/tbb/src/tbb/observer_proxy.cpp
148+
+++ b/src/tbb/src/tbb/observer_proxy.cpp
149+
@@ -317,3 +317,23 @@
150+
} // namespace r1
151+
} // namespace detail
152+
} // namespace tbb
153+
+
154+
+// Not on Windows: there the old ABI is published by the tbb.dll stub built
155+
+// from src/tbb-compat/tbb-compat.cpp, which has to define this itself in
156+
+// order for R CMD SHLIB to pick it up when generating the export list.
157+
+// Defining it here as well makes the stub fail to link with "multiple
158+
+// definition of tbb::internal::task_scheduler_observer_v3::observe".
159+
+#ifndef _WIN32
160+
+
161+
+namespace tbb {
162+
+namespace internal {
163+
+
164+
+void __TBB_EXPORTED_FUNC task_scheduler_observer_v3::observe( bool enable ) {
165+
+ auto* tso = (tbb::detail::d1::task_scheduler_observer*) (this);
166+
+ tbb::detail::r1::observe(*tso, enable);
167+
+}
168+
+
169+
+} // namespace internal
170+
+} // namespace tbb
171+
+
172+
+#endif /* _WIN32 */
173+
\ No newline at end of file

0 commit comments

Comments
 (0)