Plugins

Covers Engine Plugin SDK 2.0.0 · Interface Plugin SDK 0.2.0

TaskModuleBindings

Plugin-owned, configuration-only task/module association helper.

bind wraps existing configure/cleanup callbacks without changing SDK ABI. Limits count configured tasks, including stopped ones. Claims commit after successful preparation and release on task destruction; ordinary cycles never enter it. Device access remains synchronized by the module.

Declaration

class TaskModuleBindings {
    using Key = std::pair<std::string, std::string>;
    struct State {
        std::mutex mutex;
        std::map<Key, std::set<std::string>> claims;
        std::map<std::string, Key> tasks;
        void release(const std::string& task) {
            std::scoped_lock lock(mutex);
            if (const auto found = tasks.find(task); found != tasks.end()) {
                auto claim = claims.find(found->second);
                if (claim != claims.end()) { claim->second.erase(task); if (claim->second.empty()) claims.erase(claim); }
                tasks.erase(found);
            }
        }
    };
    std::shared_ptr<State> state_ = std::make_shared<State>();
public:
    template<class Module, class Prepare>
    void bind(TaskTypeDefinition& definition, SDK_API* api, std::string plugin_id,
        std::string module_type_id, size_t max_per_type, Prepare prepare) {
        auto state = state_;
        auto previous_configure = definition.on_configure;
        auto previous_cleanup = definition.cleanup;
        definition.on_configure = [=](const TaskTypeDefinition& type, TaskRuntime& runtime) {
            auto module = taskModule<Module>(api, runtime, plugin_id, module_type_id);
            const auto task = runtime.getTaskName();
            const Key next{taskArgument<std::string>(runtime, "module_instance_name"), runtime.getTaskType()};
            bool inserted = false;
            {
                std::scoped_lock lock(state->mutex);
                auto& owners = state->claims[next];
                if (!owners.contains(task) && max_per_type && owners.size() >= max_per_type)
                    throw std::runtime_error("Module `" + next.first + "` already has " + next.second + " task `" + *owners.begin() + "`.");
                inserted = owners.insert(task).second;
            }
            try {
                if (previous_configure) previous_configure(type, runtime);
                prepare(module, runtime);
            } catch (...) {
                if (inserted) { std::scoped_lock lock(state->mutex); auto& owners = state->claims[next]; owners.erase(task); if (owners.empty()) state->claims.erase(next); }
                throw;
            }
            {
                std::scoped_lock lock(state->mutex);
                const auto old = state->tasks.find(task);
                if (old != state->tasks.end() && old->second != next) {
                    auto& owners = state->claims[old->second]; owners.erase(task);
                    if (owners.empty()) state->claims.erase(old->second);
                }
                state->tasks[task] = next;
            }
        };
        definition.cleanup = [=](TaskRuntime& runtime) {
            state->release(runtime.getTaskName());
            if (previous_cleanup) previous_cleanup(runtime);
        };
    }
};