/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Asynchronous event processing with SIGALRM. * * By Michael Bukin. * * Distorted by George Foot. * * Mangled by Peter Wang. * * See readme.txt for copyright information. */ #include "allegro.h" #ifdef ALLEGRO_QNX #include "allegro/platform/aintqnx.h" #else #include "allegro/platform/aintunix.h" #endif #ifndef HAVE_LIBPTHREAD #include #include #include #include static int _sigalrm_already_installed = FALSE; static volatile int _sigalrm_interrupt_pending = FALSE; static volatile int _sigalrm_cli_count = 0; static volatile int _sigalrm_abort_requested = FALSE; static volatile int _sigalrm_aborting = FALSE; static void (*_sigalrm_interrupt_handler)(unsigned long interval) = 0; void (*_sigalrm_timer_interrupt_handler)(unsigned long interval) = 0; static RETSIGTYPE (*_sigalrm_old_signal_handler)(int num) = 0; static RETSIGTYPE _sigalrm_signal_handler(int num); static int first_time = TRUE; /* _sigalrm_disable_interrupts: * Disables "interrupts". */ static void _sigalrm_disable_interrupts(void) { _sigalrm_cli_count++; } /* _sigalrm_enable_interrupts: * Enables "interrupts.". */ void _sigalrm_enable_interrupts(void) { if (--_sigalrm_cli_count == 0) { /* Process pending interrupts. */ first_time = TRUE; if (_sigalrm_interrupt_pending) { _sigalrm_interrupt_pending = FALSE; _sigalrm_signal_handler(SIGALRM); } } else if (_sigalrm_cli_count < 0) { abort(); } } /* _sigalrm_interrupts_disabled: * Tests if interrupts are disabled. */ static int _sigalrm_interrupts_disabled(void) { return _sigalrm_cli_count; } static struct timeval old_time; static struct timeval new_time; /* _sigalrm_time_passed: * Calculates time passed since last call to this function. */ static unsigned long _sigalrm_time_passed(void) { unsigned long interval; gettimeofday(&new_time, 0); if (!first_time) { interval = ((new_time.tv_sec - old_time.tv_sec) * 1000000L + (new_time.tv_usec - old_time.tv_usec)); } else { first_time = FALSE; interval = 0; } old_time = new_time; return interval; } /* _sigalrm_do_abort: * Aborts program (interrupts will be enabled on entry). */ static void _sigalrm_do_abort(void) { /* Someday, it may choose to abort or exit, depending on user choice. */ if (!_sigalrm_aborting) { _sigalrm_aborting = TRUE; exit(EXIT_SUCCESS); } } /* _sigalrm_request_abort: * Requests interrupt-safe abort (abort with enabled interrupts). */ void _sigalrm_request_abort(void) { if (_sigalrm_interrupts_disabled()) _sigalrm_abort_requested = TRUE; else _sigalrm_do_abort(); } /* _sigalrm_start_timer: * Starts timer in one-shot mode. */ static void _sigalrm_start_timer(void) { struct itimerval timer; _sigalrm_old_signal_handler = signal(SIGALRM, _sigalrm_signal_handler); timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 10000; /* 10 ms */ setitimer(ITIMER_REAL, &timer, 0); } /* _sigalrm_stop_timer: * Stops timer. */ static void _sigalrm_stop_timer(void) { struct itimerval timer; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &timer, 0); signal(SIGALRM, _sigalrm_old_signal_handler); } /* _sigalrm_signal_handler: * Handler for SIGALRM signal. */ static RETSIGTYPE _sigalrm_signal_handler(int num) { unsigned long interval, i; int err; if (_sigalrm_interrupts_disabled()) { _sigalrm_interrupt_pending = TRUE; return; } err = errno; interval = _sigalrm_time_passed(); while (interval) { i = MIN(interval, INT_MAX/(TIMERS_PER_SECOND/100)); interval -= i; i = i * (TIMERS_PER_SECOND/100) / 10000L; if (_sigalrm_interrupt_handler) (*_sigalrm_interrupt_handler)(i); if (_sigalrm_timer_interrupt_handler) (*_sigalrm_timer_interrupt_handler)(i); } /* Abort with enabled interrupts. */ if (_sigalrm_abort_requested) _sigalrm_do_abort(); _sigalrm_start_timer(); errno = err; } /* _sigalrm_init: * Starts interrupts processing. */ static int _sigalrm_init(void (*handler)(unsigned long interval)) { if (_sigalrm_already_installed) return -1; _sigalrm_already_installed = TRUE; _sigalrm_interrupt_handler = handler; _sigalrm_interrupt_pending = FALSE; _sigalrm_cli_count = 0; first_time = TRUE; _sigalrm_start_timer(); return 0; } /* _sigalrm_exit: * Stops interrupts processing. */ static void _sigalrm_exit(void) { if (!_sigalrm_already_installed) return; _sigalrm_stop_timer(); _sigalrm_interrupt_handler = 0; _sigalrm_interrupt_pending = FALSE; _sigalrm_cli_count = 0; _sigalrm_already_installed = FALSE; } /*============================================================ * SIGALRM bg_manager *-----------------------------------------------------------*/ #define MAX_FUNCS 16 static bg_func funcs[MAX_FUNCS]; static int max_func; /* highest+1 used entry */ static void bg_man_sigalrm_handler(unsigned long interval) { int i; for (i = 0; i < max_func; i++) if (funcs[i]) funcs[i](0); } static int bg_man_sigalrm_init(void) { return _sigalrm_init(bg_man_sigalrm_handler); } static void bg_man_sigalrm_exit(void) { _sigalrm_exit(); } static int bg_man_sigalrm_register_func(bg_func f) { int i; for (i = 0; funcs[i] && i < MAX_FUNCS; i++); if (i == MAX_FUNCS) return -1; funcs[i] = f; if (i == max_func) max_func++; return 0; } static int bg_man_sigalrm_unregister_func(bg_func f) { int i; for (i = 0; funcs[i] != f && i < max_func; i++); if (i == max_func) return -1; funcs[i] = NULL; if (i+1 == max_func) do { max_func--; } while ((max_func > 0) && !funcs[max_func-1]); return 0; } static void bg_man_sigalrm_enable_interrupts(void) { _sigalrm_enable_interrupts(); } static void bg_man_sigalrm_disable_interrupts(void) { _sigalrm_disable_interrupts(); } static int bg_man_sigalrm_interrupts_disabled(void) { return _sigalrm_interrupts_disabled(); } struct bg_manager _bg_man_sigalrm = { 0, bg_man_sigalrm_init, bg_man_sigalrm_exit, bg_man_sigalrm_register_func, bg_man_sigalrm_unregister_func, bg_man_sigalrm_enable_interrupts, bg_man_sigalrm_disable_interrupts, bg_man_sigalrm_interrupts_disabled }; #endif