Search code examples
cmultithreadingmutexsemaphoreinterprocess

wait until the previous instance of process finish


I am new to the world of Linux C Programming so request your patience. Found several threads about inter-process synchronization (same process but different instance) using mutex and semaphores but not exactly matching my case. I tried to follow them and tried to create few samples but none of them is working for me.

Finally posting here to get some help.

I am working on to create a utility which will executed over Ethernet telnet session. As noted below in USAGE comment, 1st call will pass command line argument -init which will start a thread which will be running always. Following -init all call will have -c: argument specified with different hex code.

The problem is when one instance is still working on another call comes with different -c: hex code value. Sometimes this is creating a problem that the response returned by second call gets returned to the 1st call. (This is because the end device is sending response to 2nd command quickly while 1st command is still in-progress)

Looking for some psudo code or reference which help me synchronize the part of code which can prevent sending 2nd command before receiving response of 1st command.

Hope I explained it properly.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>

static volatile sig_atomic_t isRunning = 1;

/* Signal handler */
void signal_handler(int signal) {
    switch (signal) {
    case SIGINT:
    case SIGTERM:
    case SIGQUIT:
        /* Graceful shutdown */
        isRunning = 0;
        break;
    default:
        break;
    }
}

void* random_generator(void* data) {
    fd_set readfd;
    struct timeval timeout;
    char buff[20];
    struct tm *sTm;

    do {
        time_t now = time(0);
        sTm = gmtime(&now);

        strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", sTm);
        printf("%s\n", buff);

        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        int ret = select(0, NULL, NULL, NULL, &timeout);
        printf("Select returned: %d\n", ret);

    } while (isRunning);

    printf("Exiting thread...");
    pthread_exit((void*) 0);
}

int main(int argc, char** argv) {
    /*
     * USAGE:
     * 1st command -> ./util -init
     * 2nd command -> ./util -c:<hexcode>
     * 3rd command -> ./util -c:<hexcode>
     * .......
     */
    pthread_t mythread;

    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);

    if (argc == 2)
        return 0;

    if (strcmp(argv[1], "-c:") == 0) {
        // TODO: Only one process should be executing this part at any point of time
        ...lock();
        int count = 0;
        do{
            printf("Processing...%d\n", count);
        }while(count++ < 30);
        ...unlock();
        return 0;
    } else if (strcmp(argv[1], "-init") == 0) {
        // always running thread
        printf("Starting thread...\n");
        int ret = pthread_create(&mythread, NULL, random_generator, (void*) NULL);
        if (ret)
            printf("Failed starting thread\n");
        else
            printf("Thread started\n");

        pthread_join(mythread, NULL);
    }
    return 0;
}

Solution

  • This is how I got it working by implementing interprocess semaphore. Hopefully it will help somebody save their time and learn something. Below is the updated code. Perfectly working on Ubuntu 14.04 LTS.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <semaphore.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <time.h>
    #include <ctype.h>
    
    static volatile sig_atomic_t isRunning = 1;
    #define SEM_NAME "mutex2"
    static sem_t *mutex;
    
    /* Signal handler */
    void signal_handler(int signal) {
        switch (signal) {
        case SIGINT:
        case SIGTERM:
        case SIGQUIT:
            /* Graceful shutdown */
            isRunning = 0;
            break;
        default:
            break;
        }
    }
    
    void* random_generator(void* data) {
        fd_set readfd;
        struct timeval timeout;
        char buff[20];
        struct tm *sTm;
        int rc;
    
        do {
            time_t now = time(0);
            sTm = gmtime(&now);
    
            strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", sTm);
            printf("%s\n", buff);
    
            timeout.tv_sec = 1;
            timeout.tv_usec = 0;
            int ret = select(0, NULL, NULL, NULL, &timeout);
            printf("Select returned: %d\n", ret);
    
        } while (isRunning);
    
        printf("Exiting thread...\n");
        pthread_exit((void*) 0);
    }
    
    int main(int argc, char** argv) {
        /*
         * USAGE:
         * 1st command -> ./util -init
         * 2nd command -> ./util -c:<hexcode>
         * 3rd command -> ./util -c:<hexcode>
         * .......
         */
        pthread_t mythread;
        int rc;
    
        signal(SIGTERM, signal_handler);
        signal(SIGINT, signal_handler);
        signal(SIGQUIT, signal_handler);
    
        if (argc != 2)
            return 1;
    
        if (strcmp(argv[1], "-c:") == 0) {
            // TODO: Only once process should be executing this part at any point of time
            mutex = sem_open(SEM_NAME, O_CREAT);
            if (mutex == SEM_FAILED) {
                printf("sem_open error - %d (%m)\n", errno);
                return 1;
            }
            printf("sem_open success\n");
    
            printf("calling sem_wait\n");
            rc = sem_wait(mutex);
            if(rc < 0){
                printf("sem_wait error - %d (%m)\n", errno);
                return 1;
            }
    
            int i;
            for (i = 0; i < 10; i++){
                printf("process %d %d\n", getpid(), i);
                sleep(1);
            }
    
            printf("sem_post calling\n");
            rc = sem_post(mutex);
            if(rc < 0){
                printf("sem_post error - %d (%m)\n", errno);
                return 1;
            }
    
            return 0;
    
        } else if (strcmp(argv[1], "-init") == 0) {
    
            // always running thread
            printf("Starting thread...\n");
            int ret = pthread_create(&mythread, NULL, random_generator, (void*) NULL);
            if (ret)
                printf("Failed starting thread\n");
            else
                printf("Thread started\n");
    
            // open semaphore
            mutex = sem_open(SEM_NAME, O_CREAT);
            if (mutex == SEM_FAILED) {
                printf("sem_open error - %d (%m)\n", errno);
                sem_close(mutex);
                sem_unlink(SEM_NAME);
                return 1;
            }
            printf("sem_open success\n");
    
            rc = sem_init(mutex, 1, 1);
            if(rc < 0){
                printf("sem_init error - %d (%m)\n", errno);
                sem_close(mutex);
                sem_unlink(SEM_NAME);
                return 1;
            }
            printf("sem_init success\n");
    
            // join thread
            pthread_join(mythread, NULL);
    
            printf("Unlink semaphore...\n");
            rc = sem_close(mutex);
            if(rc < 0){
                    fprintf(stdout, "sem_close error - %d (%m)\n", errno);
            }
            rc = sem_unlink(SEM_NAME);
            if(rc < 0){
                printf("sem_unlink error - %d (%m)\n", errno);
            }
        }
    
        return 0;
    }
    

    Compile and execute commands are below,

    1. $ gcc util.c -o util -pthread
    2. On Terminal 1 run - $ ./util -init
    3. On Terminal 2,3,4 run - $ ./util -c: