| 85 | } |
| 86 | |
| 87 | void ChannelManager::addDriver(int priority, fl::shared_ptr<IChannelDriver> driver) { |
| 88 | if (!driver) { |
| 89 | FL_WARN("ChannelManager::addDriver() - Null driver provided"); |
| 90 | return; |
| 91 | } |
| 92 | |
| 93 | // Get driver name from the driver itself |
| 94 | fl::string engineName = driver->getName(); |
| 95 | |
| 96 | // Reject drivers with empty names |
| 97 | if (engineName.empty()) { |
| 98 | FL_WARN("ChannelManager::addDriver() - Engine has empty name (driver->getName() returned empty string)"); |
| 99 | return; |
| 100 | } |
| 101 | |
| 102 | // Check if driver with this name already exists |
| 103 | bool replacing = false; |
| 104 | for (const auto& entry : mDrivers) { |
| 105 | if (entry.name == engineName) { |
| 106 | // True-duplicate fast path: same shared_ptr at same priority is |
| 107 | // a no-op (legacy clockless controllers may pre-bind the same |
| 108 | // driver singleton from many template instantiations). Skip the |
| 109 | // replace flow entirely so we don't waitForReady() or emit a |
| 110 | // spurious "Replacing" warning. |
| 111 | if (entry.driver == driver && entry.priority == priority) { |
| 112 | FL_DBG("ChannelManager::addDriver() - '" << engineName.c_str() |
| 113 | << "' already registered at priority " << priority |
| 114 | << " (idempotent no-op)"); |
| 115 | return; |
| 116 | } |
| 117 | replacing = true; |
| 118 | FL_WARN("ChannelManager::addDriver() - Replacing existing driver '" << engineName.c_str() << "'"); |
| 119 | break; |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | // If replacing, wait for all drivers to become READY |
| 124 | if (replacing) { |
| 125 | FL_DBG("ChannelManager: Waiting for all drivers to become READY before replacement"); |
| 126 | waitForReady(); |
| 127 | |
| 128 | // Remove the old driver with matching name (shared_ptr may trigger deletion) |
| 129 | for (size_t i = 0; i < mDrivers.size(); ++i) { |
| 130 | if (mDrivers[i].name == engineName) { |
| 131 | FL_DBG("ChannelManager: Removing old driver '" << engineName.c_str() << "' (shared_ptr may delete)"); |
| 132 | if (mDrivers[i].driver) { |
| 133 | mDrivers[i].driver->setPollNeededCallback(IChannelDriver::PollNeededCallback()); |
| 134 | } |
| 135 | mDrivers.erase(mDrivers.begin() + i); |
| 136 | break; |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | // Respect exclusive driver mode: auto-disable if name doesn't match exclusive driver |
| 142 | bool enabled = true; // Default: enabled |
| 143 | if (!mExclusiveDriver.empty()) { |
| 144 | enabled = (engineName == mExclusiveDriver); // Only enable if matches exclusive driver |
no test coverage detected