Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 67 additions & 31 deletions core/src/drivers/plugin_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
#include "../plc_app/utils/log.h"
#include "plugin_config.h"
#include "plugin_driver.h"
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>

// External buffer declarations from image_tables.c
extern IEC_BOOL *bool_input[BUFFER_SIZE][8];
Expand Down Expand Up @@ -41,13 +41,33 @@ plugin_driver_t *plugin_driver_create(void)
return NULL;
}

// Initialize mutex
if (pthread_mutex_init(&driver->buffer_mutex, NULL) != 0)
// Initialize mutex with priority inheritance to prevent priority inversion
// This ensures that when a lower-priority plugin thread holds the mutex,
// it temporarily inherits the priority of any higher-priority thread
// (like the PLC scan cycle thread) waiting for the mutex.
pthread_mutexattr_t mutex_attr;
if (pthread_mutexattr_init(&mutex_attr) != 0)
{
free(driver);
return NULL;
}

if (pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT) != 0)
{
pthread_mutexattr_destroy(&mutex_attr);
free(driver);
return NULL;
}

if (pthread_mutex_init(&driver->buffer_mutex, &mutex_attr) != 0)
{
pthread_mutexattr_destroy(&mutex_attr);
free(driver);
return NULL;
}

pthread_mutexattr_destroy(&mutex_attr);

return driver;
}

Expand Down Expand Up @@ -105,34 +125,37 @@ int plugin_driver_load_config(plugin_driver_t *driver, const char *config_file)
// Check if config file exists, if not copy from default
if (access(config_file, F_OK) != 0)
{
printf("[PLUGIN]: Config file %s not found, copying from plugins_default.conf\n", config_file);

printf("[PLUGIN]: Config file %s not found, copying from plugins_default.conf\n",
config_file);

// Check if default config exists
if (access("plugins_default.conf", F_OK) != 0)
{
printf("[PLUGIN]: Default config file plugins_default.conf not found\n");
return -1;
}

// Copy default config to target config file
FILE *src = fopen("plugins_default.conf", "r");
FILE *dst = fopen(config_file, "w");

if (!src || !dst)
{
printf("[PLUGIN]: Failed to copy default config\n");
if (src) fclose(src);
if (dst) fclose(dst);
if (src)
fclose(src);
if (dst)
fclose(dst);
return -1;
}

char buffer[1024];
size_t bytes;
while ((bytes = fread(buffer, 1, sizeof(buffer), src)) > 0)
{
fwrite(buffer, 1, bytes, dst);
}

fclose(src);
fclose(dst);
printf("[PLUGIN]: Successfully copied default config to %s\n", config_file);
Expand All @@ -146,11 +169,12 @@ int plugin_driver_load_config(plugin_driver_t *driver, const char *config_file)
}

driver->plugin_count = config_count;
has_python_plugin = 0;
has_python_plugin = 0;
for (int w = 0; w < config_count; w++)
{
memcpy(&driver->plugins[w].config, &configs[w], sizeof(plugin_config_t));
if (configs[w].type == PLUGIN_TYPE_PYTHON) {
if (configs[w].type == PLUGIN_TYPE_PYTHON)
{
has_python_plugin = 1;
}
}
Expand All @@ -170,7 +194,9 @@ int plugin_driver_load_config(plugin_driver_t *driver, const char *config_file)
plugin_manager_destroy(plugin->manager);
return -1;
}
} else if (plugin->config.type == PLUGIN_TYPE_NATIVE) {
}
else if (plugin->config.type == PLUGIN_TYPE_NATIVE)
{
if (native_plugin_get_symbols(plugin) != 0)
{
fprintf(stderr, "Failed to get native plugin symbols for: %s\n",
Expand All @@ -195,14 +221,14 @@ int plugin_driver_init(plugin_driver_t *driver)
for (int i = 0; i < driver->plugin_count; i++)
{
plugin_instance_t *plugin = &driver->plugins[i];

// Skip disabled plugins
if (!plugin->config.enabled)
{
printf("[PLUGIN]: Skipping disabled plugin: %s\n", plugin->config.name);
continue;
}

if (plugin->config.type == PLUGIN_TYPE_PYTHON && plugin->python_plugin &&
plugin->python_plugin->pFuncInit)
{
Expand All @@ -218,7 +244,7 @@ int plugin_driver_init(plugin_driver_t *driver)
// Call the Python init function with proper capsule
PyObject *result =
PyObject_CallFunctionObjArgs(plugin->python_plugin->pFuncInit, args, NULL);

// Store the capsule reference for the lifetime of the plugin
plugin->python_plugin->args_capsule = args;

Expand All @@ -236,7 +262,8 @@ int plugin_driver_init(plugin_driver_t *driver)
{
// Generate structured args for native plugin
plugin_runtime_args_t *args =
(plugin_runtime_args_t *)generate_structured_args_with_driver(PLUGIN_TYPE_NATIVE, driver, i);
(plugin_runtime_args_t *)generate_structured_args_with_driver(PLUGIN_TYPE_NATIVE,
driver, i);
if (!args)
{
fprintf(stderr, "Failed to generate runtime args for native plugin: %s\n",
Expand Down Expand Up @@ -276,23 +303,23 @@ int plugin_driver_start(plugin_driver_t *driver)
return 0;
}


if (has_python_plugin) {
if (has_python_plugin)
{
main_tstate = PyEval_SaveThread();
gstate = PyGILState_Ensure();
}

for (int i = 0; i < driver->plugin_count; i++)
{
plugin_instance_t *plugin = &driver->plugins[i];

// Skip disabled plugins
if (!plugin->config.enabled)
{
printf("[PLUGIN]: Skipping disabled plugin during start: %s\n", plugin->config.name);
continue;
}

switch (plugin->config.type)
{
case PLUGIN_TYPE_PYTHON:
Expand Down Expand Up @@ -347,7 +374,8 @@ int plugin_driver_start(plugin_driver_t *driver)
break;
}
}
if (has_python_plugin) {
if (has_python_plugin)
{
PyGILState_Release(gstate);
}
return 0;
Expand Down Expand Up @@ -475,7 +503,8 @@ void plugin_driver_destroy(plugin_driver_t *driver)
return;
}

if (has_python_plugin) {
if (has_python_plugin)
{
gstate = PyGILState_Ensure();
}

Expand All @@ -499,10 +528,12 @@ void plugin_driver_destroy(plugin_driver_t *driver)
if (plugin->native_plugin->cleanup)
{
plugin->native_plugin->cleanup();
printf("[PLUGIN]: Native plugin %s cleaned up successfully.\n", plugin->config.name);
printf("[PLUGIN]: Native plugin %s cleaned up successfully.\n",
plugin->config.name);
}
// Close the shared library handle
if (plugin->native_plugin->handle) {
if (plugin->native_plugin->handle)
{
dlclose(plugin->native_plugin->handle);
plugin->native_plugin->handle = NULL;
}
Expand All @@ -512,7 +543,8 @@ void plugin_driver_destroy(plugin_driver_t *driver)
}
}

if (has_python_plugin) {
if (has_python_plugin)
{
PyGILState_Release(gstate);
PyEval_RestoreThread(main_tstate);
Py_FinalizeEx();
Expand Down Expand Up @@ -860,28 +892,32 @@ int native_plugin_get_symbols(plugin_instance_t *plugin)
native_bundle->start = (plugin_start_loop_func_t)dlsym(handle, "start_loop");
if (!native_bundle->start)
{
fprintf(stderr, "Warning: 'start_loop' function not found in native plugin '%s' (optional)\n",
fprintf(stderr,
"Warning: 'start_loop' function not found in native plugin '%s' (optional)\n",
plugin->config.path);
}

native_bundle->stop = (plugin_stop_loop_func_t)dlsym(handle, "stop_loop");
if (!native_bundle->stop)
{
fprintf(stderr, "Warning: 'stop_loop' function not found in native plugin '%s' (optional)\n",
fprintf(stderr,
"Warning: 'stop_loop' function not found in native plugin '%s' (optional)\n",
plugin->config.path);
}

native_bundle->cycle_start = (plugin_cycle_start_func_t)dlsym(handle, "cycle_start");
if (!native_bundle->cycle_start)
{
fprintf(stderr, "Warning: 'cycle_start' function not found in native plugin '%s' (optional)\n",
fprintf(stderr,
"Warning: 'cycle_start' function not found in native plugin '%s' (optional)\n",
plugin->config.path);
}

native_bundle->cycle_end = (plugin_cycle_end_func_t)dlsym(handle, "cycle_end");
if (!native_bundle->cycle_end)
{
fprintf(stderr, "Warning: 'cycle_end' function not found in native plugin '%s' (optional)\n",
fprintf(stderr,
"Warning: 'cycle_end' function not found in native plugin '%s' (optional)\n",
plugin->config.path);
}

Expand Down
Loading