Modbus add a timer thread, and switch timer to SIGEV_THREAD_ID to follow xenomai posix skin restrictions.
authorEdouard Tisserant
Tue, 23 Jun 2020 13:50:21 +0200
changeset 2683 5d8a4deacfe1
parent 2682 4d3320fdab19
child 2684 da8de4ef0449
Modbus add a timer thread, and switch timer to SIGEV_THREAD_ID to follow xenomai posix skin restrictions.
modbus/mb_runtime.c
modbus/mb_runtime.h
--- a/modbus/mb_runtime.c	Fri Jun 19 11:07:25 2020 +0200
+++ b/modbus/mb_runtime.c	Tue Jun 23 13:50:21 2020 +0200
@@ -25,6 +25,7 @@
 
 #include <stdio.h>
 #include <string.h>  /* required for memcpy() */
+#include <errno.h>
 #include <time.h>
 #include <signal.h>
 #include "mb_slave_and_master.h"
@@ -470,23 +471,62 @@
  * The same callback function is called by the timers of all client nodes. The id of the client node
  * in question will be passed as a parameter to the call back function.
  */
-void __client_node_timer_callback_function(union sigval sigev_value) {
+void __client_node_timer_callback_function(int client_node_id) {
     /* signal the client node's condition variable on which the client node's thread should be waiting... */
     /* Since the communication cycle is run with the mutex locked, we use trylock() instead of lock() */
-    //pthread_mutex_lock  (&(client_nodes[sigev_value.sival_int].mutex));
-    if (pthread_mutex_trylock (&(client_nodes[sigev_value.sival_int].mutex)) != 0)
+    if (pthread_mutex_trylock (&(client_nodes[client_node_id].mutex)) != 0)
         /* we never get to signal the thread for activation. But that is OK.
          * If it still in the communication cycle (during which the mutex is kept locked)
          * then that means that the communication cycle is falling behing in the periodic 
          * communication cycle, and we therefore need to skip a period.
          */
         return;
-    client_nodes[sigev_value.sival_int].execute_req  = 1; // tell the thread to execute
-    client_nodes[sigev_value.sival_int].periodic_act = 1; // tell the thread the activation was done by periodic timer   
-    pthread_cond_signal (&(client_nodes[sigev_value.sival_int].condv));
-    pthread_mutex_unlock(&(client_nodes[sigev_value.sival_int].mutex));
-}
-
+    client_nodes[client_node_id].execute_req  = 1; // tell the thread to execute
+    client_nodes[client_node_id].periodic_act = 1; // tell the thread the activation was done by periodic timer   
+    pthread_cond_signal (&(client_nodes[client_node_id].condv));
+    pthread_mutex_unlock(&(client_nodes[client_node_id].mutex));
+}
+
+
+
+static int stop_mb_client_timer_thread;
+static void *__mb_client_timer_thread(void *_index) {
+    sigset_t set;
+    int signum = SIGALRM;
+	int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast)
+    printf("%%d\n", client_node_id);
+    /* initialize the timer that will be used to periodically activate the client node */
+    {
+        // start off by reseting the flag that will be set whenever the timer expires
+        client_nodes[client_node_id].periodic_act = 0;
+
+        struct sigevent evp = {0};
+        evp.sigev_notify            = SIGEV_THREAD_ID; /* Notification method - call a function in a new thread context */
+        evp.sigev_signo             = SIGALRM;
+        evp.sigev_value.sival_int   = client_node_id;        /* Data passed to function upon notification - used to indentify which client node to activate */
+       
+        if (timer_create(CLOCK_MONOTONIC, &evp, &(client_nodes[client_node_id].timer_id)) < 0) {
+            fprintf(stderr, "Modbus plugin: Error (%%s) creating timer for modbus client node %%s\n", strerror(errno), client_nodes[client_node_id].location);
+            return NULL;
+        }
+    }
+
+    stop_mb_client_timer_thread = 0;
+    while(!stop_mb_client_timer_thread) {
+        if(sigwait (&set, &signum) == -1)
+            perror ("sigwait");
+        if(stop_mb_client_timer_thread)
+            break;
+        __client_node_timer_callback_function(client_node_id);
+    }
+
+    // timer was created, so we try to destroy it!
+    int res = timer_delete(client_nodes[client_node_id].timer_id);
+    if (res < 0)
+        fprintf(stderr, "Modbus plugin: Error destroying timer for modbus client node %%s\n", client_nodes[client_node_id].location);
+
+    return NULL;
+}
 
 
 int __cleanup_%(locstr)s ();
