1999-09-14 08:45:02 -04:00
|
|
|
/* Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees.
|
|
|
|
See file COPYING. */
|
|
|
|
|
|
|
|
#include <signal.h> /* for sigaction() (POSIX.1) */
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/times.h>
|
|
|
|
#include <errno.h> /* for errno, (POSIX?/ANSI) */
|
|
|
|
#include "sysdep.h"
|
|
|
|
#include "c-mods.h"
|
|
|
|
#include "scheme48vm.h"
|
|
|
|
#include "event.h"
|
2003-05-01 06:21:33 -04:00
|
|
|
#include "../scsh/scsh_aux.h"
|
|
|
|
#include "../scsh/signals1.h"
|
1999-09-14 08:45:02 -04:00
|
|
|
|
|
|
|
/* turning interrupts and I/O readiness into events */
|
2003-05-01 06:21:33 -04:00
|
|
|
sigset_t full_sigset;
|
1999-09-14 08:45:02 -04:00
|
|
|
|
2003-05-01 06:21:33 -04:00
|
|
|
#define block_interrupts(){sigprocmask (SIG_BLOCK, &full_sigset, 0);}
|
|
|
|
#define allow_interrupts(){sigprocmask (SIG_UNBLOCK, &full_sigset, 0);}
|
1999-09-14 08:45:02 -04:00
|
|
|
|
|
|
|
|
|
|
|
static void when_keyboard_interrupt();
|
|
|
|
static void when_alarm_interrupt();
|
|
|
|
static void when_sigpipe_interrupt();
|
2003-05-01 06:21:33 -04:00
|
|
|
|
|
|
|
/* JMG:*/
|
|
|
|
static void when_scsh_interrupt();
|
|
|
|
/* JMG: for scsh */
|
|
|
|
#define INTERRUPT_QUEUE_LENGTH 32
|
|
|
|
|
|
|
|
static int interrupt_queue [INTERRUPT_QUEUE_LENGTH];
|
|
|
|
static int next_interrupt = 0;
|
|
|
|
static int s48_os_signal_pending(void);
|
|
|
|
static bool s48_os_signal_happend(void);
|
|
|
|
|
|
|
|
|
1999-09-14 08:45:02 -04:00
|
|
|
bool s48_setcatcher(int signum, void (*catcher)(int));
|
|
|
|
void s48_start_alarm_interrupts(void);
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
s48_sysdep_init(void)
|
|
|
|
{
|
|
|
|
if (!s48_setcatcher(SIGINT, when_keyboard_interrupt)
|
|
|
|
|| !s48_setcatcher(SIGALRM, when_alarm_interrupt)
|
|
|
|
|| !s48_setcatcher(SIGPIPE, when_sigpipe_interrupt)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Failed to install signal handlers, errno = %d\n",
|
|
|
|
errno);
|
|
|
|
exit(1);
|
|
|
|
}
|
2003-05-01 06:21:33 -04:00
|
|
|
|
|
|
|
sigfillset (&full_sigset);
|
|
|
|
|
|
|
|
/* JMG: for scsh */
|
|
|
|
s48_setcatcher(SIGCHLD, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGCONT, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGHUP, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGQUIT, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGTERM, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGTSTP, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGUSR1, when_scsh_interrupt);
|
|
|
|
s48_setcatcher(SIGUSR2, when_scsh_interrupt);
|
|
|
|
#ifdef SIGINFO
|
|
|
|
s48_setcatcher(SIGINFO, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGIO
|
|
|
|
s48_setcatcher(SIGIO, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#if defined SIGPOLL && ((defined SIGIO && SIGPOLL != SIGIO) || \
|
|
|
|
!defined SIGIO)
|
|
|
|
s48_setcatcher(SIGPOLL, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGPROF
|
|
|
|
s48_setcatcher(SIGPROF, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGPWR
|
|
|
|
s48_setcatcher(SIGPWR, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGVTALRM
|
|
|
|
s48_setcatcher(SIGVTALRM, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGWINCH
|
|
|
|
s48_setcatcher(SIGWINCH, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGXCPU
|
|
|
|
s48_setcatcher(SIGXCPU, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGXFSZ
|
|
|
|
s48_setcatcher(SIGXFSZ, when_scsh_interrupt);
|
|
|
|
#endif
|
|
|
|
|
1999-09-14 08:45:02 -04:00
|
|
|
s48_start_alarm_interrupts();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unless a signal is being ignored, set up the handler.
|
|
|
|
* If we return FALSE, something went wrong and errno is set to what.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
s48_setcatcher(int signum, void (*catcher)(int))
|
|
|
|
{
|
|
|
|
struct sigaction sa;
|
|
|
|
|
2003-05-01 06:21:33 -04:00
|
|
|
if (sigaction(signum, (struct sigaction *)NULL, &sa) != 0){
|
|
|
|
fprintf(stderr, "Failed to get sigaction for signal %d\n", signum);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* JMG: what's the point of not setting the handler in this case?
|
|
|
|
if (sa.sa_handler == SIG_IGN)
|
|
|
|
return (TRUE);*/
|
1999-09-14 08:45:02 -04:00
|
|
|
sa.sa_handler = catcher;
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
sa.sa_flags = 0;
|
2003-05-01 06:21:33 -04:00
|
|
|
if (sigaction(signum, &sa, (struct sigaction *)NULL) != 0){
|
|
|
|
fprintf(stderr, "Failed to define handler for signal %d\n", signum);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
1999-09-14 08:45:02 -04:00
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long keyboard_interrupt_count = 0;
|
|
|
|
|
|
|
|
static void
|
|
|
|
when_keyboard_interrupt(int ign)
|
|
|
|
{
|
|
|
|
keyboard_interrupt_count += 1;
|
|
|
|
NOTE_EVENT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
We turn off SIGPIPE interrupts by installing a handler that does nothing.
|
|
|
|
Turning them off affects exec()'ed programs, so we don't want to do that.
|
|
|
|
Any actual pipe problems are caught when we try to read or write to them.
|
|
|
|
|
|
|
|
We thank Olin Shivers for this hack.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
when_sigpipe_interrupt(int ign)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ticks since last timer-interrupt request */
|
|
|
|
long s48_current_time = 0;
|
|
|
|
|
|
|
|
static long alarm_time = -1;
|
|
|
|
static long poll_time = -1;
|
|
|
|
static long poll_interval = 5;
|
|
|
|
|
|
|
|
static void
|
|
|
|
when_alarm_interrupt(int ign)
|
|
|
|
{
|
|
|
|
s48_current_time += 1;
|
|
|
|
/* fprintf(stderr, "[tick]"); */
|
|
|
|
if ((alarm_time >= 0 && alarm_time <= s48_current_time) ||
|
|
|
|
(poll_time >= 0 && poll_time <= s48_current_time)) {
|
|
|
|
NOTE_EVENT;
|
|
|
|
};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-05-01 06:21:33 -04:00
|
|
|
|
|
|
|
|
1999-09-14 08:45:02 -04:00
|
|
|
#define USEC_PER_POLL (1000000 / POLLS_PER_SECOND)
|
|
|
|
|
|
|
|
/* delta is in ticks, 0 cancels current alarm */
|
|
|
|
|
|
|
|
long
|
|
|
|
s48_schedule_alarm_interrupt(long delta)
|
|
|
|
{
|
|
|
|
long old;
|
|
|
|
/*
|
|
|
|
fprintf(stderr, "<scheduling alarm for %ld + %ld>\n", s48_current_time,
|
|
|
|
delta/TICKS_PER_POLL);
|
|
|
|
*/
|
|
|
|
/* get remaining time */
|
|
|
|
if (alarm_time == -1)
|
|
|
|
old = -1;
|
|
|
|
else
|
|
|
|
old = (alarm_time - s48_current_time) * TICKS_PER_POLL;
|
|
|
|
|
|
|
|
/* decrement poll_time and reset current_time */
|
|
|
|
if (poll_time != -1)
|
|
|
|
poll_time -= s48_current_time;
|
|
|
|
s48_current_time = 0;
|
|
|
|
|
|
|
|
/* set alarm_time */
|
|
|
|
if (delta == 0) {
|
|
|
|
NOTE_EVENT;
|
|
|
|
alarm_time = 0; }
|
|
|
|
else
|
|
|
|
alarm_time = delta / TICKS_PER_POLL;
|
|
|
|
|
|
|
|
return old;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The next two procedures return times in seconds and ticks */
|
|
|
|
|
|
|
|
long
|
|
|
|
s48_real_time(long *ticks)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
static struct timeval tv_orig;
|
|
|
|
static int initp = 0;
|
|
|
|
if (!initp) {
|
|
|
|
gettimeofday(&tv_orig, NULL);
|
|
|
|
initp = 1;
|
|
|
|
};
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
*ticks = (tv.tv_usec - tv_orig.tv_usec)/(1000000/TICKS_PER_SECOND);
|
|
|
|
return tv.tv_sec - tv_orig.tv_sec;
|
|
|
|
}
|
|
|
|
|
|
|
|
long
|
|
|
|
s48_run_time(long *ticks)
|
|
|
|
{
|
|
|
|
struct tms time_buffer;
|
|
|
|
static long clock_tick = 0;
|
|
|
|
long cpu_time;
|
|
|
|
|
|
|
|
if (clock_tick == 0)
|
|
|
|
clock_tick = sysconf(_SC_CLK_TCK); /* POSIX.1, POSIX.2 */
|
|
|
|
times(&time_buffer); /* On Sun, getrusage() would be better */
|
|
|
|
|
|
|
|
cpu_time = time_buffer.tms_utime + time_buffer.tms_stime;
|
|
|
|
|
|
|
|
*ticks = (cpu_time % clock_tick) * TICKS_PER_SECOND / clock_tick;
|
|
|
|
return cpu_time / clock_tick;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
s48_start_alarm_interrupts(void)
|
|
|
|
{
|
|
|
|
struct itimerval new, old;
|
|
|
|
|
|
|
|
new.it_value.tv_sec = 0;
|
|
|
|
new.it_value.tv_usec = USEC_PER_POLL;
|
|
|
|
new.it_interval.tv_sec = 0;
|
|
|
|
new.it_interval.tv_usec = USEC_PER_POLL;
|
|
|
|
if (0 != setitimer(ITIMER_REAL, &new, &old)) {
|
|
|
|
perror("setitimer");
|
|
|
|
exit(-1); }
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
s48_stop_alarm_interrupts(void)
|
|
|
|
{
|
|
|
|
struct itimerval new, old;
|
|
|
|
|
|
|
|
new.it_value.tv_sec = 0;
|
|
|
|
new.it_value.tv_usec = 0;
|
|
|
|
new.it_interval.tv_sec = 0;
|
|
|
|
new.it_interval.tv_usec = 0;
|
|
|
|
if (0 != setitimer(ITIMER_REAL, &new, &old)) {
|
|
|
|
perror("setitimer");
|
|
|
|
exit(-1); }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ; Scheme version of the get-next-event procedure
|
|
|
|
* ;
|
|
|
|
* ; 1. If there has been a keyboard interrupt, return it.
|
|
|
|
* ; 2. Check for ready ports if enough time has passed since the last check.
|
|
|
|
* ; 3. If there is a ready port, return it.
|
|
|
|
* ; 4. If an alarm is due, return it.
|
|
|
|
* ; 5. If no events are pending, clear the event flags.
|
|
|
|
* (define (get-next-event)
|
|
|
|
* (cond ((> *keyboard-interrupt-count* 0)
|
|
|
|
* (without-interrupts
|
|
|
|
* (lambda ()
|
|
|
|
* (set! *keyboard-interrupt-count*
|
|
|
|
* (- *keyboard-interrupt-count* 1))))
|
|
|
|
* (values (enum event-type keyboard-interrupt) #f #f))
|
|
|
|
* (else
|
|
|
|
* (cond ((>= *current_time* *poll-time*)
|
|
|
|
* (queue-ready-ports)
|
|
|
|
* (set! *poll-time* (+ *time* *poll-interval*))))
|
|
|
|
* (cond ((not (queue-empty? ready-ports))
|
2003-05-01 06:21:33 -04:00
|
|
|
* (values (enum event-type i/o-{read/write}-completion)
|
1999-09-14 08:45:02 -04:00
|
|
|
* (dequeue! ready-ports)))
|
|
|
|
* ((>= *current_time* *alarm-time*)
|
|
|
|
* (set! *alarm-time* max-integer)
|
|
|
|
* (values (enum event-type alarm-interrupt) #f))
|
|
|
|
* (else
|
|
|
|
* (without-interrupts
|
|
|
|
* (lambda ()
|
|
|
|
* (if (and (= *keyboard-interrupt-count* 0)
|
|
|
|
* (> *alarm-time* *current_time*)
|
|
|
|
* (> *poll-time* *current_time*))
|
|
|
|
* (set! *pending-event?* #f))))
|
|
|
|
* (values (enum event-type no-event) #f))))))
|
|
|
|
*/
|
|
|
|
|
2003-05-01 06:21:33 -04:00
|
|
|
#define FD_QUIESCENT 0 /* idle */
|
|
|
|
#define FD_READY 1 /* I/O ready to be performed */
|
|
|
|
#define FD_PENDING 2 /* waiting */
|
|
|
|
|
|
|
|
typedef struct fd_struct {
|
|
|
|
int fd, /* file descriptor */
|
|
|
|
status; /* one of the FD_* constants */
|
|
|
|
bool is_input; /* iff input */
|
|
|
|
struct fd_struct *next; /* next on same queue */
|
|
|
|
} fd_struct;
|
|
|
|
|
|
|
|
static bool there_are_ready_ports(void);
|
|
|
|
static fd_struct *next_ready_fd_struct(void);
|
|
|
|
static int queue_ready_ports(bool wait, long seconds, long ticks);
|
1999-09-14 08:45:02 -04:00
|
|
|
|
|
|
|
int
|
|
|
|
s48_get_next_event(long *ready_fd, long *status)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
extern int s48_os_signal_pending(void);
|
|
|
|
*/
|
|
|
|
|
|
|
|
int io_poll_status;
|
2003-05-01 06:21:33 -04:00
|
|
|
fd_struct *f;
|
|
|
|
|
1999-09-14 08:45:02 -04:00
|
|
|
/*
|
|
|
|
fprintf(stderr, "[poll at %d (waiting for %d)]\n", s48_current_time, alarm_time);
|
|
|
|
*/
|
|
|
|
if (keyboard_interrupt_count > 0) {
|
|
|
|
block_interrupts();
|
|
|
|
--keyboard_interrupt_count;
|
|
|
|
allow_interrupts();
|
|
|
|
/* fprintf(stderr, "[keyboard interrupt]\n"); */
|
|
|
|
return (KEYBOARD_INTERRUPT_EVENT);
|
|
|
|
}
|
|
|
|
if (poll_time != -1 && s48_current_time >= poll_time) {
|
|
|
|
io_poll_status = queue_ready_ports(FALSE, 0, 0);
|
|
|
|
if (io_poll_status == NO_ERRORS)
|
|
|
|
poll_time = s48_current_time + poll_interval;
|
|
|
|
else {
|
|
|
|
*status = io_poll_status;
|
|
|
|
return (ERROR_EVENT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (there_are_ready_ports()) {
|
2003-05-01 06:21:33 -04:00
|
|
|
f = next_ready_fd_struct();
|
|
|
|
*ready_fd = f->fd;
|
1999-09-14 08:45:02 -04:00
|
|
|
*status = 0; /* chars read or written */
|
|
|
|
/* fprintf(stderr, "[i/o completion]\n"); */
|
2003-05-01 06:21:33 -04:00
|
|
|
if (f->is_input)
|
|
|
|
return (IO_READ_COMPLETION_EVENT);
|
|
|
|
else
|
|
|
|
return (IO_WRITE_COMPLETION_EVENT);
|
1999-09-14 08:45:02 -04:00
|
|
|
}
|
|
|
|
if (alarm_time != -1 && s48_current_time >= alarm_time) {
|
|
|
|
alarm_time = -1;
|
|
|
|
/* fprintf(stderr, "[alarm]\n"); */
|
|
|
|
return (ALARM_EVENT);
|
|
|
|
}
|
2003-05-01 06:21:33 -04:00
|
|
|
block_interrupts();
|
|
|
|
/* JMG: scsh should handle this */
|
1999-09-14 08:45:02 -04:00
|
|
|
if (s48_os_signal_pending())
|
|
|
|
return (OS_SIGNAL_EVENT);
|
2003-05-01 06:21:33 -04:00
|
|
|
|
1999-09-14 08:45:02 -04:00
|
|
|
if ((keyboard_interrupt_count == 0)
|
|
|
|
&& (alarm_time == -1 || s48_current_time < alarm_time)
|
|
|
|
&& (poll_time == -1 || s48_current_time < poll_time))
|
|
|
|
s48_Spending_eventsPS = FALSE;
|
|
|
|
allow_interrupts();
|
|
|
|
return (NO_EVENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We keep two queues of ports: those that have a pending operation, and
|
|
|
|
* those whose operation has completed. Periodically, we call select() on
|
|
|
|
* the pending ports and move any that are ready onto the other queue and
|
|
|
|
* signal an event.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A queue of fd_structs is empty iff the first field is NULL. In
|
|
|
|
* that case, lastp points to first.
|
|
|
|
*/
|
|
|
|
typedef struct fdque {
|
|
|
|
fd_struct *first,
|
|
|
|
**lastp;
|
|
|
|
} fdque;
|
|
|
|
|
|
|
|
|
|
|
|
static fd_struct *fds[FD_SETSIZE];
|
|
|
|
static fdque ready = {
|
|
|
|
NULL,
|
|
|
|
&ready.first
|
|
|
|
},
|
|
|
|
pending = {
|
|
|
|
NULL,
|
|
|
|
&pending.first
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void findrm(fd_struct *entry, fdque *que);
|
|
|
|
static fd_struct *rmque(fd_struct **link, fdque *que);
|
|
|
|
static void addque(fd_struct *entry, fdque *que);
|
|
|
|
static fd_struct *add_fd(int fd, bool is_input);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a fd_struct in a queue, and remove it.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
findrm(fd_struct *entry, fdque *que)
|
|
|
|
{
|
|
|
|
fd_struct **fp,
|
|
|
|
*f;
|
|
|
|
|
|
|
|
for (fp = &que->first; (f = *fp) != entry; fp = &f->next)
|
|
|
|
if (f == NULL) {
|
|
|
|
fprintf(stderr, "ERROR: findrm fd %d, status %d not on queue.\n",
|
|
|
|
entry->fd, entry->status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rmque(fp, que);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a pointer to the link of a fd_struct, and a pointer to
|
|
|
|
* the queue it is on, remove the entry from the queue.
|
|
|
|
* The entry removed is returned.
|
|
|
|
*/
|
|
|
|
static fd_struct *
|
|
|
|
rmque(fd_struct **link, fdque *que)
|
|
|
|
{
|
|
|
|
fd_struct *res;
|
|
|
|
|
|
|
|
res = *link;
|
|
|
|
*link = res->next;
|
|
|
|
if (res->next == NULL)
|
|
|
|
que->lastp = link;
|
|
|
|
return (res);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a fd_struct to a queue.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
addque(fd_struct *entry, fdque *que)
|
|
|
|
{
|
|
|
|
*que->lastp = entry;
|
|
|
|
entry->next = NULL;
|
|
|
|
que->lastp = &entry->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
there_are_ready_ports(void)
|
|
|
|
{
|
|
|
|
return (ready.first != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-01 06:21:33 -04:00
|
|
|
static fd_struct *
|
|
|
|
next_ready_fd_struct(void)
|
1999-09-14 08:45:02 -04:00
|
|
|
{
|
|
|
|
fd_struct *p;
|
|
|
|
|
|
|
|
p = rmque(&ready.first, &ready);
|
|
|
|
p->status = FD_QUIESCENT;
|
2003-05-01 06:21:33 -04:00
|
|
|
return (p);
|
1999-09-14 08:45:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put fd on to the queue of ports with pending operations.
|
|
|
|
* Return TRUE if successful, and FALSE otherwise.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
s48_add_pending_fd(int fd, bool is_input)
|
|
|
|
{
|
|
|
|
fd_struct *data;
|
|
|
|
|
|
|
|
if (! (0 <= fd && fd < FD_SETSIZE)) {
|
|
|
|
fprintf(stderr, "ERROR: add_pending fd %d not in [0, %d)\n",
|
|
|
|
fd,
|
|
|
|
FD_SETSIZE);
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
data = fds[fd];
|
|
|
|
if (data == NULL) {
|
|
|
|
data = add_fd(fd, is_input);
|
|
|
|
if (data == NULL)
|
|
|
|
return (FALSE); /* no more memory */
|
|
|
|
} else if (data->status == FD_PENDING)
|
|
|
|
return (TRUE); /* fd is already pending */
|
|
|
|
else if (data->status == FD_READY)
|
|
|
|
findrm(data, &ready);
|
|
|
|
data->status = FD_PENDING;
|
|
|
|
addque(data, &pending);
|
|
|
|
if (poll_time == -1)
|
|
|
|
poll_time = s48_current_time + poll_interval;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new fd_struct for fd.
|
|
|
|
*/
|
|
|
|
static fd_struct *
|
|
|
|
add_fd(int fd, bool is_input)
|
|
|
|
{
|
|
|
|
struct fd_struct *new;
|
|
|
|
|
|
|
|
new = (struct fd_struct *)malloc(sizeof(*new));
|
|
|
|
if (new != NULL) {
|
|
|
|
new->fd = fd;
|
|
|
|
new->status = FD_QUIESCENT;
|
|
|
|
new->is_input = is_input;
|
|
|
|
new->next = NULL;
|
|
|
|
fds[fd] = new;
|
|
|
|
}
|
|
|
|
return (new);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove fd from any queues it is on. Returns true if the FD was on a queue
|
|
|
|
* and false if it wasn't.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
s48_remove_fd(int fd)
|
|
|
|
{
|
|
|
|
struct fd_struct *data;
|
|
|
|
|
|
|
|
if (! (0 <= fd && fd < FD_SETSIZE)) {
|
|
|
|
fprintf(stderr, "ERROR: s48_remove_fd fd %d not in [0, %d)\n",
|
|
|
|
fd,
|
|
|
|
FD_SETSIZE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
data = fds[fd];
|
|
|
|
if (data == NULL)
|
|
|
|
return FALSE;
|
|
|
|
if (data->status == FD_PENDING) {
|
|
|
|
findrm(data, &pending);
|
|
|
|
if (pending.first == NULL)
|
|
|
|
poll_time = -1;
|
|
|
|
} else if (data->status == FD_READY)
|
|
|
|
findrm(data, &ready);
|
|
|
|
free((void *)data);
|
|
|
|
fds[fd] = NULL;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
s48_wait_for_event(long max_wait, bool is_minutes)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
long seconds,
|
|
|
|
ticks;
|
|
|
|
|
|
|
|
/* fprintf(stderr, "[waiting]\n"); */
|
|
|
|
|
|
|
|
s48_stop_alarm_interrupts();
|
|
|
|
ticks = 0;
|
|
|
|
if (max_wait == -1)
|
|
|
|
seconds = -1;
|
|
|
|
else if (is_minutes)
|
|
|
|
seconds = max_wait * 60;
|
|
|
|
else {
|
|
|
|
seconds = max_wait / TICKS_PER_SECOND;
|
|
|
|
ticks = max_wait % TICKS_PER_SECOND;
|
|
|
|
}
|
|
|
|
if (keyboard_interrupt_count > 0)
|
|
|
|
status = NO_ERRORS;
|
2003-05-01 06:21:33 -04:00
|
|
|
else if (s48_os_signal_happend ())
|
|
|
|
status = NO_ERRORS;
|
1999-09-14 08:45:02 -04:00
|
|
|
else {
|
|
|
|
status = queue_ready_ports(TRUE, seconds, ticks);
|
|
|
|
if (there_are_ready_ports())
|
|
|
|
NOTE_EVENT;
|
|
|
|
}
|
|
|
|
s48_start_alarm_interrupts();
|
|
|
|
return (status);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call select() on the pending ports and move any ready ones to the ready
|
|
|
|
* queue. If wait is true, seconds is either -1 (wait forever) or the
|
|
|
|
* maximum number of seconds to wait (with ticks any additional ticks).
|
|
|
|
* The returned value is a status code.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
queue_ready_ports(bool wait, long seconds, long ticks)
|
|
|
|
{
|
|
|
|
fd_set reads,
|
|
|
|
writes,
|
|
|
|
alls;
|
|
|
|
int limfd;
|
|
|
|
fd_struct *fdp,
|
|
|
|
**fdpp;
|
|
|
|
int left;
|
|
|
|
struct timeval tv,
|
|
|
|
*tvp;
|
|
|
|
|
|
|
|
if ((! wait)
|
|
|
|
&& (pending.first == NULL))
|
|
|
|
return (NO_ERRORS);
|
|
|
|
FD_ZERO(&reads);
|
|
|
|
FD_ZERO(&writes);
|
|
|
|
FD_ZERO(&alls);
|
|
|
|
limfd = 0;
|
|
|
|
for (fdp = pending.first; fdp != NULL; fdp = fdp->next) {
|
|
|
|
FD_SET(fdp->fd, fdp->is_input ? &reads : &writes);
|
|
|
|
FD_SET(fdp->fd, &alls);
|
|
|
|
if (limfd <= fdp->fd)
|
|
|
|
limfd = fdp->fd + 1;
|
|
|
|
}
|
|
|
|
tvp = &tv;
|
|
|
|
if (wait)
|
2003-05-01 06:21:33 -04:00
|
|
|
if (seconds == -1){
|
1999-09-14 08:45:02 -04:00
|
|
|
tvp = NULL;
|
2003-05-01 06:21:33 -04:00
|
|
|
}
|
1999-09-14 08:45:02 -04:00
|
|
|
else {
|
|
|
|
tv.tv_sec = seconds;
|
|
|
|
tv.tv_usec = ticks * (1000000 / TICKS_PER_SECOND);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
timerclear(&tv);
|
|
|
|
while(TRUE) {
|
2003-05-01 06:21:33 -04:00
|
|
|
if ((keyboard_interrupt_count > 0) || s48_os_signal_happend ())
|
|
|
|
return NO_ERRORS;
|
|
|
|
/* time gap */
|
1999-09-14 08:45:02 -04:00
|
|
|
left = select(limfd, &reads, &writes, &alls, tvp);
|
|
|
|
if (left > 0) {
|
|
|
|
fdpp = &pending.first;
|
|
|
|
while (left > 0 && (fdp = *fdpp) != NULL)
|
|
|
|
if ((FD_ISSET(fdp->fd, &alls))
|
|
|
|
|| (FD_ISSET(fdp->fd, fdp->is_input ? &reads : &writes))) {
|
|
|
|
--left;
|
|
|
|
rmque(fdpp, &pending);
|
|
|
|
fdp->status = FD_READY;
|
|
|
|
addque(fdp, &ready);
|
|
|
|
} else
|
|
|
|
fdpp = &fdp->next;
|
|
|
|
if (pending.first == NULL)
|
|
|
|
poll_time = -1;
|
|
|
|
return NO_ERRORS;
|
|
|
|
}
|
|
|
|
else if (left == 0)
|
|
|
|
return NO_ERRORS;
|
|
|
|
else if (errno == EINTR) {
|
|
|
|
tvp = &tv; /* turn off blocking and try again */
|
|
|
|
timerclear(tvp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return errno;
|
|
|
|
}
|
|
|
|
}
|
2003-05-01 06:21:33 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Adds `signum' to the queue of received signals.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
queue_interrupt(int signum)
|
|
|
|
{
|
|
|
|
if (next_interrupt == INTERRUPT_QUEUE_LENGTH){
|
|
|
|
perror("Interrupt queue overflow -- report to Scheme 48 maintainers.");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
interrupt_queue[next_interrupt] = signum;
|
|
|
|
next_interrupt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* JMG: for scsh */
|
|
|
|
static void when_scsh_interrupt(int signo)
|
|
|
|
{
|
|
|
|
queue_interrupt(sig2int[signo]);
|
|
|
|
NOTE_EVENT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This procedure is called periodically by the VM .
|
|
|
|
*
|
|
|
|
* s48_set_os_signal() is a VM procedure. The two arguments are the type
|
|
|
|
* of interrupt and one other value which can be used to return whatever
|
|
|
|
* associated information is desired. The two values, along with the
|
|
|
|
* current enabled-interrupts mask, are passed to the handler for os-signal
|
|
|
|
* interrupts.
|
|
|
|
*
|
|
|
|
* A handler can be installed by doing
|
|
|
|
(set-interrupt-handler! (enum interrupt os-signal)
|
|
|
|
(lambda (type arg enabled-interrupts)
|
|
|
|
(display type)
|
|
|
|
(newline)
|
|
|
|
(display arg)
|
|
|
|
(newline)
|
|
|
|
(display enabled-interrupts)
|
|
|
|
(newline)))
|
|
|
|
* The handler is called with all interrupts disabled. They are
|
|
|
|
* reenabled when the handler returns (or if done by hand).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns TRUE if there is a signal to be delivered up to Scheme.
|
|
|
|
* Needs no be called with interrupts blocked.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
s48_os_signal_pending(void) {
|
|
|
|
int i;
|
|
|
|
s48_value interrupt_list = S48_NULL;
|
|
|
|
block_interrupts();
|
|
|
|
|
|
|
|
if (next_interrupt == 0) {
|
|
|
|
allow_interrupts();
|
|
|
|
return FALSE; }
|
|
|
|
else {
|
|
|
|
/* turn the queue into a scheme list and preserve the order */
|
|
|
|
for (i = next_interrupt; i > 0 ; i--)
|
|
|
|
interrupt_list = s48_cons (s48_enter_fixnum (interrupt_queue [i - 1]),
|
|
|
|
interrupt_list);
|
|
|
|
s48_set_os_signals(interrupt_list);
|
|
|
|
|
|
|
|
next_interrupt = 0;
|
|
|
|
allow_interrupts();
|
|
|
|
return TRUE; }
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
s48_os_signal_happend(void) {
|
|
|
|
return (next_interrupt != 0);
|
|
|
|
}
|
|
|
|
|