Thursday, 19 September 2013

Async safe functions.

Hi again, I am writing this post to point out one of the problems that we faced during our application development.

We have a client-server application.the communication between involved multiple client connected to single server. The operating system that we use is Redhat linux or ubuntu.
The problem was whenever the user wanted to kill a particular client the client application used to hang on 
strange futex statement. Because of this the server also used to hang and we had to restart our application over and over again. This problem was frequent (like every day once or twice) but not that frequent.

When we looked into it using strace command (strace -p process-id) we found that our code used to hang on below call.
futex(0x5ac9df, FUTEX_WAIT, ....

This error was not reoccurring whenever we tried to kill a client and was in between.
But our application is real-time application and we wanted to solve this problem 
as soon as possible cause it was very annoying and this problem bit us in past but 
was resolved after re-installation of system but this time even after re-installation 
it persisted.

So after a long time of goggling for the issue we struck upon a link:
http://www.fedoraforum.org/forum/showthread.php?t=187375

There it is written that there are only few functions that can be called from signal-
handler and it also gives the list of safe-functions.Meaning the system calls that you
can use in your signal handler safely.

Here is one more link which involves communication on same.

http://stackoverflow.com/questions/2013202/i-need-a-list-of-async-signal-safe-functions-from-glibc
There it is written that which functioins you can safely call from signal handler and the reason for not calling any other functions.
The reason is "If  an unsafe function, and handler calls and unsafe functions then the behaviour is undefined.
 Its time to demonstrate you how we simulated the error and what is the workaround we used.

They program may not hang first few times or may hang in even first attempt.The reason for this is we are calling ctime.The reason is consider our main code as a thread when it enters the while loop it is prepared to call ctime and aquires lock over it and if during this time if we try to kill this program using kill -INT then handler code is invoked and in that again it tries to aquire lock over the ctime call.But it has already aquired lock over it and waiting for himself to release it (which will never happen).
But suppose during the normal execution in while loop and had done call to ctime and reached statement printf then it would have realeased lock over ctime and if at this point of time we would have killed using  kill -INT  then the program would have exited normally.Hence the problem.

Sample code causing the issue :

  exit(0);
}

int main()
{
 
  struct sigaction sa;
  time_t t;
  int counter = 0;

  memset(&sa, 0, sizeof(sa));
  sa.sa_handler = handler;
  sigaction(SIGINT, &sa, NULL);

  while(1) {
    counter++;
    time(&t);
    r = ctime(&t);
    printf("Loop %d\n",counter);
  }

  return 0;
}


Now here is workaround the problem.See the POSIX says you must not call the unsafe functions from signal handlers.So there is no way we can call them and sit comfortably. The simple solution is to use some sort of flag and run the code in main till its false and when the signal is received set the flag true indicating that it has received a kill signal and it should try to exit after doing house cleaning stuff I mean freeing memory, closing file pointers etc.

Here is the way i tried to do it:


volatile char *r;
      printf("Loop %d safe_check %d\n",counter,safe_check);
    }
    else {
      house_cleaning_function();
    }
  }

  return 0;
}


Here safe_check int variable is used as a flag to check to see if the program has received a kill signal and if it has I call callthisfunction
If you run the above code you will see that the safe_check variable should always print 0 in while loop but sometimes it's not the case. You understand the reason why?

Hope this post was useful to you..

Saturday, 8 June 2013

Shared Memory Programming

Hi all,

This is officially the first blog that i went forward to write. I am working as software programmer.So most of the time you will find blogs while me writing something about it or asking for help.

Today i will  write about shared memory. It comes under the section of IPC(ie Inter process communication).There are several ways that to process can communicate. Some ways are via TCP,UDP,semaphores.

We have tried communication via TCP and unix domain sockets and other IPC mechanisms as part of performance improvement but though it was fast, the problem was it produces multiple copies of the same message. Hence we are going forward with using shared memory as it keeps a single copy of the message in the memory which is accessible to both or more(doubt here) communicating parties.To avoid redundancy was the main goal for us to look for the other means of IPC.

Shared memody is memory that may be simultaneously accessed by multiple processes with goal provide communication among them or avoid redundant copies. Shared memory is an efficient means of passing data between programs.

We can have same process accessing the same memory or buffer you can say, with the help of threads.
One thread reading (get) from the memory and other thread writing (put) in memory.

The trick here is you have to use some sort of mechanism to avoid multiple access to same data as it would result in incompleted data. For Eg. if the thread writing data has to put 1 packet (say 500 bytes) of data in
memory it is in middle of writing data ie written only say 250 bytes and during this the other thread tries to read data as both have them have access to that buffer at that time it will result in incomplete data read from the reader thread.

So we need a way of making sure that if one process is using a shared memory, the other processes will be excluded from accessing the same memory and when that process has finished its work with the memory the other process previously denied access may be granted access to same.In this way the cycle will go on.
This is known as Mutual exclusion.

In the above scenario if the process who is granted permission over the shared segment does not release its lock over the memory this would result in chaos known as Deadlock, as the other process will not ever be able to access the shared segment resulting in no work done and waste of resources and time.
So We have to be very careful while implementing locks and mutual exclusion.
Here is one server and client code..

server.c

#include 
#include 
#include 
#include 

#define SHMSZ     27

main()
{
    char c;
    int shmid;
    key_t key;
    char *shm, *s;

    /*
     * We'll name our shared memory segment
     * "5678".
     */
    key = 5678;

    /*
     * Create the segment.
     */
    if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /*
     * Now we attach the segment to our data space.
     */
    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    /*
     * Now put some things into the memory for the
     * other process to read.
     */
    s = shm;

    for (c = 'a'; c <= 'z'; c++)
        *s++ = c;
    *s = NULL;

    /*
     * Finally, we wait until the other process 
     * changes the first character of our memory
     * to '*', indicating that it has read what 
     * we put there.
     */
    while (*shm != '*')
        sleep(1);

    exit(0);
}

client.c

/*
 * shm-client - client program to demonstrate shared memory.
 */
#include 
#include 
#include 
#include 

#define SHMSZ     27

main()
{
    int shmid;
    key_t key;
    char *shm, *s;

    /*
     * We need to get the segment named
     * "5678", created by the server.
     */
    key = 5678;

    /*
     * Locate the segment.
     */
    if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /*
     * Now we attach the segment to our data space.
     */
    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    /*
     * Now read what the server put in the memory.
     */
    for (s = shm; *s != NULL; s++)
        putchar(*s);
    putchar('\n');

    /*
     * Finally, change the first character of the 
     * segment to '*', indicating we have read 
     * the segment.
     */
    *shm = '*';

    exit(0);
}