@@ -581,22 +621,17 @@
         client_nodes[index].execute_req = 0; //variable associated with condition variable
 		client_nodes[index].init_state = 3; // we have created the condition variable
 		
-		/* initialize the timer that will be used to periodically activate the client node */
-        {
-            // start off by reseting the flag that will be set whenever the timer expires
-            client_nodes[index].periodic_act = 0;
-
-            struct sigevent evp;
-            evp.sigev_notify            = SIGEV_THREAD; /* Notification method - call a function in a new thread context */
-            evp.sigev_value.sival_int   = index;        /* Data passed to function upon notification - used to indentify which client node to activate */
-            evp.sigev_notify_function   = __client_node_timer_callback_function; /* function to call upon timer expiration */
-            evp.sigev_notify_attributes = NULL;         /* attributes for new thread in which sigev_notify_function will be called/executed */
-            
-            if (timer_create(CLOCK_MONOTONIC, &evp, &(client_nodes[index].timer_id)) < 0) {
-                fprintf(stderr, "Modbus plugin: Error creating timer for modbus client node %%s\n", client_nodes[index].location);
-                goto error_exit;                
-            }
-        }
+		/* launch a thread to handle this client node timer */
+		{
+			int res = 0;
+			pthread_attr_t attr;
+			res |= pthread_attr_init(&attr);
+			res |= pthread_create(&(client_nodes[index].timer_thread_id), &attr, &__mb_client_timer_thread, (void *)((char *)NULL + index));
+			if (res !=  0) {
+				fprintf(stderr, "Modbus plugin: Error starting timer thread for modbus client node %%s\n", client_nodes[index].location);
+				goto error_exit;
+			}
+		}
         client_nodes[index].init_state = 4; // we have created the timer
 
 		/* launch a thread to handle this client node */
@@ -754,10 +789,14 @@
 
 		close = 0;
 		if (client_nodes[index].init_state >= 4) {
-			// timer was created, so we try to destroy it!
-			close  = timer_delete(client_nodes[index].timer_id);
+            stop_mb_client_timer_thread = 1;
+            pthread_kill(client_nodes[index].timer_thread_id, SIGALRM);
+			// thread was launched, so we try to cancel it!
+			close  = pthread_cancel(client_nodes[index].timer_thread_id);
+			close |= pthread_join  (client_nodes[index].timer_thread_id, NULL);
 			if (close < 0)
-				fprintf(stderr, "Modbus plugin: Error destroying timer for modbus client node %%s\n", client_nodes[index].location);
+				fprintf(stderr, "Modbus plugin: Error closing timer thread for modbus client node %%s\n", client_nodes[index].location);
+
 		}
 		res |= close;
 
--- a/modbus/mb_runtime.h	Fri Jun 19 11:07:25 2020 +0200
+++ b/modbus/mb_runtime.h	Tue Jun 23 13:50:21 2020 +0200
@@ -106,6 +106,7 @@
 	    u64		comm_period;// period to use when periodically sending requests to remote server
 	    int		prev_error; // error code of the last printed error message (0 when no error) 
 	    pthread_t   thread_id;  // thread handling all communication for this client node
+	    pthread_t	timer_thread_id;  // thread handling periodical timer for this client node
 	    timer_t      timer_id;  // timer used to periodically activate this client node's thread
 	    pthread_mutex_t mutex;  // mutex to be used with the following condition variable
         pthread_cond_t  condv;  // used to signal the client thread when to start new modbus transactions