* Moved shared memory read lock handling from tele2json.c to individual clients.
authorUrban Wallasch <urban.wallasch@freenet.de>
Sun, 18 Aug 2019 20:38:08 +0000 (22:38 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Sun, 18 Aug 2019 20:38:08 +0000 (22:38 +0200)
* Fixed shared memory rwlock initialization to use appropriate set of attributes.
* Fixed not releasing the write lock when plugin initialization failed.
* Changed order of telemetry data initialization (e.g. raise alive flag only on success).
* Declared shared memory telemetry objects volatile in the clients.

tele2json.c
telehttpd.c
telelogger.c
teleshmem.cpp

index aee5bbde5257317b04f5a7261f18286db5f5d2ac..7a402942b5e1213467eb77d83353f82814369986 100644 (file)
@@ -7,22 +7,12 @@
 #include "tele2json.h"
 #include "log.h"
 
-size_t tele2json( char *buf, size_t size, struct telemetry_state_t *ptele ) {
-    int e;
+size_t tele2json( char *buf, size_t size, struct telemetry_state_t *tele ) {
     size_t n = 0;
-    struct telemetry_state_t t_, *tele = &t_;
 
-    if ( NULL == ptele )
+    if ( NULL == tele )
         return 0;
 
-    e = pthread_rwlock_rdlock( &ptele->rwlock );
-    if ( 0 != e ) {
-        EPRINT( "pthread_rwlock_rdlock: %s\n", strerror( e ) );
-        return 0;
-    }
-    memcpy( tele, ptele, sizeof *tele );
-    pthread_rwlock_unlock( &ptele->rwlock );
-
 #define CHKSIZE do{ if ( n >= size ) return 0; }while(0)
     CHKSIZE;
     n += snprintf( buf+n, size-n, "{\n" ); CHKSIZE;
index a3c31f52656ce3614ae4164638fc89f01c458fa6..1c9571b80346ba24f09dfe6a8e38f3b7b24ef21b 100644 (file)
@@ -42,8 +42,7 @@ struct {
     false,
 };
 
-static struct telemetry_state_t *telemetry;
-static struct telemetry_state_t telemetry0;
+static volatile struct telemetry_state_t *telemetry;
 
 static volatile sig_atomic_t force_quit;
 
@@ -69,7 +68,7 @@ static int init_shm( bool retry ) {
         else if ( TELE_VERSION != telemetry->version ) {
             EPRINT( "telemetry version mismatch: got %u, want %u\n",
                     (unsigned)telemetry->version, TELE_VERSION );
-            release_shm( TELE_SHM_KEY, telemetry );
+            release_shm( TELE_SHM_KEY, (void *)telemetry );
             telemetry = NULL;
             if ( !retry )
                 return -1;
@@ -86,7 +85,7 @@ static int check_shm( bool retry ) {
     if ( NULL == telemetry )
         return init_shm( retry );
     if ( !(telemetry->flags & TELE_FLAG_ALIVE) ) {
-        release_shm( TELE_SHM_KEY, telemetry );
+        release_shm( TELE_SHM_KEY, (void *)telemetry );
         telemetry = NULL;
         return init_shm( retry );
     }
@@ -277,7 +276,19 @@ static int serve_http(void) {
         if ( 0 < as ) {
             if ( NULL != (td = malloc( sizeof *td )) ) {
                 td->sock = as;
-                memcpy( &td->tele, telemetry ? telemetry : &telemetry0, sizeof td->tele );
+                if ( telemetry ) {
+                    int e = pthread_rwlock_rdlock( (pthread_rwlock_t *)&telemetry->rwlock );
+                    if ( 0 != e ) {
+                        EPRINT( "pthread_rwlock_rdlock: %s\n", strerror( e ) );
+                        return -1;
+                    }
+                    memcpy( &td->tele, (void *)telemetry, sizeof td->tele );
+                    pthread_rwlock_unlock( (pthread_rwlock_t *)&telemetry->rwlock );
+                }
+                else {
+                    DPRINT( "No telemetry, serving dummy data\n" );
+                    memset( &td->tele, 0, sizeof td->tele );
+                }
                 pthread_attr_init( &attr );
                 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
                 if ( 0 != pthread_create( &tid, &attr, handle_conn, td ) ) {
index 0fb02b2b47ca1dc98dc797a50c9d9ab7655b10fb..8223e8d159e66e4ab090e73831cb5030428c0c70 100644 (file)
@@ -15,7 +15,7 @@
 #include "telemetry.h"
 #include "tele2json.h"
 
-static struct telemetry_state_t *telemetry;
+static volatile struct telemetry_state_t *telemetry;
 
 static bool last_paused;
 static uint64_t last_timestamp;
@@ -51,7 +51,7 @@ static int init_shm( bool retry ) {
                     (unsigned)telemetry->version, TELE_VERSION );
             if ( !retry )
                 return -1;
-            release_shm( TELE_SHM_KEY, telemetry );
+            release_shm( TELE_SHM_KEY, (void *)telemetry );
             telemetry = NULL;
             sleep(1);
         }
@@ -66,7 +66,7 @@ static int check_shm( bool retry ) {
     if ( NULL == telemetry )
         return init_shm( retry );
     if ( !(telemetry->flags & TELE_FLAG_ALIVE) ) {
-        release_shm( TELE_SHM_KEY, telemetry );
+        release_shm( TELE_SHM_KEY, (void *)telemetry );
         telemetry = NULL;
         return init_shm( retry );
     }
@@ -74,15 +74,25 @@ static int check_shm( bool retry ) {
 }
 
 static int log_console( int cnt, int delay ) {
+    int e;
     char buf[4096];
     bool forever = !cnt;
+    struct telemetry_state_t tele;
 
     last_timestamp = telemetry->timestamp;
     while ( forever || cnt-- > 0 ) {
         if ( 0 != check_shm( cfg.retry ) )
             return -1;
 
-        if ( tele2json( buf, sizeof buf, telemetry ) > 0 )
+        e = pthread_rwlock_rdlock( (pthread_rwlock_t *)&telemetry->rwlock );
+        if ( 0 != e ) {
+            EPRINT( "pthread_rwlock_rdlock: %s\n", strerror( e ) );
+            return -1;
+        }
+        memcpy( &tele, (void *)telemetry, sizeof tele );
+        pthread_rwlock_unlock( (pthread_rwlock_t *)&telemetry->rwlock );
+
+        if ( tele2json( buf, sizeof buf, &tele ) > 0 )
             puts( buf );
 
         if ( forever || cnt > 0 ) {
index 46f24823ff181f67a5478efc17d40f73eb161742..e00e63fa57366e50ca1d93e1628c7283e629e957 100644 (file)
@@ -207,16 +207,31 @@ static bool init_shm(void)
         void *p;
         p = init_shmput( TELE_SHM_KEY, sizeof *telemetry );
         telemetry = static_cast<struct telemetry_state_t *>(p);
-        if ( NULL != telemetry )
-            pthread_rwlock_init( &telemetry->rwlock, NULL );
-    }
-    if ( NULL != telemetry ) {
-        lock_shm();
-        telemetry->version = TELE_VERSION;
-        telemetry->flags |= TELE_FLAG_ALIVE;
-        unlock_shm();
+        if ( NULL != telemetry ) {
+            int e;
+            pthread_rwlockattr_t attr;
+
+            e = pthread_rwlockattr_init( &attr );
+            if ( 0 != e ) {
+                log_print( "ERROR: pthread_rwlockattr_init: %s\n", strerror(e) );
+                goto RWLOCK_ERR;
+            }
+            pthread_rwlockattr_setkind_np( &attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP );
+            pthread_rwlockattr_setpshared( &attr, PTHREAD_PROCESS_SHARED );
+            e = pthread_rwlock_init( &telemetry->rwlock, &attr );
+            pthread_rwlockattr_destroy( &attr );
+            if ( 0 != e ) {
+                log_print( "ERROR: pthread_rwlock_init: %s\n", strerror(e) );
+                goto RWLOCK_ERR;
+            }
+        }
     }
     return NULL != telemetry;
+
+  RWLOCK_ERR:
+    release_shm( TELE_SHM_KEY, telemetry );
+    telemetry = NULL;
+    return false;
 }
 
 static void drop_shm(void)
@@ -565,6 +580,8 @@ SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_in
         return SCS_RESULT_generic_error;
     }
     lock_shm();
+    telemetry->version = TELE_VERSION;
+    telemetry->paused = true;
 
     // Check application version. Note that this example uses fairly basic channels which are likely to be supported
     // by any future SCS trucking game however more advanced application might want to at least warn the user if there
@@ -632,6 +649,7 @@ SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_in
         // cleared automatically so we can simply exit.
         log_print("FATAL: Unable to register event callbacks\n");
         version_params->common.log(SCS_LOG_TYPE_error, "[teleshmem] Unable to register telemetry event callbacks.");
+        unlock_shm();
         return SCS_RESULT_generic_error;
     }
     log_print("Registered event callbacks\n");
@@ -665,16 +683,16 @@ SCSAPI_RESULT scs_telemetry_init(const scs_u32_t version, const scs_telemetry_in
     if ( !channels_registered) {
         log_print("FATAL: Unable to register channel callbacks\n");
         version_params->common.log(SCS_LOG_TYPE_error, "[teleshmem] Unable to register telemetry channel callbacks.");
+        unlock_shm();
         return SCS_RESULT_generic_error;
     }
     log_print("Registered channel callbacks\n");
     version_params->common.log(SCS_LOG_TYPE_message, "[teleshmem] Registered telemetry channel callbacks.");
 
-    // Set the structure with defaults.
-    last_timestamp = static_cast<scs_timestamp_t>(-1);
-    // Initially the game is paused.
-    telemetry->paused = true;
+    // Last not least raise the alive flag:
+    telemetry->flags |= TELE_FLAG_ALIVE;
     unlock_shm();
+    last_timestamp = static_cast<scs_timestamp_t>(-1);
     return SCS_RESULT_ok;
 }