Stopping Rotter to crash (I Hope)
sept 7th, 2007 by Prune
I talked about Rotter in a previous post. This litte app connect to a Jack plug and record the audio to a file, in mp3, wav or other depending how you compiled it.
This app can also be told to spead files in a tree depending of the date, create a new file every X minutes and remove files older than X hours. Great.
But I had some crashed or unexpected exit. A friend of mine, already using it for a month or two said having no problem with it. But digging in the code let us find what the problem may be :
As we are recording in stereo, we are getting 2 ports from Jack. Then we create one stereo file (in wave).
The process is simple :
Inside a loop, check the jack ring-buffer to see if enough data is here. If yes, write a new chunk of sound. If not, wait for some time and check again.
I first found that the Jack ring-buffer check was made only on the first channel. As we are having 2 channels for stereo, what will happen if the first channel is filled up but not the second ?
Let me guess… a crash ?
NO ![]()
This little app is so well built that it will raise a fatal error and exit gracefuly.
So, what should I do ??
To be sure to have something recorded whatever happen I made 2 changes to the code (CC to the author) :
- check for both channels to be filled before writing sound
- only raise an error and not a fatal error, so “no sound” is recorded instead and the app continues to run.
Since then, no exit or crashed occured.
I had another problem. As my rotter is writing on a NFS mount, I also started a rotter to write on the local filesystem, just in case something goes wrong.
Then I started to see defunct processes like this :
goa 2059 20746 0 11:00 ? 00:00:00 [rotter]
The parent of the defuct process was the local rotter, which was still running fine and writing data.
Cutting the story short, I found it was the “deletion” of old files which was causing the problem. Reading through the code, the deletion process is a fork of the main program. But the main process never check for the child to return… so the child process is always hanging around (in a prent point of view).
Adding a quick “wait()” in the code partly solved the problem. I plane to change this to a “waitpid()” but I had to do this quick before going live.
I finaly changed the “wait” time of the loop. By default, rotter read the Jack ringbuffer once every Rotter-buffer-size/4. I changed it to Rotter-buffer-size/10 as my buffer was 40 seconds (so I check the buffer every 4 seconds… not too many).
In this cas, one can ask “why having a such big buffer if you read it less ?”
I don’t have the answer of this good question. Maybe the secret is to have a 8 seconds buffer and check Jack every 2 seconds ?
So there are the diffs to tweak the code :
diff -u rotter-0.5/src/sndfile.c rotter-0.5-new/src/sndfile.c --- rotter-0.5/src/sndfile.c 2007-03-22 17:02:37.000000000 +0100 +++ rotter-0.5-new/src/sndfile.c 2007-09-06 00:26:35.000000000 +0200 @@ -70,9 +70,12 @@ } // Is the enough in the ring buffer? - if (jack_ringbuffer_read_space( ringbuffer[0] ) < desired) { + for (c=0; c<channels;> + { + if (jack_ringbuffer_read_space( ringbuffer[c] ) < desired) { // Try again later return 0; + } } // Get the audio out of the ring buffer @@ -85,7 +88,7 @@ // Copy frames from ring buffer to temporary buffer bytes_read = jack_ringbuffer_read( ringbuffer[c], (char*)tmp_buffer[c], desired); if (bytes_read != desired) { - rotter_fatal( "Failed to read desired number of bytes from ringbuffer." ); + rotter_error( "Failed to read desired number of bytes %d from ringbuffer of %d on channel %d.", desired, bytes_read, c ); } } </channels;>
diff -ru rotter-0.5/src/rotter.c rotter-0.5-RTL/src/rotter.c --- rotter-0.5/src/rotter.c 2007-06-01 03:57:38.000000000 +0200 +++ rotter-0.5-new/src/rotter.c 2008-03-06 12:06:46.000000000 +0100 @@ -200,7 +200,15 @@ return 0; } - if (mkdir(dir, DEFAULT_DIR_MODE) < 0) { + // added by prune + // get the umask value (by stting it to 0) then set it back + mode_t mask = umask (0); + umask (mask); + + // Compute the right mode + mode_t DIR_MODE = ((0777) ^ mask); + + if (mkdir(dir, DIR_MODE) < 0) { if (errno == ENOENT) { // ENOENT (a parent directory doesn't exist) char* parent = strdup( dir ); @@ -219,7 +227,7 @@ // Try again to create the directory if (result==0) { - result = mkdir(dir, DEFAULT_DIR_MODE); + result = mkdir(dir, DIR_MODE); } } else { @@ -382,9 +390,9 @@ // Write some audio to disk int result = encoder->write(); if (result == 0) { - // Sleep for 1/4 of the ringbuffer duration - rotter_debug("Failed to write some audio, sleeping for %f sec.", rb_duration/4); - usleep( (rb_duration/4) * 1000000 ); + // Sleep for 1/10 of the ringbuffer duration + rotter_debug("Failed to write some audio, sleeping for %f sec.", rb_duration/10); + usleep( (rb_duration/10) * 1000000 ); } else if (result < 0) { rotter_fatal("Shutting down, due to encoding error."); break; <pre lang="cpp"> diff -u rotter-0.5/src/deletefiles.c rotter-0.5-new/src/deletefiles.c --- rotter-0.5/src/deletefiles.c 2007-03-22 17:02:37.000000000 +0100 +++ rotter-0.5-new/src/deletefiles.c 2007-09-06 01:29:11.000000000 +0200 @@ -30,6 +30,7 @@ #include <errno.h> #include <sys> +#include <sys> #include <sys> #include <dirent.h> @@ -139,6 +140,9 @@ rotter_info( "Deleting files older than %d hours in %s.", hours, dirpath ); + int mychild; + wait(&mychild); + // Fork a new process child_pid = fork(); if (child_pid>0) { </dirent.h></sys></sys></sys></errno.h>