Windows: Implement setitimer() and sigaction().
The timer is implemented using a thread that calls the signal handler at regular intervals. We also replace Windows's signal() function because we must intercept that SIGALRM is set (which is used when a timer is canceled). Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
This commit is contained in:
parent
82f8d969f5
commit
6072fc314e
112
compat/mingw.c
112
compat/mingw.c
@ -125,12 +125,120 @@ struct passwd *getpwuid(int uid)
|
|||||||
return &p;
|
return &p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HANDLE timer_event;
|
||||||
|
static HANDLE timer_thread;
|
||||||
|
static int timer_interval;
|
||||||
|
static int one_shot;
|
||||||
|
static sig_handler_t timer_fn = SIG_DFL;
|
||||||
|
|
||||||
|
/* The timer works like this:
|
||||||
|
* The thread, ticktack(), is a trivial routine that most of the time
|
||||||
|
* only waits to receive the signal to terminate. The main thread tells
|
||||||
|
* the thread to terminate by setting the timer_event to the signalled
|
||||||
|
* state.
|
||||||
|
* But ticktack() interrupts the wait state after the timer's interval
|
||||||
|
* length to call the signal handler.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static __stdcall unsigned ticktack(void *dummy)
|
||||||
|
{
|
||||||
|
while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
|
||||||
|
if (timer_fn == SIG_DFL)
|
||||||
|
die("Alarm");
|
||||||
|
if (timer_fn != SIG_IGN)
|
||||||
|
timer_fn(SIGALRM);
|
||||||
|
if (one_shot)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int start_timer_thread(void)
|
||||||
|
{
|
||||||
|
timer_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (timer_event) {
|
||||||
|
timer_thread = (HANDLE) _beginthreadex(NULL, 0, ticktack, NULL, 0, NULL);
|
||||||
|
if (!timer_thread )
|
||||||
|
return errno = ENOMEM,
|
||||||
|
error("cannot start timer thread");
|
||||||
|
} else
|
||||||
|
return errno = ENOMEM,
|
||||||
|
error("cannot allocate resources for timer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop_timer_thread(void)
|
||||||
|
{
|
||||||
|
if (timer_event)
|
||||||
|
SetEvent(timer_event); /* tell thread to terminate */
|
||||||
|
if (timer_thread) {
|
||||||
|
int rc = WaitForSingleObject(timer_thread, 1000);
|
||||||
|
if (rc == WAIT_TIMEOUT)
|
||||||
|
error("timer thread did not terminate timely");
|
||||||
|
else if (rc != WAIT_OBJECT_0)
|
||||||
|
error("waiting for timer thread failed: %lu",
|
||||||
|
GetLastError());
|
||||||
|
CloseHandle(timer_thread);
|
||||||
|
}
|
||||||
|
if (timer_event)
|
||||||
|
CloseHandle(timer_event);
|
||||||
|
timer_event = NULL;
|
||||||
|
timer_thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_timeval_eq(const struct timeval *i1, const struct timeval *i2)
|
||||||
|
{
|
||||||
|
return i1->tv_sec == i2->tv_sec && i1->tv_usec == i2->tv_usec;
|
||||||
|
}
|
||||||
|
|
||||||
int setitimer(int type, struct itimerval *in, struct itimerval *out)
|
int setitimer(int type, struct itimerval *in, struct itimerval *out)
|
||||||
{
|
{
|
||||||
return -1;
|
static const struct timeval zero;
|
||||||
|
static int atexit_done;
|
||||||
|
|
||||||
|
if (out != NULL)
|
||||||
|
return errno = EINVAL,
|
||||||
|
error("setitimer param 3 != NULL not implemented");
|
||||||
|
if (!is_timeval_eq(&in->it_interval, &zero) &&
|
||||||
|
!is_timeval_eq(&in->it_interval, &in->it_value))
|
||||||
|
return errno = EINVAL,
|
||||||
|
error("setitimer: it_interval must be zero or eq it_value");
|
||||||
|
|
||||||
|
if (timer_thread)
|
||||||
|
stop_timer_thread();
|
||||||
|
|
||||||
|
if (is_timeval_eq(&in->it_value, &zero) &&
|
||||||
|
is_timeval_eq(&in->it_interval, &zero))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
timer_interval = in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000;
|
||||||
|
one_shot = is_timeval_eq(&in->it_interval, &zero);
|
||||||
|
if (!atexit_done) {
|
||||||
|
atexit(stop_timer_thread);
|
||||||
|
atexit_done = 1;
|
||||||
|
}
|
||||||
|
return start_timer_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
int sigaction(int sig, struct sigaction *in, struct sigaction *out)
|
int sigaction(int sig, struct sigaction *in, struct sigaction *out)
|
||||||
{
|
{
|
||||||
return -1;
|
if (sig != SIGALRM)
|
||||||
|
return errno = EINVAL,
|
||||||
|
error("sigaction only implemented for SIGALRM");
|
||||||
|
if (out != NULL)
|
||||||
|
return errno = EINVAL,
|
||||||
|
error("sigaction: param 3 != NULL not implemented");
|
||||||
|
|
||||||
|
timer_fn = in->sa_handler;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef signal
|
||||||
|
sig_handler_t mingw_signal(int sig, sig_handler_t handler)
|
||||||
|
{
|
||||||
|
if (sig != SIGALRM)
|
||||||
|
return signal(sig, handler);
|
||||||
|
sig_handler_t old = timer_fn;
|
||||||
|
timer_fn = handler;
|
||||||
|
return old;
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,9 @@ char *mingw_getcwd(char *pointer, int len);
|
|||||||
int mingw_rename(const char*, const char*);
|
int mingw_rename(const char*, const char*);
|
||||||
#define rename mingw_rename
|
#define rename mingw_rename
|
||||||
|
|
||||||
|
sig_handler_t mingw_signal(int sig, sig_handler_t handler);
|
||||||
|
#define signal mingw_signal
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* git specific compatibility
|
* git specific compatibility
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user