[C] signal.h, data racing, thread-safe multithreading programs (pthread)

@Dexter, @matt:
hi,
in your C examples you are using signal.h
as to data racing: signal() may cause an undefined behaviour in multithreading programs (pthread). Additionally, if still either threads are running then they probably will not be terminated and joined before exitting.
In my own programs I am using this following program tool instead, providing
kbhit()
to check for intermediate key strokes without waiting (e.g., ESC ==ASCII 27).

If you wish you may try this for a clean terminating instead of (or additionally to) ctrl+c :

#include <stdbool.h>  // boolean types and values
#include <stdio.h>    // files, keyboard IO (getchar())
#include <termio.h>   // low level XBD (termios, ioctl)
#include <unistd.h>   // POSIX/LINUX symbolic constants and types
#include <string.h>   // strings+arrays (memcpy())

bool kbhit(void)
{
    struct termios original;
    tcgetattr(STDIN_FILENO, &original);

    struct termios term;
    memcpy(&term, &original, sizeof(term));

    term.c_lflag &= ~ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);

    int characters_buffered = 0;
    ioctl(STDIN_FILENO, FIONREAD, &characters_buffered);

    tcsetattr(STDIN_FILENO, TCSANOW, &original);

    bool pressed = (characters_buffered != 0);

    return pressed;
}

example:




while(true) {
   if ( kbhit() ) {
      int key = getchar();
      if (key==27) {    // ESC == ASCII 27
         //  optionally set semaphores or flags, then
         break();
      }
   }
   // rest of the code
}

//...
// then wait for threads to join, 

// then close communication, reset, and clean up:
BP.reset_all();    // Reset everything so there are no run-away motors

//...
// then finally:
exit(0);

proposal:
example code for a Brickpi3 multithreading setup

compile/build flags:
-pthread -lwiringPi

wiringPi Installation: http://wiringpi.com/download-and-install/

share and enjoy! :slight_smile:

/*
 * example code for a Brickpi3 multithreading setup
 * version 0.0.4b
*/

#include <stdbool.h>    // boolean types and values
#include <stdio.h>      // files, keyboard IO (getchar())
#include <termio.h>     // low level XBD (termios, ioctl)
#include <unistd.h>     // POSIX/LINUX symbolic constants and types
#include <string.h>     // strings+arrays (memcpy())
#include <pthread.h>    // multithreading 
#include <wiringPi>     // GPIOs, delay, millis

#include "BrickPi3.cpp" // for BrickPi3
#include <signal.h>     // for catching exit signals


volatile int MT_active=1, Ts1=0, Ts2=0, Ts3=0;  // semaphores to control and monitor MT

//===============================================
// create BrickPi instance
//===============================================

BrickPi3 BP;


//===============================================
// Signal handler to be called when Ctrl+C is pressed 
//===============================================

void exit_signal_handler(int signo){
  if(signo == SIGINT){
     MT_active = 0;     // signalize threads to terminate by themselves  
     fprintf( stdout, "\naborted by [CTRL+C]\n" );      // optional
  }
}




//===============================================
// mimics conio.h kbhit()
//===============================================

bool kbhit(void)
{
    struct termios original;
    tcgetattr(STDIN_FILENO, &original);
    struct termios term;
    memcpy(&term, &original, sizeof(term));
    term.c_lflag &= ~ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);
    int characters_buffered = 0;
    ioctl(STDIN_FILENO, FIONREAD, &characters_buffered);
    tcsetattr(STDIN_FILENO, TCSANOW, &original);
    bool pressed = (characters_buffered != 0);

    return pressed;
}

//===============================================
//  single threads & MT thread control
//===============================================

pthread_mutex_t  mutexBP;

void* thread1Name(void *) {
  Ts1=1;     // indicate thread 1 was started

  while(MT_active) {
    ///... (code/loop)
    delay(1);    // optional, arbitrarily
  }
  Ts1=0;     // indicate thread 1 was stopped
  fprintf( stdout, "\nthread 1 exit message\n" );      // optional
  return NULL;
}


void* thread2Name(void *) {
  Ts2=1;     // indicate thread 2 was started

  while(MT_active) { 
    ///... (code/loop)   
    delay(10);    // optional, arbitrarily
  }
  Ts2=0;     // indicate thread 2 was stopped
  fprintf( stdout, "\nthread 2 exit message\n" );      // optional
  return NULL;
}


void* thread3Name(void *) {
  Ts3=1;     // indicate thread 3 was started

  while(MT_active) {
    ///... (code/loop)
    delay(100);    // optional, arbitrarily
  }
  Ts3=0;     // indicate thread 3 was stopped
  fprintf( stdout, "\nthread 3 exit message\n" );      // optional
  return NULL;
}

//===============================================
// main()
//===============================================

int main() {

    signal(SIGINT, exit_signal_handler);     // register the exit function for Ctrl+C 
    pthread_mutex_init (&mutexBP, NULL);     //  init a mutex for Brickpi access
    
    //----------------------------------------
    // mutex handling: lock and unlock
    //----------------------------------------
    pthread_mutex_lock (&mutexBP);            // lock the following variable operations by the BrickPi mutex
    
       // Make sure that the BrickPi3 is communicating and that the firmware is compatible with the drivers:
       BP.detect(); 
       // Reset the encoders:
       BP.offset_motor_encoder(PORT_A, BP.get_motor_encoder(PORT_A));
       BP.offset_motor_encoder(PORT_B, BP.get_motor_encoder(PORT_B));
       BP.offset_motor_encoder(PORT_C, BP.get_motor_encoder(PORT_C));
       BP.offset_motor_encoder(PORT_D, BP.get_motor_encoder(PORT_D));
    
    pthread_mutex_unlock (&mutexBP);       // release the mutex to write/read by different threads

     
    //----------------------------------------
    // create and start the pthread threads
    //----------------------------------------
    pthread_t   tid1, tid2, tid3;   // thread IDs
   
    pthread_create(&tid1, NULL, thread1Name, NULL);   
    pthread_create(&tid2, NULL, thread2Name, NULL);   
    pthread_create(&tid3, NULL, thread3Name, NULL);

    delay(1);  // give the threads a chance to start
    
    //----------------------------------------
    // keyboard handler: terminate by ESC key
    //----------------------------------------
    while(MT_active) {
      if ( kbhit() ) {
        int key = getchar();
        if (key==27) {    // ESC == ASCII 27
           MT_active = 0;
           fprintf( stdout, "\naborted by [ESC]\n" );      // optional
           break();
        }
      }    
      // optional: rest of a perpetual main() code
      // instead, put BP code into a proprietary thread
      delay(100);  // optional
    }


    //----------------------------------------
    // wait for threads to join before exiting
    //----------------------------------------
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    //----------------------------------------
    // reset and then terminate
    //----------------------------------------
    pthread_mutex_lock (&mutexBP);  // not compellingly required here, just to demonstrate mutexes
       BP.reset_all();    // Reset everything so there are no run-away motors
    pthread_mutex_unlock (&mutexBP);  
    
    return 0;
}

edited, c+p mistake