RandomInsano
RandomInsano

Reputation: 1260

c alternative to signal() + alarm()

I'm building some FastCGI apps and it sort of bugs me that lighttpd doesn't kill them off after they've been idle, so I'm trying to have them close on their own.

I tried using

signal(SIGALRM, close);
alarm(300);

and having the close function execute exit(0), and that works almost well.

The problem is the close function is being called every time the main program loop runs though (I call alarm(300) each loop to reset it). I've read the man page for alarm() and it doesn't seem as though calling it multiple times with the same value should trip SIGALRM so I'm assuming Lighttpd is sending an alarm signal.

The big question! Is there a way to run a method after a specific interval, and have that interval be resettable without SIGALRM? I'd be nice if I could have multiple alarms as well.

Here's the whole app thus far:

#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include "fcgiapp.h"

FCGX_Stream     *in, *out, *err;
FCGX_ParamArray envp;
int calls = 0;

void print(char*, ...);
void close();

int main(void)
{
        // If I'm not used for five minutes, leave
        signal(SIGALRM, close);

        int reqCount = 0;

        while (FCGX_Accept(&in, &out, &err, &envp) >= 0)
        {
                print("Content-type: text/plain\r\n\r\n");

                int i = 0;
                char **elements = envp;
                print("Environment:\n");
                while (elements[i])
                        print("\t%s\n", elements[i++]);

                print("\n\nDone. Have served %d requests", ++reqCount);
                print("\nFor some reason, close was called %d times", calls);

                alarm(300);
        }

        return 0;
}

void print(char *strFormat, ...)
{
        va_list args;
        va_start(args, strFormat);
        FCGX_VFPrintF(out, strFormat, args);
        va_end(args);
}

void close()
{
        calls++;
//      exit(0);
}

Upvotes: 3

Views: 3103

Answers (6)

user233009
user233009

Reputation: 296

I'd probably use POSIX timers. Timers do not have to use signals. You have a choice between not notifying at all, raising a signal, or running a function as a new thread (which I would do as it will not interfere with fastcgi).

Make sure you include <signal.h> and <time.h>, and link with -lrt

First, I'd fill out your sigevent structure:

struct sigevent myTimerSignal = {
    .sigev_notify = SIGEV_THREAD,
    .sigev_notify_function = close //Make sure you change your function declaration to close(union sigval), you do not need to use the sigval unless you store data in your event too
};

Now create your timer:

timer_t myTimer;
if(timer_create(CLOCK_REALTIME, &myTimerSignal, &myTimer)){
    //An error occurred, handle it
}

Lets arm it, it will call close() in a new thread in 300 seconds:

struct itimerspec timeUntilClose = {
    .it_value = {
        .tv_sec = 300 //300 seconds
    }
};

if(timer_settime(myTimer, 0, &timeUntilClose, NULL)){
    //Handle the error
}

Now, you should have a timer ready to stop the program after 300 seconds. I know I may be late, but I hope this helps a future reader.

Upvotes: 2

Test
Test

Reputation: 1727

the best way is: add a thread so that you can remove signal and alarm, and sync the thread and your main code (main thread).

Upvotes: 2

eyalm
eyalm

Reputation: 3366

  • Try to close all the file descriptors (including stdin and stdout). This should close the CGI instance if its idle.
  • You can use select() with timeout to schedule instead of SIGALRM

Upvotes: 0

RandomInsano
RandomInsano

Reputation: 1260

Here's a solution that sort of avoids the point of the question, but it works. It will respond to only my application's signal events:

void close(int intSignal, siginfo_t *info, void *context)
{
        // For some stupid reason MY signal doesn't populate siginfo_t
        if (!info)
        {
                count++;
        }
}

If the siginfo struct is empty, that's because alarm() tripped it. If an outside process does it, siginfo_t.si_pid is populated with zero.

I still don't like this solution, but it works. Odd problem now is that doing an exit(0) doesn't close the application, though lighttpd thinks it's gone and spawns another. This means that now I've got rouge processes. raise(SIGUSR1) which is what is supposed to stop FastCGI scripts doesn't seem to do the trick either... hmmm...

Question still remains: How does one call asynchronous functions on an interval timer without the use of signals?

Upvotes: 0

caf
caf

Reputation: 239011

The argument to the alarm call is seconds, not minutes. So you're asking to be woken up in 5 seconds after each time through the main loop.

Upvotes: 0

Igor
Igor

Reputation: 27250

Maybe you can wrap the close function by another function which will first call sleep()?

Upvotes: 0

Related Questions