Logo Search packages:      
Sourcecode: tucnak2 version File versions  Download package

select.c

/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2006  Ladislav Vaiz <ok1zia@nagano.cz>
    and authors of web browser Links 0.96

    This program is free software; you can redistribute it and/or                                                        
    modify it under the terms of the GNU General Public License                                                          
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"

struct timeval start;

struct thread {
    void (*read_func)(union cba_t);
    void (*write_func)(union cba_t);
    void (*error_func)(union cba_t);
    union cba_t cba;
#ifdef LEAK_DEBUG_LIST
      char *file;
      int line;
#endif      
};

struct thread threads[FD_SETSIZE];

fd_set w_read;
fd_set w_write;
fd_set w_error;

fd_set x_read;
fd_set x_write;
fd_set x_error;

int w_max;

int timer_id = 1;

struct timer {
    struct timer *next;
    struct timer *prev;
    ttime interval;

    void (*func)(union cba_t);
    union cba_t cba;
    int id;
};

/*struct list_head timers = {&timers, &timers};*/
struct timer timers = {&timers, &timers};

ttime get_time(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}


struct bottom_half {
    struct bottom_half *next;
    struct bottom_half *prev;
    void (*fn)(union cba_t);
    union cba_t cba;
};

/*struct list_head bottom_halves = { &bottom_halves, &bottom_halves };*/
struct bottom_half bottom_halves = { &bottom_halves, &bottom_halves };

int register_bottom_half(void (*fn)(union cba_t), union cba_t cba)
{
    struct bottom_half *bh;
    foreach(bh, bottom_halves) if (bh->fn == fn && bh->cba.int_ == cba.int_) return 0;

    if (!(bh = mem_alloc(sizeof(struct bottom_half)))) return -1;
      /*dbg("register_bottom_half(0x%x,0x%x)=0x%x\n", fn, data, bh);*/
    bh->fn = fn;
    bh->cba = cba;
    add_to_list(bottom_halves, bh);
    return 0;
}

void check_bottom_halves()
{
    struct bottom_half *bh;
    void (*fn)(union cba_t);
    union cba_t cba;
    rep:
    if (list_empty(bottom_halves)) return;
    bh = bottom_halves.prev;
    fn = bh->fn;
    cba = bh->cba;
    del_from_list(bh);
    mem_free(bh);
    fn(cba);
    goto rep;
}

#define CHK_BH if (!list_empty(bottom_halves)) check_bottom_halves();
        

ttime last_time;

void check_timers(void)
{
    ttime interval = get_time() - last_time;
    struct timer *t;
    foreach(t, timers) t->interval -= interval;
    ch:
    foreach(t, timers) if (t->interval <= 0) {
        struct timer *tt = t;
        del_from_list(tt);
        tt->func(tt->cba);
        mem_free(tt);
        CHK_BH;
        goto ch;
    } else break;
    last_time += interval;
}

#ifdef LEAK_DEBUG_LIST
int debug_install_timer(char *file, int line, ttime t, void (*func)(union cba_t), union cba_t cba)
#else    
int install_timer(ttime t, void (*func)(union cba_t), union cba_t cba)
#endif    
{
    struct timer *tm, *tt;
#ifdef LEAK_DEBUG_LIST
    tm = debug_mem_alloc(file, line, sizeof(struct timer));
#else            
    tm = mem_alloc(sizeof(struct timer));
#endif        
    if (!tm) return -1;
    tm->interval = t;
    tm->func = func;
    tm->cba = cba;
    tm->id = timer_id++;
#if 0
      dbg("before:\n");
      foreach(tt, timers){
            dbg("    id=%d inter=%d fce=%p\n",tt->id, tt->interval, tt->func);  
      }
#endif      
      
    foreach(tt, timers) if (tt->interval >= t) break;
    add_at_pos(tt->prev, tm);
/*    dbg("__installed timer %d %p t=%d\n",tm->id, func,t );*/
#if 0
      dbg("after:\n");
      foreach(tt, timers){
            dbg("    id=%d inter=%d fce=%p\n",tt->id, tt->interval, tt->func);  
      }
      dbg("\n");
#endif      
    return tm->id;
}

