From 4d84d14ac1aa13958eaa2971b03f7f929a519105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 8 Feb 2008 13:00:32 +0000 Subject: reorganized svn path=/trunk/; revision=9400 --- desiredata/src/s_inter.c | 732 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 732 insertions(+) create mode 100644 desiredata/src/s_inter.c (limited to 'desiredata/src/s_inter.c') diff --git a/desiredata/src/s_inter.c b/desiredata/src/s_inter.c new file mode 100644 index 00000000..ad19ceb1 --- /dev/null +++ b/desiredata/src/s_inter.c @@ -0,0 +1,732 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* Pd side of the Pd/Pd-gui interface. Also, some system interface routines +that didn't really belong anywhere. */ + +#define WATCHDOGTHREAD + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "pthread.h" +#include +#ifdef UNISTD +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#ifdef HAVE_BSTRING_H +#include +#endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +#include +#include +#include +#include +#ifdef _MSC_VER +typedef int pid_t; +#endif +typedef int socklen_t; +#define EADDRINUSE WSAEADDRINUSE +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef SIGIOT +#define SIGIOT SIGABRT +#endif + +#ifdef __APPLE__ +#include +#include +#include +#else +#include +#endif + +#define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */ +#define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */ + +/* T.Grill - make it a _little_ more adaptable... */ +#ifndef PDBINDIR +#define PDBINDIR "bin/" +#endif + +#ifndef WISHAPP +#define WISHAPP "wish84.exe" +#endif + +#ifdef __linux__ +#define LOCALHOST "127.0.0.1" +#else +#define LOCALHOST "localhost" +#endif + +struct t_fdpoll { + int fdp_fd; + t_fdpollfn fdp_fn; + void *fdp_ptr; +}; + +#define INBUFSIZE 16384 + +extern int sys_guisetportnumber; +static int sys_nfdpoll; +static t_fdpoll *sys_fdpoll; +static int sys_maxfd; +t_text *sys_netreceive; +static t_binbuf *inbinbuf; +t_socketreceiver *sys_socketreceiver; +extern int sys_addhist(int phase); + +/* ----------- functions for timing, signals, priorities, etc --------- */ + +#ifdef _WIN32 +static LARGE_INTEGER nt_inittime; +static double nt_freq = 0; + +static void sys_initntclock() { + LARGE_INTEGER f1; + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + if (!QueryPerformanceFrequency(&f1)) { + fprintf(stderr, "pd: QueryPerformanceFrequency failed\n"); + f1.QuadPart = 1; + } + nt_freq = f1.QuadPart; + nt_inittime = now; +} + +#if 0 +/* this is a version you can call if you did the QueryPerformanceCounter + call yourself. Necessary for time tagging incoming MIDI at interrupt + level, for instance; but we're not doing that just now. */ + +double nt_tixtotime(LARGE_INTEGER *dumbass) { + if (nt_freq == 0) sys_initntclock(); + return (((double)(dumbass->QuadPart - nt_inittime.QuadPart)) / nt_freq); +} +#endif +#endif /* _WIN32 */ + + /* get "real time" in seconds; take the + first time we get called as a reference time of zero. */ +double sys_getrealtime() { +#ifndef _WIN32 + static struct timeval then; + struct timeval now; + gettimeofday(&now, 0); + if (then.tv_sec == 0 && then.tv_usec == 0) then = now; + return (now.tv_sec - then.tv_sec) + (1./1000000.) * (now.tv_usec - then.tv_usec); +#else + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + if (nt_freq == 0) sys_initntclock(); + return double(now.QuadPart - nt_inittime.QuadPart) / nt_freq; +#endif +} + +int sys_pollsockets () { + struct timeval timout; + int didsomething = 0; + fd_set readset, writeset, exceptset; + timout.tv_sec = 0; + timout.tv_usec = 0; + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + t_fdpoll *fp = sys_fdpoll; + for (int i=sys_nfdpoll; i--; fp++) FD_SET(fp->fdp_fd, &readset); + select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout); + for (int i=0; i= 200112L || (_POSIX_MEMLOCK - 0) >= 200112L +#include +#endif +#if (_POSIX_MEMLOCK - 0) >= 200112L +#include +#endif +void sys_set_priority(int higher) { +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L + struct sched_param par; +#ifdef USEAPI_JACK + int p1 = sched_get_priority_min(SCHED_FIFO); + int p3 = (higher ? p1 + 7 : p1 + 5); +#else + int p2 = sched_get_priority_max(SCHED_FIFO); + int p3 = (higher ? p2 - 1 : p2 - 3); +#endif + par.sched_priority = p3; + if (sched_setscheduler(0,SCHED_FIFO,&par) != -1) + fprintf(stderr, "priority %d scheduling enabled.\n", p3); +#endif +#if (_POSIX_MEMLOCK - 0) >= 200112L + /* tb: force memlock to physical memory { */ + struct rlimit mlock_limit; + mlock_limit.rlim_cur=0; + /* tb: only if we are really root we can set the hard limit */ + mlock_limit.rlim_max = getuid() ? 100 : 0; + setrlimit(RLIMIT_MEMLOCK,&mlock_limit); + /* } tb */ + if (mlockall(MCL_FUTURE) != -1) fprintf(stderr, "memory locking enabled.\n"); +#endif +} +#endif /* __linux__ */ + +#ifdef IRIX /* hack by at 2003/09/21 */ + +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L || (_POSIX_MEMLOCK - 0) >= 200112L +#include +#endif + +void sys_set_priority(int higher) { +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L + struct sched_param par; + /* Bearing the table found in 'man realtime' in mind, I found it a */ + /* good idea to use 192 as the priority setting for Pd. Any thoughts? */ + if (higher) par.sched_priority = 250; /* priority for watchdog */ + else par.sched_priority = 192; /* priority for pd (DSP) */ + if (sched_setscheduler(0, SCHED_FIFO, &par) != -1) + fprintf(stderr, "priority %d scheduling enabled.\n", par.sched_priority); +#endif + +#if (_POSIX_MEMLOCK - 0) >= 200112L + if (mlockall(MCL_FUTURE) != -1) fprintf(stderr, "memory locking enabled.\n"); +#endif +} +/* end of hack */ +#endif /* IRIX */ + +/* ------------------ receiving incoming messages over sockets ------------- */ + +void sys_sockerror(char *s) { +#ifdef _WIN32 + int err = WSAGetLastError(); + if (err == 10054) return; + else if (err == 10044) { + fprintf(stderr, "Warning: you might not have TCP/IP \"networking\" turned on\n"); + fprintf(stderr, "which is needed for Pd to talk to its GUI layer.\n"); + } +#else + int err = errno; +#endif /* _WIN32 */ + fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); +} + +void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr) { + int nfd = sys_nfdpoll; + int size = nfd * sizeof(t_fdpoll); + sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size, size + sizeof(t_fdpoll)); + t_fdpoll *fp = sys_fdpoll + nfd; + fp->fdp_fd = fd; + fp->fdp_fn = fn; + fp->fdp_ptr = ptr; + sys_nfdpoll = nfd + 1; + if (fd >= sys_maxfd) sys_maxfd = fd + 1; +} + +void sys_rmpollfn(int fd) { + int nfd = sys_nfdpoll; + int size = nfd * sizeof(t_fdpoll); + t_fdpoll *fp = sys_fdpoll; + for (int i=nfd; i--; fp++) { + if (fp->fdp_fd == fd) { + while (i--) { + fp[0] = fp[1]; + fp++; + } + sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size, size - sizeof(t_fdpoll)); + sys_nfdpoll = nfd - 1; + return; + } + } + post("warning: %d removed from poll list but not found", fd); +} + +t_socketreceiver *socketreceiver_new(t_pd *owner, int fd, t_socketnotifier notifier, +t_socketreceivefn socketreceivefn, int udp) { + t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x)); + x->inhead = x->intail = 0; + x->owner = owner; + x->notifier = notifier; + x->socketreceivefn = socketreceivefn; + x->udp = udp; + x->fd = fd; + x->obuf = 0; + x->next = 0; + x->inbuf = (char *)malloc(INBUFSIZE); + if (!x->inbuf) bug("t_socketreceiver"); + return x; +} + +void socketreceiver_free(t_socketreceiver *x) {free(x->inbuf); free(x);} + +/* this is in a separately called subroutine so that the buffer isn't + sitting on the stack while the messages are getting passed. */ +static int socketreceiver_doread(t_socketreceiver *x) { + char messbuf[INBUFSIZE], *bp = messbuf; + int inhead = x->inhead; + int intail = x->intail; + char *inbuf = x->inbuf; + if (intail == inhead) return 0; + for (int i=intail; i!=inhead; i=(i+1)&(INBUFSIZE-1)) { + /* ";" not preceded by "\" is a message boundary in current syntax. + in future syntax it might become more complex. */ + char c = *bp++ = inbuf[i]; + if (c == ';' && (!i || inbuf[i-1] != '\\')) { + intail = (i+1)&(INBUFSIZE-1); + binbuf_text(inbinbuf, messbuf, bp - messbuf); + x->inhead = inhead; + x->intail = intail; + return 1; + } + } + return 0; +} + +static void socketreceiver_getudp(t_socketreceiver *x, int fd) { + char buf[INBUFSIZE+1]; + int ret = recv(fd, buf, INBUFSIZE, 0); + if (ret < 0) { + sys_sockerror("recv"); + sys_rmpollfn(fd); + sys_closesocket(fd); + } else if (ret > 0) { + buf[ret] = 0; + if (buf[ret-1] == '\n') { + char *semi = strchr(buf, ';'); + if (semi) *semi = 0; + binbuf_text(inbinbuf, buf, strlen(buf)); + outlet_setstacklim(); + x->socketreceivefn(x->owner, inbinbuf); + } else {/*bad buffer ignored */} + } +} + +void sys_exit(); + +void socketreceiver_read(t_socketreceiver *x, int fd) { + if (x->udp) {socketreceiver_getudp(x, fd); return;} + /* else TCP */ + int readto = x->inhead >= x->intail ? INBUFSIZE : x->intail-1; + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->inhead) { + fprintf(stderr, "pd: dropped message from gui\n"); + x->inhead = x->intail = 0; + readto = INBUFSIZE; + } else { + int ret = recv(fd, x->inbuf+x->inhead, readto-x->inhead, 0); + if (ret<=0) { + if (ret<0) sys_sockerror("recv"); else post("EOF on socket %d", fd); + if (x->notifier) x->notifier(x->owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + return; + } + x->inhead += ret; + if (x->inhead >= INBUFSIZE) x->inhead = 0; + while (socketreceiver_doread(x)) { + outlet_setstacklim(); + x->socketreceivefn(x->owner, inbinbuf); + } + } +} + +void sys_closesocket(int fd) { +#ifdef UNISTD + close(fd); +#endif +#ifdef _WIN32 + closesocket(fd); +#endif /* _WIN32 */ +} + +/* ---------------------- sending messages to the GUI ------------------ */ +#define GUI_ALLOCCHUNK 8192 +#define GUI_UPDATESLICE 512 /* how much we try to do in one idle period */ +#define GUI_BYTESPERPING 1024 /* how much we send up per ping */ + +static void sys_trytogetmoreguibuf(t_socketreceiver *self, int newsize) { + self->osize = newsize; + self->obuf = (char *)realloc(self->obuf, newsize); +} + +#undef max /* for msys compat */ +int max(int a, int b) { return ((a)>(b)?(a):(b)); } + +std::ostringstream lost_posts; + +void sys_vgui(char *fmt, ...) { + t_socketreceiver *self = sys_socketreceiver; + va_list ap; + va_start(ap, fmt); + if (!self) {voprintf(lost_posts,fmt,ap); va_end(ap); return;} + if (!self->obuf) { + self->obuf = (char *)malloc(GUI_ALLOCCHUNK); + self->osize = GUI_ALLOCCHUNK; + self->ohead = self->otail = 0; + } + if (self->ohead > self->osize - GUI_ALLOCCHUNK/2) + sys_trytogetmoreguibuf(self,self->osize + GUI_ALLOCCHUNK); + int msglen = vsnprintf(self->obuf+self->ohead, self->osize-self->ohead, fmt, ap); + va_end(ap); + if(msglen < 0) {fprintf(stderr, "Pd: buffer space wasn't sufficient for long GUI string\n"); return;} + if (msglen >= self->osize - self->ohead) { + int msglen2, newsize = self->osize+1+max(msglen,GUI_ALLOCCHUNK); + sys_trytogetmoreguibuf(self,newsize); + va_start(ap, fmt); + msglen2 = vsnprintf(self->obuf+self->ohead, self->osize-self->ohead, fmt, ap); + va_end(ap); + if (msglen2 != msglen) bug("sys_vgui"); + if (msglen >= self->osize-self->ohead) msglen = self->osize-self->ohead; + } + self->ohead += msglen; + self->bytessincelastping += msglen; +} + +void sys_gui(char *s) {sys_vgui("%s", s);} + +static int sys_flushtogui(t_socketreceiver *self) { + int writesize = self->ohead-self->otail; + if (!writesize) return 0; + int nwrote = send(self->fd, self->obuf+self->otail, writesize, 0); + if (nwrote < 0) { + perror("pd-to-gui socket"); + sys_bail(1); + } else if (!nwrote) { + return 0; + } else if (nwrote >= self->ohead-self->otail) { + self->ohead = self->otail = 0; + } else if (nwrote) { + self->otail += nwrote; + if (self->otail > self->osize>>2) { + memmove(self->obuf, self->obuf+self->otail, self->ohead-self->otail); + self->ohead -= self->otail; + self->otail = 0; + } + } + return 1; +} + +void glob_ping(t_pd *dummy) {t_socketreceiver *self = sys_socketreceiver; self->waitingforping = 0;} + +int sys_pollgui() { + if (sys_socketreceiver) sys_flushtogui(sys_socketreceiver); + return sys_pollsockets(); +} + +/* --------------------- starting up the GUI connection ------------- */ + +#ifdef __linux__ +void glob_watchdog(t_pd *dummy) { +#ifndef WATCHDOGTHREAD + if (write(sys_watchfd, "\n", 1) < 1) { + fprintf(stderr, "pd: watchdog process died\n"); + sys_bail(1); + } +#endif +} +#endif + +static void sys_setsignals() { +#ifdef UNISTD + signal(SIGHUP, sys_huphandler); + signal(SIGINT, sys_exithandler); + signal(SIGQUIT, sys_exithandler); + signal(SIGILL, sys_exithandler); + signal(SIGIOT, sys_exithandler); + signal(SIGFPE, SIG_IGN); +/* signal(SIGILL, sys_exithandler); + signal(SIGBUS, sys_exithandler); + signal(SIGSEGV, sys_exithandler); */ + signal(SIGPIPE, SIG_IGN); + signal(SIGALRM, SIG_IGN); +/* GG says: don't set SIGSTKFLT */ +#endif +} + +//t_pd *pd_new3(const char *s); + +extern "C" t_text *netreceive_new(t_symbol *compatflag, t_floatarg fportno, t_floatarg udpflag); +static int sys_start_watchdog_thread(); +static void sys_setpriority(); +extern t_text *manager; + +int sys_startgui() { +#ifdef _WIN32 + short version = MAKEWORD(2, 0); + WSADATA nobby; + if (WSAStartup(version, &nobby)) sys_sockerror("WSAstartup"); +#endif /* _WIN32 */ + /* create an empty FD poll list */ + sys_fdpoll = (t_fdpoll *)t_getbytes(0); + sys_nfdpoll = 0; + inbinbuf = binbuf_new(); + sys_setsignals(); + sys_netreceive = netreceive_new(&s_,sys_guisetportnumber,0); +// fprintf(stderr,"sys_netreceive=%p\n",sys_netreceive); + if (!sys_netreceive) return 0; +// obj_connect(sys_netreceive,0,(t_text *)pd_new3("print left"),0); +// obj_connect(sys_netreceive,1,(t_text *)pd_new3("print right"),0); + obj_connect(sys_netreceive,0,manager,0); +#if defined(__linux__) || defined(IRIX) +/* now that we've spun off the child process we can promote our process's + priority, if we can and want to. If not specfied (-1), we check root + status. This misses the case where we might have permission from a + "security module" (linux 2.6) -- I don't know how to test for that. + The "-rt" flag must be set in that case. */ + if (sys_hipriority == -1) sys_hipriority = !getuid() || !geteuid(); +#endif + if (sys_hipriority) sys_setpriority(); + setuid(getuid()); + return 0; +} + +/* To prevent lockup, we fork off a watchdog process with higher real-time + priority than ours. The GUI has to send a stream of ping messages to the + watchdog THROUGH the Pd process which has to pick them up from the GUI + and forward them. If any of these things aren't happening the watchdog + starts sending "stop" and "cont" signals to the Pd process to make it + timeshare with the rest of the system. (Version 0.33P2 : if there's no + GUI, the watchdog pinging is done from the scheduler idle routine in this + process instead.) */ +void sys_setpriority() { +#if defined(__linux__) || defined(IRIX) +#ifndef WATCHDOGTHREAD + int pipe9[2]; + if (pipe(pipe9) < 0) { + setuid(getuid()); + sys_sockerror("pipe"); + return 1; + } + int watchpid = fork(); + if (watchpid < 0) { + setuid(getuid()); + if (errno) perror("sys_startgui"); else fprintf(stderr, "sys_startgui failed\n"); + return 1; + } else if (!watchpid) { /* we're the child */ + sys_set_priority(1); + setuid(getuid()); + if (pipe9[1]) { + dup2(pipe9[0], 0); + close(pipe9[0]); + } + close(pipe9[1]); + sprintf(cmdbuf, "%s/pd-watchdog\n", guidir); + if (sys_verbose) fprintf(stderr, "%s", cmdbuf); + execl("/bin/sh", "sh", "-c", cmdbuf, (char*)0); + perror("pd: exec"); + _exit(1); + } else { /* we're the parent */ + sys_set_priority(0); + setuid(getuid()); + close(pipe9[0]); + sys_watchfd = pipe9[1]; + /* We also have to start the ping loop in the GUI; this is done later when the socket is open. */ + } +#else + sys_start_watchdog_thread(); + sys_set_priority(0); +#endif +#endif /* __linux__ */ +#ifdef MSW + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) + fprintf(stderr, "pd: couldn't set high priority class\n"); +#endif +#ifdef __APPLE__ + if (sys_hipriority) { + struct sched_param param; + int policy = SCHED_RR; + param.sched_priority = 80; /* adjust 0 : 100 */ + int err = pthread_setschedparam(pthread_self(), policy, ¶m); + if (err) post("warning: high priority scheduling failed"); + } +#endif /* __APPLE__ */ + if (!sys_nogui) { + /* here is where we start the pinging. */ +#if defined(__linux__) || defined(IRIX) +#ifndef WATCHDOGTHREAD + if (sys_hipriority) sys_gui("pdtk_watchdog\n"); +#endif +#endif + } +} + +/* This is called when something bad has happened, like a segfault. +Call glob_quit() below to exit cleanly. +LATER try to save dirty documents even in the bad case. */ +void sys_bail(int n) { + static int reentered = 0; + if (reentered) _exit(1); + reentered = 1; + fprintf(stderr, "closing audio...\n"); + sys_close_audio(); + fprintf(stderr, "closing MIDI...\n"); + sys_close_midi(); + fprintf(stderr, "... done.\n"); + exit(n); +} + +extern "C" void glob_closeall(void *dummy, t_floatarg fforce); + +void glob_quit(void *dummy) { + glob_closeall(0, 1); + sys_bail(0); +} + +static pthread_t watchdog_id; +static pthread_t main_pd_thread; +static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t watchdog_cond = PTHREAD_COND_INITIALIZER; +static void *watchdog_thread(void *); + +/* start a high priority watchdog thread */ +static int sys_start_watchdog_thread() { + pthread_attr_t w_attr; + main_pd_thread = pthread_self(); + pthread_attr_init(&w_attr); + pthread_attr_setschedpolicy(&w_attr, SCHED_FIFO); /* use rt scheduling */ + int status = pthread_create(&watchdog_id, &w_attr, (void*(*)(void*)) watchdog_thread, NULL); + return status; /* what is this function supposed to return anyway? it tried returning void though declared as int */ +} + +#ifdef MSW +int gettimeofday (struct timeval *tv, void *tz); +#endif + +static t_int* watchdog_callback(t_int *dummy) { + /* signal the condition */ + pthread_cond_signal(&watchdog_cond); + return 0; +} + +/* this watchdog thread registers an idle callback once a minute + if the idle callback isn't excecuted within one minute, we're probably + blocking the system. + kill the whole process! +*/ + +static void *watchdog_thread(void *dummy) { + sys_set_priority(1); + post("watchdog thread started"); + sys_microsleep(60*1000*1000); /* wait 60 seconds ... hoping that everything is set up */ + post("watchdog thread active"); + while (1) { + struct timespec timeout; + struct timeval now; + int status; + gettimeofday(&now,0); + timeout.tv_sec = now.tv_sec + 15; /* timeout: 15 seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + sys_callback((t_int(*)(t_int*))watchdog_callback, 0, 0); + status = pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &timeout); + if (status) { +#if defined(__linux__) || defined(IRIX) + fprintf(stderr, "watchdog killing"); + kill(0,9); /* kill parent thread */ +#endif + } + sys_microsleep(15*1000*1000); /* and sleep for another 15 seconds */ + } + return 0; /* avoid warning */ +} -- cgit v1.2.1