| 270 | |
| 271 | template <typename T> |
| 272 | inline void Blackboard::set(const std::string& key, const T& value) |
| 273 | { |
| 274 | if(StartWith(key, '@')) |
| 275 | { |
| 276 | rootBlackboard()->set(key.substr(1, key.size() - 1), value); |
| 277 | return; |
| 278 | } |
| 279 | std::shared_lock storage_lock(storage_mutex_); |
| 280 | |
| 281 | // check local storage |
| 282 | auto it = storage_.find(key); |
| 283 | if(it == storage_.end()) |
| 284 | { |
| 285 | // create a new entry |
| 286 | Any new_value(value); |
| 287 | storage_lock.unlock(); |
| 288 | std::shared_ptr<Blackboard::Entry> entry; |
| 289 | // if a new generic port is created with a string, it's type should be AnyTypeAllowed |
| 290 | if constexpr(std::is_same_v<std::string, T>) |
| 291 | { |
| 292 | entry = createEntryImpl(key, PortInfo(PortDirection::INOUT)); |
| 293 | } |
| 294 | else |
| 295 | { |
| 296 | PortInfo new_port(PortDirection::INOUT, new_value.type(), |
| 297 | GetAnyFromStringFunctor<T>()); |
| 298 | entry = createEntryImpl(key, new_port); |
| 299 | } |
| 300 | |
| 301 | // Lock entry_mutex before writing to prevent data races with |
| 302 | // concurrent readers (BUG-1/BUG-8 fix). |
| 303 | std::scoped_lock entry_lock(entry->entry_mutex); |
| 304 | entry->value = new_value; |
| 305 | entry->sequence_id++; |
| 306 | entry->stamp = std::chrono::steady_clock::now().time_since_epoch(); |
| 307 | } |
| 308 | else |
| 309 | { |
| 310 | // this is not the first time we set this entry, we need to check |
| 311 | // if the type is the same or not. |
| 312 | // Copy shared_ptr to prevent use-after-free if another thread |
| 313 | // calls unset() while we hold the reference (BUG-2 fix). |
| 314 | auto entry_ptr = it->second; |
| 315 | storage_lock.unlock(); |
| 316 | Entry& entry = *entry_ptr; |
| 317 | |
| 318 | std::scoped_lock scoped_lock(entry.entry_mutex); |
| 319 | |
| 320 | Any& previous_any = entry.value; |
| 321 | Any new_value(value); |
| 322 | |
| 323 | // special case: entry exists but it is not strongly typed... yet |
| 324 | if(!entry.info.isStronglyTyped()) |
| 325 | { |
| 326 | // Use the new type to create a new entry that is strongly typed. |
| 327 | entry.info = TypeInfo::Create<T>(); |
| 328 | entry.sequence_id++; |
| 329 | entry.stamp = std::chrono::steady_clock::now().time_since_epoch(); |