#ifdef LEAK_DEBUG_LIST
void debug_kill_timer(char *file, int line, int id)
#else    
void kill_timer(int id)
#endif    
{
    struct timer *tm;
    int k = 0;
      /*dbg("__kill_timer(%d)\n", id);*/
    foreach(tm, timers) if (tm->id == id) {
        struct timer *tt = tm;
        del_from_list(tm);
        tm = tm->prev;
#ifdef LEAK_DEBUG_LIST        
        debug_mem_free(file, line, tt);
#else        
        mem_free(tt);
#endif        
        k++;
    }
    if (!k) internal("trying to kill nonexisting timer");
    if (k >= 2) internal("more timers with same id");
}


ttime get_timer_time(int id)
{
    struct timer *tm;
    foreach(tm, timers) if (tm->id == id) {
        return tm->interval;
    }
    return -1;
}


void *get_handler(int fd, int tp)
{
    if (fd < 0 || fd >= FD_SETSIZE) {
        internal("get_handler: handle %d >= FD_SETSIZE %d", fd, FD_SETSIZE);
        return NULL;
    }
    switch (tp) {
        case H_READ:    return threads[fd].read_func;
        case H_WRITE:   return threads[fd].write_func;
        case H_ERROR:   return threads[fd].error_func;
        case H_DATA:    return threads[fd].cba.void_; /* todo */
    }
    internal("get_handler: bad type %d", tp);
    return NULL;
}

#ifdef LEAK_DEBUG_LIST
void debug_set_handlers(char *file, int line, int fd, void (*read_func)(union cba_t), void (*write_func)(union cba_t), void (*error_func)(union cba_t), union cba_t cba)
#else
void set_handlers(int fd, void (*read_func)(union cba_t), void (*write_func)(union cba_t), void (*error_func)(union cba_t), union cba_t cba)
#endif
{
    /*dbg("set_handlers(%d,%p,%p,%p,%p)\n",fd,read_func,write_func,error_func,cba.void_);*/
    
    if (fd!=0) sock_debug(fd, "set_handlers %p,%p,%p %p",read_func,write_func,error_func,cba);
    if (fd < 0 || fd >= FD_SETSIZE) {
        internal("set_handlers: handle %d >= FD_SETSIZE %d", fd, FD_SETSIZE);
        return;
    }
    threads[fd].read_func = read_func;
    threads[fd].write_func = write_func;
    threads[fd].error_func = error_func;
    threads[fd].cba = cba;
    if (read_func) FD_SET(fd, &w_read);
    else {
        FD_CLR(fd, &w_read);
        FD_CLR(fd, &x_read);
    }
    if (write_func) FD_SET(fd, &w_write);
    else {
        FD_CLR(fd, &w_write);
        FD_CLR(fd, &x_write);
    }
    if (error_func) FD_SET(fd, &w_error);
    else {
        FD_CLR(fd, &w_error);
        FD_CLR(fd, &x_error);
    }
    if (read_func || write_func || error_func) {
        if (fd >= w_max) w_max = fd + 1;
    } else if (fd == w_max - 1) {
        int i;
        for (i = fd - 1; i >= 0; i--)
            if (FD_ISSET(i, &w_read) || FD_ISSET(i, &w_write) ||
                FD_ISSET(i, &w_error)) break;
        w_max = i + 1;
    }
#ifdef LEAK_DEBUG_LIST
      threads[fd].file=file;
      threads[fd].line=line;
#endif      
}

#define NUM_SIGNALS 32

struct signal_handler {
    void (*fn)(union cba_t);
    union cba_t cba;
    int critical;
};

int signal_mask[NUM_SIGNALS];
struct signal_handler signal_handlers[NUM_SIGNALS];

