Thread: 2-3 Second delay with alarm()

  1. #1
    Registered User
    Join Date
    Sep 2002
    Posts
    52

    2-3 Second delay with alarm()

    Hi, sorry to post so quickyl again, I just have another quite simple question.

    The way I'm currently doing timers is using alarm(1); then when SIGALRM is received, I have a handle that is setup to handle it and do do_timers();

    Timers are currently stored in a structure with how many times it is meant to be run, how long the interval is, the function to be called, the name of it and the data for the called function.

    Basically, all do_timers() does is loop through that structure and perform the necessary functions (if the time is passed) and remove any now unwanted timers. After this is done, it then calls alarm(1); again.

    In theory, it should loop through this structure every second. I can't use sleep() or pause() because the program cannot be halted, so I thought the only things I can use are setitimer() and alarm(). There is only one item stored in the structure and it has a 2-3 second delay in performing it, I'd imagine the delay would be quite large if there were thousands of items in there, but not one.

    When I set it up so it sent the alarm and reported when it was received, that worked fine, I tried multiple times with different lengths of time and it was fine, but as soon as I do this, it puts a delay on it. Maybe my looping is way too slow, here is what I have for adding and looping through the timers (and the structure)...

    Code:
    /* struct.h */
    typedef struct zTimer aTimer;
    struct zTimer {
        char		*name;
        int			(*func)();
        time_t		lastrun;
        int			interval;
        int			times;
        int			run;
        MYSQL_ROW	row;
        char		*ch;
        aTimer		*next, *prev;
    };
    
    
    /* timers.c */
    aTimer *alltimers = (aTimer*) NULL;
    
    int timer_add(char *Name, int Times, int Interval, int(*func)(), MYSQL_ROW r, char *chr) {
    	aTimer *t;
    
    	if(Name) {
    		t = (aTimer*) find_timer(Name);
    		if(t)
    			timer_del(t);
    	}
    
    	t = MyMalloc(sizeof(aTimer));
    
    	AllocNCpy(t->name, Name);
    	t->func = func;
    	t->lastrun = time(NULL);
    	t->interval = Interval;
    	t->times = Times == 0 ? 0 : Times;
    	t->run = 0;
    
    	t->prev = NULL;
    	t->next = alltimers;
    	t->row= r;
    	t->ch = chr ? strdup(chr) : NULL;
    
    	if(alltimers)
    		alltimers->prev = t;
    	alltimers = t;
    	return(1);
    }
    
    void do_timers() {
    	aTimer *t;
    	time_t curTime = time(NULL);
    	int i, x;
    
    	for(t = alltimers; t; t = t->next) {
    		if((int)(t->lastrun + t->interval) <= curTime) {
    			i = t->func(t->row, t->ch);
    //			x = t->times - 1;
    			x = t->times;
    			if(!i || (t->times && x && t->run && (t->run >= x)))
    				timer_del(t);
    			else {
    				t->lastrun = curTime;
    				t->run++;
    			}
    		}
    	}
    	alarm(1);
    	return;
    }
    
    /* Other stuff */
    void handle_alrm() {
    	do_timers();
    	return;
    }
    
    int main() {
    	struct  sigaction alrm;
    
    	alrm.sa_handler = handle_alrm;
    	alrm.sa_flags = 0;
    
    	(void)sigaddset(&alrm.sa_mask, SIGALRM);
    	(void)sigaction(SIGALRM, &alrm, NULL);
    
    	/* Unneccesary code cut out */
    
    	alarm(1);
    	return(0);
    }
    Any help would be greatly appreciated
    - Daniel Wallace

  2. #2
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    I don't know anything about alarm() and how it works, but are you sure you called timer_add()? I don't see in the code posted where it's called.

  3. #3
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    If t->func() involves a network call to retrieve a row of information from a MySql database, a second of latency doesn't seem unreasonable. You could try calling alarm(1) before the for loop, although I don't know the consequences of this.

  4. #4
    Registered User
    Join Date
    Sep 2002
    Posts
    52
    I think if I call alarm(1); before the loop everything will get banked up.
    It doesn't call the row from the MySQL database, that is called when timer_add(); is used, it just stores the array in there, basically the same as having char *blah[];, but it's better to use the in-built functions (in my opinion).

    Here is some code that I forgot to paste:

    Code:
    #define AllocNCpy(x, y) x = (char *) MyMalloc(strlen(y) + 1); strcpy(x,y);
    #define MyMalloc(x) malloc(x)
    #define MyFree(x)	free(x)
    
    /* An example timer_add(); */
    int ex_1() {
        MYSQL_RES *res;
        MYSQL_ROW row;
        char query_string[512];
        int blah_id = 4;
    
        //mysql_init() and mysql_real_connect() has already been done
    
        sprintf(query_string, "SELECT * FROM blah");
        if(mysql_query(&mysql, query_string)) {
            //Error
            return(1);
        }
        res = mysql_store_result(&mysql);
        for(row = mysql_fetch_row(res); row; row = mysql_fetch_row(res)) {
            if(blah_id == atoi(row[0]))
                break; //as row is set
        }
        if(!res)
            mysql_free_result(res);
    
        //No need for mysql_close() as it is a global database connection
    
        return(timer_add("blah1", 1, 3600, updatedb_newuser, row, "moose"));
    }
        //Then there would be some function int updatedb(MYSQL_ROW r, char *newuser);
    Also, with the t->ch = chr ? strdup(chr) : NULL, I used that because you cant use strcpy (AllocNCpy) with NULL and for t->(char *)blah you need to allocate the memory with strdup (as far as I know).

    I've also added in 'if((t = MyMalloc(sizeof(aTimer)) == NULL) error' checking too, sorry about that
    - Daniel Wallace

  5. #5
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I still don't see why there would be a delay. Is there anything in the one second t->func()that could be causing a delay?
    --
    However, a couple of things I noticed in your code.
    Code:
       if(!res)
            mysql_free_result(res);
    You're not freeing res here so it is leaking. You can't free it here anyway, as you will be using row later. You need to come up with some scheme to free res when you are finished with it(or knowingly leak it).
    --
    Couldn't you optimise the query with:
    Code:
       sprintf(query_string, "SELECT * FROM blah WHERE blah_col = 4");

  6. #6
    Registered User
    Join Date
    Sep 2002
    Posts
    52
    Sorry, but I'm confused, mysql_free_result(res); does free the result, doesn't it?

    And row is already stored, so freeing the actual result won't matter. I'm assuming so because MYSQL_ROW is an array, so freeing where it came from won't matter, unless I'm wrong, which is quite possible :P

    I could use
    Code:
    sprintf(query_string, "SELECT * FROM blah WHERE blah_col = 4");
    but that was an example, I have used that where I need to, but this time was an error on my part.

    And regarding the 'anything in t->func()' that could be causing the delay, not that I can see, it does it with all of them. I made
    Code:
    int testing(MYSQL_ROW r, char *test) {
        printf("Time function went off %lu\n", time(NULL));
        return(0);
    }
    And for some reason it still does it.
    Actually, it might be because of a socket loop I have, as it continually reads from a socket. Here is the code for that (and please, any suggestions on cleaning it up would be great):

    Code:
    fd_set readfds, nullfds;
    int servsocik = 0;
    
    void read_loop()
    {
    	register int i, j, SelectResult;
    	struct timeval TimeOut;
    	char c;
    	char buf[BUFSIZE];
    
    	for(;;) {
    
    //		chk();
    
    		memset(buf, '\0', BUFSIZE);
    		FD_ZERO(&readfds);
    
    		TimeOut.tv_sec = 500;
    		TimeOut.tv_usec = 0L;
    		FD_SET(servsock, &readfds);
    
    		if((SelectResult = select(FD_SETSIZE, &readfds, &nullfds, &nullfds, &TimeOut)) > 0) {
    			for(j = 0; j < BUFSIZE; j++) {
    				if(FD_ISSET(servsock, &readfds)) {
    					i = read(servsock, &c, 1);
    					if(i !=0) {
    						buf[j] = c;
    						if ((c == '\n') || (c == '\r')) {
    							parse(buf); // external function
    							break;
    						}
    					}
    					else
    						return;
    				}
    				else
    					break;
    			}
    		}
    		else {
    			if(SelectResult == 0) {
    				log("Lost connection to server.");
    				return;
    			}
    		}
    	}
    }
    - Daniel Wallace

  7. #7
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> Sorry, but I'm confused, mysql_free_result(res); does free the result, doesn't it? <<

    Yep, but you have:
    Code:
        if(!res)
            mysql_free_result(res);
    which is equivalent to:
    Code:
        if(res == NULL)
            mysql_free_result(res);
    and if res is NULL, there is nothing to free. I think this is just a typo on your behalf.

    >> And row is already stored, so freeing the actual result won't matter. I'm assuming so because MYSQL_ROW is an array, so freeing where it came from won't matter, unless I'm wrong, which is quite possible :P <<

    MYSQL_ROW is a pointer to an array of pointers. As far as I can tell, these pointers reference memory controlled by the MYSQL_RES. When you free the result set you free all the rows and data in the result set.
    The MySql manual rather ambiguously says:
    Do not attempt to access a result set after freeing it.
    Otherwise, you would have to free each row seperately.
    --
    Can you run something like below and see what is going on:
    Code:
    struct timeval tod;
    
    gettimeofday(&tod, NULL);
    printf("before: %d.%d\n", tod.tv_sec, tod.tv_usec);
    
    	for(t = alltimers; t; t = t->next) {
    		if((int)(t->lastrun + t->interval) <= curTime) {
    			printf("Running %s\n", t->name);
    
    			i = t->func(t->row, t->ch);
    //			x = t->times - 1;
    			x = t->times;
    			if(!i || (t->times && x && t->run && (t->run >= x)))
    				timer_del(t);
    			else {
    				t->lastrun = curTime;
    				t->run++;
    			}
    		}
    		else
    		{
    			printf("Not running %s. lastrun=%d, curTime=%d\n", t->name, t->lastrun, curTime);
    		}
    	}
    
    gettimeofday(&tod, NULL);
    printf("after: %d.%d\n", tod.tv_sec, tod.tv_usec);

  8. #8
    Registered User
    Join Date
    Sep 2002
    Posts
    52
    Yeah, for some strange reason I thought that the MySQL manual said to free it if you had !result, which didn't make sense to me, but oh well. Thanks for picking that up.

    I did the debugging info, here is some of what was displayed:

    before: 1083628103.552941
    after: 1083628103.555295
    before: 1083628104.552987
    after: 1083628104.563100
    Storing timer test_1
    before: 1083628164.672922
    after: 1083628164.673637
    before: 1083628165.672945
    Running test_1
    after: 1083628165.683723
    before: 1083628166.682909
    after: 1083628166.684135

    I pasted some before the timer was stored, while it was stored and after it was run. Seems to be having a 600-750 millisecond delay between timer checks, which must be the amount of time it takes to run through the timers stored
    - Daniel Wallace

  9. #9
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Well it seems that do_timers() is running once per second and looping through the timers and running test_1->func() is taking only a little over 10 ms, so there is no problem there.

    The question is, assuming test_1 is scheduled to run once per second:

    Storing timer test_1
    before: 1083628164.672922
    Why didn't test_1 run here?
    after: 1083628164.673637

    before: 1083628165.672945
    Running test_1
    after: 1083628165.683723

    before: 1083628166.682909
    Why didn't test_1 run here?
    after: 1083628166.684135
    which would imply that the condition
    Code:
    	if((int)(t->lastrun + t->interval) <= curTime)
    is not being met.

    Could you put in the else to see why not?
    Code:
    		else
    		{
    			printf("Not running %s. lastrun=%d, curTime=%d\n", t->name, t->lastrun, curTime);
    		}
    Maybe the time() function is not accurate enough on your machine?

  10. #10
    Registered User
    Join Date
    Sep 2002
    Posts
    52
    Here is some results with all running and not running information:

    before: 1083629749.643156
    after: 1083629749.644111
    before: 1083629750.643096
    after: 1083629750.644049
    before: 1083629751.642899
    after: 1083629751.643914
    Storing timer test_1
    before: 1083629752.642871
    Not running test_1. lastrun=1083629752, curTime=1083629752
    after: 1083629752.646055
    before: 1083629753.642848
    Not running test_1. lastrun=1083629752, curTime=1083629753
    after: 1083629753.646020
    before: 1083629754.652874
    Not running test_1. lastrun=1083629752, curTime=1083629754
    after: 1083629754.654639
    ...
    before: 1083629812.733247
    Running cban_#coder.dev_1
    after: 1083629812.774184
    before: 1083629813.772880
    after: 1083629813.773936
    before: 1083629814.773043
    after: 1083629814.774885

    This is really weird, I don't know why there is such a huge gap, perhaps the tiem difference just adds up. That will be quite bad when there is huge amounts of timers' stored. I'm thinking about storing them in a hash or something like that, that might be a little faster, what do you think? I don't expect it to do 4,000,000 timers instantaneously, but surely one timer with a duration of one minute shouldn't have a 5+ second delay.

    The question is, assuming test_1 is scheduled to run once per second:
    It's actually scheduled to run once, one minute later after it is stored. But it is running 65-68 seconds later, which isn't good. This is quite mind boggling, but I bet the answer is quite simple
    Last edited by Longie; 06-19-2004 at 08:38 AM.
    - Daniel Wallace

  11. #11
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    According to the times you have posted, the timer is stored at around 1083629752 and run at 1083629812.733247. By my calculation:
    Code:
    1083629812.733247 - 1083629752 = 60.733247
    It is probably closer to 60.15 but we don't have the fraction portion of the time it was stored. If you put the alarm() call before the loop and remove the printf() calls(which are very time consuming) you should get it down to 60.0???, even with several hundred timers.

    >> But it is running 65-68 seconds later <<
    Sorry, I just don't see where you are getting these figures from. Could you elaborate?

  12. #12
    Registered User
    Join Date
    Sep 2002
    Posts
    52
    >> But it is running 65-68 seconds later <<
    Sorry, I just don't see where you are getting these figures from. Could you elaborate?
    My guess is that because it is taking 10 milliseconds to check through all the timers, by the time one minute is up, seen as it is checking once a second, 10 x 60 == 600, which is 6 seconds (unless there is 6000 milliseconds in a second), hence the delay. I meant 65-68 seconds later as opposed to the 60 seconds which it is supposed to be. My guess is there is no way to remove the delay, checking once every 2 seconds would make the 600 milliseconds go down to 300, but it would still be the same in the long run.
    - Daniel Wallace

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Regarding delay in the connection
    By byatin in forum Networking/Device Communication
    Replies: 2
    Last Post: 08-19-2008, 02:59 PM
  2. temperature sensors
    By danko in forum C Programming
    Replies: 22
    Last Post: 07-10-2007, 07:26 PM
  3. Networking (queuing delay, avg packet loss)
    By spoon_ in forum A Brief History of Cprogramming.com
    Replies: 0
    Last Post: 09-05-2005, 11:23 AM
  4. ANN: The Fourth Contest: Alarm Clock, sign up here
    By ygfperson in forum A Brief History of Cprogramming.com
    Replies: 59
    Last Post: 08-10-2002, 12:24 AM
  5. Delay
    By CanadianOutlaw in forum C++ Programming
    Replies: 4
    Last Post: 09-13-2001, 06:28 AM