0

For a school project, the goal is to make a real-time system that exists out of 3 processes, lets say "process calculate" and "process execute". On top of these 2 processes, there is a state holder, lets call it "process StateCommunication", of the 2 processes. whenever the state of the statecommunication changes, the 2 processes should then read the state of the statecommunication, and if it's a state that is used in that process, then an action has to be executed within that process. What is the best way to retrieve the state of the statecommunication process? As this state controls what happens in the 2 other processes. A last thing, all the statecommunication does is adjust the state accordingly to the two other processes, so it doesn't calculate anything whatsoever, this all happens in the other 2 processes.

For the communication between calculate and execute, we've decided to use a FIFO to send the data from the calculation to the executer. But, this doesn't seem to be the optimal solution for the statecommunication, as this should then have to be a FIFO that can be read by multiple processes. But in a FIFO, once the data gets read by one process, all the data disappears from the pipe.

Now my question is, what would be the best way to retrieve the state from the "statecommunication", which can continuously be retrieved by multiple processes?

Kyle
  • 107
  • 7
  • What you describe sounds (for me) like _Interprocess Communication_ (IPC). Hence, I googled "interprocess communication c". 2nd hit looked quite promising: [6 Linux Interprocess Communications](http://tldp.org/LDP/lpg/node7.html). I haven't worked on Linux for a while, never on Minix. Hence, I cannot recommend the most efficient in your case. Btw. and out of curiosity: "once the data gets read by one process, all the data disappears from the pipe." IMHO, this is what I would expect from a pipe. How about using **2** FIFOs? – Scheff's Cat Dec 05 '17 at 17:46

1 Answers1

1

Below is one approach to implementing a state machine in C. In the example below, I used forking to create the processes you mentioned and I used a thread to emulate some input that transitions the state. I've used this pattern for cases where I have a device that implements a state machine with input from some external source.

This can be done a bunch of ways, but the core idea behind this approach is to have the worker threads (or processes) watching a state variable, have a main thread (or process) watching a state transition variable(s) and changing the state variable accordingly, and then an independent thread (or process) that changes the state transition variable(s) according to the external data received.

Keep in mind that it is necessary for the main thread to "reset" the state transition variable so that it doesn't immediately transition out of the new state. I did this by creating a NO OP transition state. Since both the main and ExternalSource write to the state transition variable, I used a mutex for protection.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>

pthread_mutex_t mutex; 


typedef enum _State{
    EXECUTE,    
    IDLE,    
    CALCULATE
} State;


typedef enum _StateTransition{
    TRANS_EXECUTE,    
    TRANS_IDLE,    
    TRANS_CALCULATE,
    TRANS_NOOP
} StateTransition;


void Calculate(State *state_p){
    int flag = 0; // Only calculate once per state transition
    while (1){
        if ((*state_p == CALCULATE) && flag){
            puts("Calculated");
            flag = 0;
        } else if (*state_p != CALCULATE){
            flag = 1;
        }
    }
}


void Execute(State *state_p){
    int flag = 0; // Only execute once per state transition
    while (1){
        if ((*state_p == EXECUTE) && flag){
            puts("executed");
            flag = 0;
        } else if (*state_p != EXECUTE){
            flag = 1;
        }
    }
}


void * ExternalSource(void * param){
    StateTransition * transition_p = (StateTransition*) param;

    // Emulate External input
    while(1){
        sleep(2);
        puts("Transiton to idle");
        pthread_mutex_lock(&mutex);
        *transition_p = TRANS_IDLE;
        pthread_mutex_unlock(&mutex);

        sleep(2);
        puts("Transition to calculate");
        pthread_mutex_lock(&mutex);
        *transition_p = TRANS_CALCULATE;
        pthread_mutex_unlock(&mutex);

        sleep(2);
        puts("Transition to execute");
        pthread_mutex_lock(&mutex);
        *transition_p = TRANS_EXECUTE;
        pthread_mutex_unlock(&mutex);    
    }
}



int main()
{
    pthread_t externalThread;
    int rv;
    pid_t pidExecute;
    pid_t pidCalculate;
    volatile StateTransition transition;
    State * state;

    // Setup Initial Values
    transition = TRANS_NOOP;
    state = mmap(NULL, sizeof(State), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    *state = IDLE;

    // Create Processes for Calculate and Execute
    pidExecute = fork();
    if (pidExecute == 0){
        Execute(state);
    } else{
        pidCalculate = fork();
        if (pidCalculate == 0){
            Calculate(state);
        }
    }

    // Create Thread for Input Emulation
    rv = pthread_create(&externalThread, NULL, ExternalSource, (void*)&transition);
    if (rv){
        perror("ERROR Unable to create thread");
        return -1;
    }


    while(1){ //Implement State Machine

        switch (*state){

        case IDLE:
            if (transition == TRANS_CALCULATE){
                // Do some transition stuff    

                *state = CALCULATE; // Update new state
                pthread_mutex_lock(&mutex);
                transition = TRANS_NOOP; // Reset transition
                pthread_mutex_unlock(&mutex);

            }
            break;

        case CALCULATE:
            if (transition == TRANS_EXECUTE){
                // Do some transition stuff

                *state = EXECUTE; // Update new state        
                pthread_mutex_lock(&mutex);    
                transition = TRANS_NOOP; // Reset transition
                pthread_mutex_unlock(&mutex);
            }
            break;

        case EXECUTE:
            if (transition == TRANS_IDLE){
                // Do some transition stuff

                *state = IDLE; // Update new state            
                pthread_mutex_lock(&mutex);        
                transition = TRANS_NOOP; // Reset transition            
                pthread_mutex_unlock(&mutex);
            }
            break;

        default:
            break;
        }
    }

    return 0;
}

This code does compile, but it is not platform independent. This is really just to demonstrate the idea. If you're going to use this code as a starting place, make sure understand what's going on in each line, or else it will end up taking you more time in the long run.

Adam S
  • 58
  • 5
  • First, sorry for the late response, i've been discussing this solution with the group members, but now the state transitions take place in the ExternalSource thread. Would it be possible for this to happen after the execute/calculate processes have been completed? One more question.. how would you approach this method if the two processes, execute and calculate are no child processes but are instead two unknown processes from the statemachine. Would you then work with pipes? This has helped a lot, so thanks! And if you have any answers for me, then i'd like to hear your opinion on this – Kyle Dec 06 '17 at 15:34
  • Definitely. In general, the state machine is driven by some kind of input. Where that input comes from shouldn't matter. So in your case, maybe you could have to state transition variables, transitionCalc and transitionExec. When a calculation finished, it sets the `transitionCalc = true`, and likewise for Execution. Then the main thread can watch these two variables and make state changes based off those. – Adam S Dec 06 '17 at 17:04
  • As for communicating with independent processes, you'll have to use other forms of IPC than what I used in the example. [Here](http://tldp.org/LDP/lpg/node7.html#SECTION00700000000000000000) is a pretty solid resources to get you started. **Named Pipes** is a good place to start. Also, here's a previous [answer](https://stackoverflow.com/questions/2784500/how-to-send-a-simple-string-between-two-programs-using-pipes/2789967#2789967) that might help. – Adam S Dec 06 '17 at 17:08