int critical_section = 0;

void check_for_select_race(void);

void got_signal(int sig)
{
    if (sig >= NUM_SIGNALS || sig < 0) {
        error("ERROR: bad signal number: %d", sig);
        return;
    }
    /* for debugging of unknown error */
    if (sig == 11) {
        sig_segv(CBA0);
        return;
    }
    if (!signal_handlers[sig].fn) return;
    if (signal_handlers[sig].critical) {
        signal_handlers[sig].fn(signal_handlers[sig].cba);
        return;
    }
    signal_mask[sig] = 1;
    check_for_select_race();
}

void install_signal_handler(int sig, void (*fn)(union cba_t), union cba_t cba, int critical)
{
    struct sigaction sa;
    if (sig >= NUM_SIGNALS || sig < 0) {
        internal("bad signal number: %d", sig);
        return;
    }
    memset(&sa, 0, sizeof sa);
    if (!fn) sa.sa_handler = SIG_IGN;
    else sa.sa_handler = got_signal;
    sigfillset(&sa.sa_mask);
    /*sa.sa_flags = SA_RESTART;*/
    if (!fn) sigaction(sig, &sa, NULL);
    signal_handlers[sig].fn = fn;
    signal_handlers[sig].cba = cba;
    signal_handlers[sig].critical = critical;
    if (fn) sigaction(sig, &sa, NULL);
}

int pending_alarm = 0;

void alarm_handler(union cba_t cba)
{
    pending_alarm = 0;
    check_for_select_race();
}

void check_for_select_race(void)
{
    if (critical_section) {
#ifdef SIGALRM
        install_signal_handler(SIGALRM, alarm_handler, CBA0, 1);
#endif
        pending_alarm = 1;
#ifdef HAVE_ALARM
        /*alarm(1);*/
#endif
    }
}

void uninstall_alarm(void)
{
    pending_alarm = 0;
#ifdef HAVE_ALARM
    alarm(0);
#endif
}

int check_signals(void)
{
    int i, r = 0;
    for (i = 0; i < NUM_SIGNALS; i++)
        if (signal_mask[i]) {
            signal_mask[i] = 0;
            if (signal_handlers[i].fn) signal_handlers[i].fn(signal_handlers[i].cba);
            CHK_BH;
            r = 1;
        }
    return r;
}

void sigchld(union cba_t cba)
{
    /*wait(NULL);*/
    waitpid(-1, NULL, WNOHANG);
}

void set_sigcld()
{
    install_signal_handler(SIGCHLD, sigchld, CBA0, 1);
}

int terminate = 0;

