66#include < stdint.h>
77#include < unordered_map>
88#include < mutex>
9+ #include < shared_mutex>
910#include < sstream>
1011
1112#include " behaviortree_cpp/basic_types.h"
1213#include " behaviortree_cpp/utils/safe_any.hpp"
1314#include " behaviortree_cpp/exceptions.h"
15+ #include " behaviortree_cpp/utils/locked_reference.hpp"
1416
1517namespace BT
1618{
19+
20+ using AnyReadRef = LockedConstRef<Any>;
21+ using AnyWriteRef = LockedRef<Any>;
22+
1723/* *
1824 * @brief The Blackboard is the mechanism used by BehaviorTrees to exchange
1925 * typed data.
2026 */
2127class Blackboard
2228{
29+
2330public:
2431 typedef std::shared_ptr<Blackboard> Ptr;
2532
@@ -29,6 +36,21 @@ class Blackboard
2936 {}
3037
3138public:
39+
40+ struct Entry
41+ {
42+ Any value;
43+ PortInfo port_info;
44+ std::shared_mutex entry_mutex;
45+
46+ Entry (const PortInfo& info) : port_info(info)
47+ {}
48+
49+ Entry (Any&& other_any, const PortInfo& info) :
50+ value (std::move(other_any)), port_info(info)
51+ {}
52+ };
53+
3254 /* * Use this static method to create an instance of the BlackBoard
3355 * to share among all your NodeTrees.
3456 */
@@ -39,13 +61,7 @@ class Blackboard
3961
4062 virtual ~Blackboard () = default ;
4163
42- /* *
43- * @brief The method getAny allow the user to access directly the type
44- * erased value.
45- *
46- * @return the pointer or nullptr if it fails.
47- */
48- const Any* getAny (const std::string& key) const
64+ const Entry* getEntry (const std::string& key) const
4965 {
5066 std::unique_lock<std::mutex> lock (mutex_);
5167 // search first if this port was remapped
@@ -56,19 +72,49 @@ class Blackboard
5672 auto remapping_it = internal_to_external_.find (key);
5773 if (remapping_it != internal_to_external_.end ())
5874 {
59- return parent->getAny (remapping_it->second );
75+ return parent->getEntry (remapping_it->second );
6076 }
6177 }
6278 }
6379 auto it = storage_.find (key);
64- return (it == storage_.end ()) ? nullptr : &( it->second .value );
80+ return (it == storage_.end ()) ? nullptr : it->second .get ( );
6581 }
6682
67- Any* getAny (const std::string& key)
83+ Entry* getEntry (const std::string& key)
6884 {
6985 // "Avoid Duplication in const and Non-const Member Function,"
7086 // on p. 23, in Item 3 "Use const whenever possible," in Effective C++, 3d ed
71- return const_cast <Any*>( static_cast <const Blackboard &>(*this ).getAny (key));
87+ return const_cast <Entry*>( static_cast <const Blackboard &>(*this ).getEntry (key));
88+ }
89+
90+ AnyReadRef getAnyRead (const std::string& key) const
91+ {
92+ if (auto entry = getEntry (key))
93+ {
94+ return AnyReadRef (&entry->value , const_cast <std::shared_mutex*>(&entry->entry_mutex ));
95+ }
96+ return {};
97+ }
98+
99+ AnyWriteRef getAnyWrite (const std::string& key)
100+ {
101+ if (auto entry = getEntry (key))
102+ {
103+ return AnyWriteRef (&entry->value , &entry->entry_mutex );
104+ }
105+ return {};
106+ }
107+
108+ [[deprecated(" Use getAnyRead instead" )]]
109+ const Any* getAny (const std::string& key) const
110+ {
111+ return getAnyRead (key).get ();
112+ }
113+
114+ [[deprecated(" Use getAnyWrite instead" )]]
115+ Any* getAny (const std::string& key)
116+ {
117+ return getAnyWrite (key).get ();
72118 }
73119
74120 /* * Return true if the entry with the given key was found.
@@ -77,12 +123,12 @@ class Blackboard
77123 template <typename T>
78124 bool get (const std::string& key, T& value) const
79125 {
80- const Any* val = getAny (key);
81- if (val)
126+ if (auto any_ref = getAnyRead (key))
82127 {
83- value = val->cast <T>();
128+ value = any_ref.get ()->cast <T>();
129+ return true ;
84130 }
85- return ( bool )val ;
131+ return false ;
86132 }
87133
88134 /* *
@@ -91,10 +137,9 @@ class Blackboard
91137 template <typename T>
92138 T get (const std::string& key) const
93139 {
94- const Any* val = getAny (key);
95- if (val)
140+ if (auto any_ref = getAnyRead (key))
96141 {
97- return val ->cast <T>();
142+ return any_ref. get () ->cast <T>();
98143 }
99144 else
100145 {
@@ -106,7 +151,6 @@ class Blackboard
106151 template <typename T>
107152 void set (const std::string& key, const T& value)
108153 {
109- std::unique_lock lock_entry (entry_mutex_);
110154 std::unique_lock lock (mutex_);
111155
112156 // search first if this port was remapped.
@@ -133,19 +177,21 @@ class Blackboard
133177 if (std::is_constructible<StringView, T>::value)
134178 {
135179 PortInfo new_port (PortDirection::INOUT, typeid (std::string), {});
136- storage_.emplace (key, Entry (Any (value), new_port));
180+ storage_.emplace (key, std::make_unique< Entry> (Any (value), new_port));
137181 }
138182 else
139183 {
140184 PortInfo new_port (PortDirection::INOUT, typeid (T), {});
141- storage_.emplace (key, Entry (Any (value), new_port));
185+ storage_.emplace (key, std::make_unique< Entry> (Any (value), new_port));
142186 }
143187 }
144188 else
145189 {
146190 // this is not the first time we set this entry, we need to check
147191 // if the type is the same or not.
148- Entry& entry = it->second ;
192+ Entry& entry = *it->second ;
193+ std::scoped_lock lock (entry.entry_mutex );
194+
149195 Any& previous_any = entry.value ;
150196 const PortInfo& port_info = entry.port_info ;
151197
@@ -209,30 +255,9 @@ class Blackboard
209255 storage_.clear ();
210256 }
211257
212- // Lock this mutex before using get() and getAny() and unlock it while you have
213- // done using the value.
214- std::recursive_mutex& entryMutex () const
215- {
216- return entry_mutex_;
217- }
218-
219258private:
220- struct Entry
221- {
222- Any value;
223- PortInfo port_info;
224-
225- Entry (const PortInfo& info) : port_info(info)
226- {}
227-
228- Entry (Any&& other_any, const PortInfo& info) :
229- value (std::move(other_any)), port_info(info)
230- {}
231- };
232-
233259 mutable std::mutex mutex_;
234- mutable std::recursive_mutex entry_mutex_;
235- std::unordered_map<std::string, Entry> storage_;
260+ std::unordered_map<std::string, std::unique_ptr<Entry>> storage_;
236261 std::weak_ptr<Blackboard> parent_bb_;
237262 std::unordered_map<std::string, std::string> internal_to_external_;
238263
0 commit comments