void select_loop(void (*init)(void))
{
      int err;

    memset(signal_mask, 0, sizeof signal_mask);
    memset(signal_handlers, 0, sizeof signal_handlers);
    FD_ZERO(&w_read);
    FD_ZERO(&w_write);
    FD_ZERO(&w_error);
    w_max = 0;
    last_time = get_time();
    signal(SIGPIPE, SIG_IGN);
    init();
    CHK_BH;
    while (!terminate) {
        int n, i;
        struct timeval tv;
        struct timeval *tm = NULL;
        check_signals();
        check_timers();

        check_sdevlist(sdevlist);
        if (!list_empty(timers)) {
            ttime tt = ((struct timer *)&timers)->next->interval + 1;
            if (tt < 0) tt = 0;
            tv.tv_sec = tt / 1000;
            tv.tv_usec = (tt % 1000) * 1000;
            tm = &tv;
        }
#ifdef HAVE_SDL        
        if (sdl) {
            sdl_redraw_screen();
        }
        else
#endif
        {
            term_redraw_screen();
        }
        memcpy(&x_read, &w_read, sizeof(fd_set));
        memcpy(&x_write, &w_write, sizeof(fd_set));
        memcpy(&x_error, &w_error, sizeof(fd_set));
        /*rep_sel:*/
        if (terminate) break;
        if (!w_max && list_empty(timers)) break;
        critical_section = 1;
        if (check_signals()) {
            critical_section = 0;
            continue;
        }
/*          {
                int i;
                printf("\nR:");
                for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_read)) printf("%d,", i);
                printf("\nW:");
                for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_write)) printf("%d,", i);
                printf("\nE:");
                for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_error)) printf("%d,", i);
                fflush(stdout);
            }*/
        CHK_BH;


#if 0
      if (tm)     
            dbg("SELECT: tm=%d.%06d\n", tm->tv_sec, tm->tv_usec);
      else
            dbg("SELECT: no timers-----------------\n");
#endif
#if 0    
    struct timer *tt;
      foreach(tt, timers){
            dbg("    id=%d inter=%d fce=%p\n",tt->id, tt->interval, tt->func);  
      }
      dbg("\n");
#endif      
     /*   ST_STOP;
        sound(0);  */
        n = select(w_max, &x_read, &x_write, &x_error, tm);
       /* sound(800);
        ST_START;      */
        if (n < 0) {
                  err=errno;
            critical_section = 0;
            uninstall_alarm();
            if (err != EINTR) {
                error("ERROR: select failed: %d", err);
                dbg("ERROR: select failed: %d", err);
                if (err == EBADF){
                    int i;
                    fd_set x, used;
                    struct timeval tv;
                        
                    
                              FD_ZERO(&used);
                    dbg("\nR:");
                    for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_read)) {dbg("%d,", i); FD_SET(i, &used);}
                    dbg("\nW:");
                    for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_write)) {dbg("%d,", i); FD_SET(i, &used);}
                    dbg("\nE:");
                    for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_error)) {dbg("%d,", i); FD_SET(i, &used);}
/*                    dbg("\n_:");*/

                    for (i = 0; i < 256; i++) {
                                    if (!FD_ISSET(i, &used)) continue;
                        FD_ZERO(&x);
                        FD_SET(i, &x);
                        tv.tv_sec=0;
                        tv.tv_usec=1;
                        if (select(i+1, &x, NULL, NULL, &tv)<0 && errno==EBADF)
#ifdef LEAK_DEBUG_LIST                                      
                            dbg("fd %d failed(err=%d) at %s:%d\n", i, errno, threads[i].file, threads[i].line);
#else                               
                            dbg("fd %d failed(err=%d)\n ", i, errno);
#endif                                          
                    }
                    
                    raise(SIGSEGV);
                    /*dbg("DIE\n");
                    
                    sleep(1000000);
                    DIE; */
                }
            }
            
            continue;
        }
        critical_section = 0;
        uninstall_alarm();
        check_signals();
/*      printf("sel: %d\n", n);*/
/*      dbg(".\n");*/
        check_timers();
        i = -1;
        while (n > 0 && ++i < w_max) {
            int k = 0;
/*          printf("C %d : %d,%d,%d\n",i,FD_ISSET(i, &w_read),FD_ISSET(i, &w_write),FD_ISSET(i, &w_error));
            printf("A %d : %d,%d,%d\n",i,FD_ISSET(i, &x_read),FD_ISSET(i, &x_write),FD_ISSET(i, &x_error));*/
            if (FD_ISSET(i, &x_read)) {
                if (threads[i].read_func) {
                    threads[i].read_func(threads[i].cba);
                    CHK_BH;
                }
                k = 1;
            }
            if (FD_ISSET(i, &x_write)) {
                if (threads[i].write_func) {
                    threads[i].write_func(threads[i].cba);
                    CHK_BH;
                }
                k = 1;
            }
            if (FD_ISSET(i, &x_error)) {
                if (threads[i].error_func) {
                    threads[i].error_func(threads[i].cba);
                    CHK_BH;
                }
                k = 1;
            }
            n -= k;
        }
    }
    sound(0);

}


Generated by  Doxygen 1.6.0   Back to index