Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

8968 lines (7183 sloc) 195.914 kb
/* File: "os_io.c" */
/* Copyright (c) 1994-2012 by Marc Feeley, All Rights Reserved. */
/*
* This module implements the operating system specific routines
* related to I/O.
*/
#define ___INCLUDED_FROM_OS_IO
#define ___VERSION 406005
#include "gambit.h"
#include "os_base.h"
#include "os_io.h"
#include "os_tty.h"
#include "os_files.h"
#include "setup.h"
#include "c_intf.h"
/*---------------------------------------------------------------------------*/
___io_module ___io_mod =
{
0,
0
#ifdef ___IO_MODULE_INIT
___IO_MODULE_INIT
#endif
};
/*---------------------------------------------------------------------------*/
/* Device groups. */
___SCMOBJ ___device_group_setup
___P((___device_group **dgroup),
(dgroup)
___device_group **dgroup;)
{
___SCMOBJ e;
___device_group *g;
g = ___CAST(___device_group*,
___alloc_mem (sizeof (___device_group)));
if (g == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
g->list = NULL;
*dgroup = g;
return ___FIX(___NO_ERR);
}
void ___device_group_cleanup
___P((___device_group *dgroup),
(dgroup)
___device_group *dgroup;)
{
while (dgroup->list != NULL)
if (___device_cleanup (dgroup->list) != ___FIX(___NO_ERR))
break;
___free_mem (dgroup);
}
void ___device_add_to_group
___P((___device_group *dgroup,
___device *dev),
(dgroup,
dev)
___device_group *dgroup;
___device *dev;)
{
___device *head = dgroup->list;
dev->group = dgroup;
if (head == NULL)
{
dev->next = dev;
dev->prev = dev;
dgroup->list = dev;
}
else
{
___device *tail = head->prev;
dev->next = head;
dev->prev = tail;
tail->next = dev;
head->prev = dev;
}
}
void ___device_remove_from_group
___P((___device *dev),
(dev)
___device *dev;)
{
___device_group *dgroup = dev->group;
___device *prev = dev->prev;
___device *next = dev->next;
if (prev == dev)
dgroup->list = NULL;
else
{
if (dgroup->list == dev)
dgroup->list = next;
prev->next = next;
next->prev = prev;
dev->next = dev;
dev->prev = dev;
}
dev->group = NULL;
}
___device_group *___global_device_group ___PVOID
{
return ___io_mod.dgroup;
}
/*---------------------------------------------------------------------------*/
/* Nonblocking pipes */
#ifdef USE_PUMPS
___HIDDEN ___SCMOBJ ___nonblocking_pipe_setup
___P((___nonblocking_pipe *pipe,
int size),
(pipe,
size)
___nonblocking_pipe *pipe;
int size;)
{
___U8 *buffer;
HANDLE mutex;
HANDLE revent;
HANDLE wevent;
buffer = ___CAST(___U8*,
___alloc_mem (size));
if (buffer == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
mutex = CreateMutex (NULL, /* can't inherit */
FALSE, /* unlocked */
NULL); /* no name */
if (mutex == NULL)
{
___SCMOBJ e = err_code_from_GetLastError ();
___free_mem (buffer);
return e;
}
revent = CreateEvent (NULL, /* can't inherit */
TRUE, /* manual reset */
FALSE, /* not signaled */
NULL); /* no name */
if (revent == NULL)
{
___SCMOBJ e = err_code_from_GetLastError ();
CloseHandle (mutex); /* ignore error */
___free_mem (buffer);
return e;
}
wevent = CreateEvent (NULL, /* can't inherit */
TRUE, /* manual reset */
FALSE, /* not signaled */
NULL); /* no name */
if (wevent == NULL)
{
___SCMOBJ e = err_code_from_GetLastError ();
CloseHandle (revent); /* ignore error */
CloseHandle (mutex); /* ignore error */
___free_mem (buffer);
return e;
}
pipe->mutex = mutex;
pipe->revent = revent;
pipe->wevent = wevent;
pipe->rerr = ___FIX(___NO_ERR);
pipe->werr = ___FIX(___NO_ERR);
pipe->oob = 0;
pipe->rd = 0;
pipe->wr = 0;
pipe->size = size;
pipe->buffer = buffer;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_cleanup
___P((___nonblocking_pipe *pipe),
(pipe)
___nonblocking_pipe *pipe;)
{
CloseHandle (pipe->wevent); /* ignore error */
CloseHandle (pipe->revent); /* ignore error */
CloseHandle (pipe->mutex); /* ignore error */
___free_mem (pipe->buffer);
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_set_reader_err
___P((___nonblocking_pipe *pipe,
___SCMOBJ err),
(pipe,
err)
___nonblocking_pipe *pipe;
___SCMOBJ err;)
{
if (WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
/* note: the reader error indicator may get overwritten */
pipe->rerr = err;
SetEvent (pipe->wevent); /* ignore error */
if (pipe->werr == ___FIX(___NO_ERR))
ResetEvent (pipe->revent); /* ignore error */
ReleaseMutex (pipe->mutex); /* ignore error */
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_set_writer_err
___P((___nonblocking_pipe *pipe,
___SCMOBJ err),
(pipe,
err)
___nonblocking_pipe *pipe;
___SCMOBJ err;)
{
if (WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
/* note: the writer error indicator may get overwritten */
pipe->werr = err;
SetEvent (pipe->revent); /* ignore error */
if (pipe->rerr == ___FIX(___NO_ERR))
ResetEvent (pipe->wevent); /* ignore error */
ReleaseMutex (pipe->mutex); /* ignore error */
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_write_oob
___P((___nonblocking_pipe *pipe,
___nonblocking_pipe_oob_msg *oob_msg),
(pipe,
oob_msg)
___nonblocking_pipe *pipe;
___nonblocking_pipe_oob_msg *oob_msg;)
{
if (WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
if (pipe->werr != ___FIX(___NO_ERR) || pipe->oob)
{
ReleaseMutex (pipe->mutex); /* ignore error */
return ___ERR_CODE_EAGAIN;
}
pipe->oob = 1;
pipe->oob_msg = *oob_msg;
if (pipe->rerr == ___FIX(___NO_ERR))
{
SetEvent (pipe->revent); /* ignore error */
ResetEvent (pipe->wevent); /* ignore error */
}
ReleaseMutex (pipe->mutex); /* ignore error */
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_read_ready_wait
___P((___nonblocking_pipe *pipe),
(pipe)
___nonblocking_pipe *pipe;)
{
___SCMOBJ werr;
if (WaitForSingleObject (pipe->revent, INFINITE) == WAIT_FAILED ||
WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
werr = pipe->werr;
if (werr != ___FIX(___NO_ERR))
{
pipe->werr = ___FIX(___NO_ERR);
if (pipe->rerr != ___FIX(___NO_ERR) ||
(pipe->rd == pipe->wr && !pipe->oob))
ResetEvent (pipe->revent); /* ignore error */
}
ReleaseMutex (pipe->mutex); /* ignore error */
return werr;
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_write_ready_wait
___P((___nonblocking_pipe *pipe),
(pipe)
___nonblocking_pipe *pipe;)
{
___SCMOBJ rerr;
if (WaitForSingleObject (pipe->wevent, INFINITE) == WAIT_FAILED ||
WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
rerr = pipe->rerr;
if (rerr != ___FIX(___NO_ERR))
{
pipe->rerr = ___FIX(___NO_ERR);
ResetEvent (pipe->wevent); /* ignore error */
}
ReleaseMutex (pipe->mutex); /* ignore error */
return rerr;
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_read
___P((___nonblocking_pipe *pipe,
___U8 *buf,
___stream_index len,
___stream_index *len_done,
___nonblocking_pipe_oob_msg *oob_msg),
(pipe,
buf,
len,
len_done,
oob_msg)
___nonblocking_pipe *pipe;
___U8 *buf;
___stream_index len;
___stream_index *len_done;
___nonblocking_pipe_oob_msg *oob_msg;)
{
___SCMOBJ rerr;
___SCMOBJ werr;
DWORD rd;
DWORD wr;
___U8 *p;
DWORD end;
DWORD n;
DWORD i;
if (len <= 0)
return ___FIX(___UNKNOWN_ERR);
if (WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
rerr = pipe->rerr;
werr = pipe->werr;
rd = pipe->rd;
wr = pipe->wr;
if (rerr != ___FIX(___NO_ERR))
{
/* there is a reader error */
if (werr == ___FIX(___NO_ERR))
werr = ___ERR_CODE_EAGAIN;
else
{
pipe->werr = ___FIX(___NO_ERR);
ResetEvent (pipe->revent); /* ignore error */
}
ReleaseMutex (pipe->mutex); /* ignore error */
return werr;
}
/* there is no reader error */
if (rd == wr)
{
/* no bytes in FIFO buffer */
if (pipe->oob)
{
/* out-of-band present */
*oob_msg = pipe->oob_msg;
pipe->oob = 0;
if (werr == ___FIX(___NO_ERR))
{
ResetEvent (pipe->revent); /* ignore error */
#if 0
/******************zzzzzzzzzzzzzz****/
SetEvent (pipe->wevent); /* ignore error */
#endif
}
ReleaseMutex (pipe->mutex); /* ignore error */
*len_done = 0;
return ___FIX(___NO_ERR);
}
/* out-of-band not present */
if (werr != ___FIX(___NO_ERR))
{
/* there is a writer error */
pipe->werr = ___FIX(___NO_ERR);
ResetEvent (pipe->revent); /* ignore error */
ReleaseMutex (pipe->mutex); /* ignore error */
return werr;
}
/* there is no writer error */
SetEvent (pipe->wevent); /* ignore error */
ReleaseMutex (pipe->mutex); /* ignore error */
return ___ERR_CODE_EAGAIN;
}
/* at least one byte in FIFO buffer */
end = pipe->size - rd; /* number of bytes from rd to end */
n = wr + end; /* number of bytes available */
if (n >= pipe->size)
n -= pipe->size;
if (n > ___CAST(DWORD,len)) /* don't transfer more than len */
n = len;
*len_done = n;
p = pipe->buffer + rd; /* prepare transfer source */
rd = rd + n;
if (rd >= pipe->size)
rd -= pipe->size;
pipe->rd = rd;
if (werr == ___FIX(___NO_ERR) && !pipe->oob)
{
/* there is no writer error and out-of-band not present */
if (rd == wr) /* the FIFO will be empty? */
ResetEvent (pipe->revent); /* ignore error */
/* the FIFO will not be full */
SetEvent (pipe->wevent); /* ignore error */
}
if (n <= end)
{
/* only need to transfer n bytes starting from original rd */
for (i=n; i>0; i--)
*buf++ = *p++;
}
else
{
/* need to transfer end bytes starting from original rd */
for (i=end; i>0; i--)
*buf++ = *p++;
/* and to transfer n-end bytes starting from 0 */
p = pipe->buffer;
for (i=n-end; i>0; i--)
*buf++ = *p++;
}
ReleaseMutex (pipe->mutex); /* ignore error */
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___nonblocking_pipe_write
___P((___nonblocking_pipe *pipe,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(pipe,
buf,
len,
len_done)
___nonblocking_pipe *pipe;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___SCMOBJ rerr;
___SCMOBJ werr;
DWORD rd;
DWORD wr;
___U8 *p;
DWORD end;
DWORD n;
DWORD i;
if (len <= 0)
return ___FIX(___UNKNOWN_ERR);
if (WaitForSingleObject (pipe->mutex, INFINITE) == WAIT_FAILED)
return err_code_from_GetLastError ();
rerr = pipe->rerr;
werr = pipe->werr;
rd = pipe->rd;
wr = pipe->wr;
if (werr != ___FIX(___NO_ERR))
{
/* there is a writer error */
if (rerr == ___FIX(___NO_ERR))
rerr = ___ERR_CODE_EAGAIN;
else
{
pipe->rerr = ___FIX(___NO_ERR);
ResetEvent (pipe->wevent); /* ignore error */
}
ReleaseMutex (pipe->mutex); /* ignore error */
return rerr;
}
/* there is no writer error */
if (rerr != ___FIX(___NO_ERR))
{
/* there is a reader error */
pipe->rerr = ___FIX(___NO_ERR);
if (rd != wr || pipe->oob) /* FIFO is not empty */
SetEvent (pipe->revent); /* ignore error */
ResetEvent (pipe->wevent); /* ignore error */
ReleaseMutex (pipe->mutex); /* ignore error */
return rerr;
}
/* there is no reader error */
if (wr + 1 - rd == 0 || wr + 1 - rd == pipe->size || pipe->oob)
{
/* FIFO buffer is full or out-of-band present */
ReleaseMutex (pipe->mutex); /* ignore error */
return ___ERR_CODE_EAGAIN;
}
/* FIFO buffer is not full and out-of-band not present */
end = pipe->size - wr; /* number of bytes from wr to end */
n = rd + end - 1; /* number of bytes available */
if (n >= pipe->size)
n -= pipe->size;
if (n > ___CAST(DWORD,len)) /* don't transfer more than len */
n = len;
*len_done = n;
p = pipe->buffer + wr; /* prepare transfer source */
wr = wr + n;
if (wr >= pipe->size)
wr -= pipe->size;
pipe->wr = wr;
if (wr + 1 - rd == 0 || wr + 1 - rd == pipe->size) /* FIFO will be full? */
ResetEvent (pipe->wevent); /* ignore error */
/* FIFO will not be empty */
SetEvent (pipe->revent); /* ignore error */
if (n <= end)
{
/* only need to transfer n bytes starting from original wr */
for (i=n; i>0; i--)
*p++ = *buf++;
}
else
{
/* need to transfer end bytes starting from original wr */
for (i=end; i>0; i--)
*p++ = *buf++;
/* and to transfer n-end bytes starting from 0 */
p = pipe->buffer;
for (i=n-end; i>0; i--)
*p++ = *buf++;
}
ReleaseMutex (pipe->mutex); /* ignore error */
return ___FIX(___NO_ERR);
}
#endif
/*---------------------------------------------------------------------------*/
/* Operations on I/O devices. */
/* Miscellaneous utility functions. */
#ifdef USE_POSIX
#ifdef USE_sigaction
typedef sigset_t sigset_type;
#else
typedef int sigset_type;
#endif
___HIDDEN sigset_type block_signal
___P((int signum),
(signum)
int signum;)
{
sigset_type oldmask;
#ifdef USE_sigaction
sigset_type toblock;
sigemptyset (&toblock);
sigaddset (&toblock, signum);
sigprocmask (SIG_BLOCK, &toblock, &oldmask);
#endif
#ifdef USE_signal
oldmask = sigblock (sigmask (signum));
#endif
return oldmask;
}
___HIDDEN void restore_sigmask
___P((sigset_type oldmask),
(oldmask)
sigset_type oldmask;)
{
#ifdef USE_sigaction
sigprocmask (SIG_SETMASK, &oldmask, 0);
#endif
#ifdef USE_signal
sigsetmask (oldmask);
#endif
}
/*
* Some system calls can be interrupted by a signal and fail with
* errno == EINTR. The following functions are wrappers for system
* calls which may be interrupted. They simply ignore the EINTR and
* retry the operation.
*
* TODO: add wrappers for all the system calls which can fail
* with EINTR. Also, move these functions to a central place.
*/
pid_t waitpid_no_EINTR
___P((pid_t pid,
int *stat_loc,
int options),
(pid,
stat_loc,
options)
pid_t pid;
int *stat_loc;
int options;)
{
pid_t result;
for (;;)
{
result = waitpid (pid, stat_loc, options);
if (result >= 0 || errno != EINTR)
break;
}
return result;
}
ssize_t read_no_EINTR
___P((int fd,
void *buf,
size_t len),
(fd,
buf,
len)
int fd;
void *buf;
size_t len;)
{
char *p = ___CAST(char*,buf);
ssize_t result = 0;
int n;
while (result < len)
{
n = read (fd, p+result, len-result);
if (n > 0)
result += n;
else if (n == 0)
break;
else if (errno != EINTR)
return n; /* this forgets that some bytes were transferred */
}
return result;
}
int close_no_EINTR
___P((int fd),
(fd)
int fd;)
{
int result;
for (;;)
{
result = close (fd);
if (result >= 0 || errno != EINTR)
break;
}
return result;
}
int dup_no_EINTR
___P((int fd),
(fd)
int fd;)
{
int result;
for (;;)
{
result = dup (fd);
if (result >= 0 || errno != EINTR)
break;
}
return result;
}
int dup2_no_EINTR
___P((int fd,
int fd2),
(fd,
fd2)
int fd;
int fd2;)
{
int result;
for (;;)
{
result = dup2 (fd, fd2);
if (result >= 0 || errno != EINTR)
break;
}
return result;
}
int set_fd_blocking_mode
___P((int fd,
___BOOL blocking),
(fd,
blocking)
int fd;
___BOOL blocking;)
{
int fl;
if ((fl = fcntl (fd, F_GETFL, 0)) >= 0)
fl = fcntl (fd,
F_SETFL,
blocking ? (fl & ~O_NONBLOCK) : (fl | O_NONBLOCK));
return fl;
}
#endif
/*---------------------------------------------------------------------------*/
/* Generic device operations. */
___SCMOBJ ___device_select
___P((___device **devs,
int nb_read_devs,
int nb_write_devs,
___time timeout),
(devs,
nb_read_devs,
nb_write_devs,
timeout)
___device **devs;
int nb_read_devs;
int nb_write_devs;
___time timeout;)
{
int nb_devs;
___device_select_state state;
int pass;
int dev_list;
int i;
int prev;
___time delta;
nb_devs = nb_read_devs + nb_write_devs;
state.devs = devs;
state.timeout = timeout;
state.relative_timeout = POS_INFINITY;
#ifdef USE_select
state.highest_fd_plus_1 = 0;
FD_ZERO(&state.readfds);
FD_ZERO(&state.writefds);
FD_ZERO(&state.exceptfds);
#endif
#ifdef USE_MsgWaitForMultipleObjects
state.message_queue_mask = 0;
state.message_queue_dev_pos = -1;
state.wait_objs_buffer[0] = ___io_mod.abort_select;
state.wait_objs_buffer[1] = ___time_mod.heartbeat_thread;
state.nb_wait_objs = 2;
#endif
if (nb_devs > 0)
{
state.devs_next[nb_devs-1] = -1;
for (i=nb_devs-2; i>=0; i--)
state.devs_next[i] = i+1;
dev_list = 0;
}
else
dev_list = -1;
pass = ___SELECT_PASS_1;
while (dev_list != -1)
{
i = dev_list;
prev = -1;
while (i != -1)
{
___SCMOBJ e;
___device *d = devs[i];
if ((e = ___device_select_virt
(d,
i>=nb_read_devs,
i,
pass,
&state))
== ___FIX(___NO_ERR))
{
prev = i;
i = state.devs_next[i];
}
else
{
int j;
if (e != ___FIX(___SELECT_SETUP_DONE))
return e;
j = state.devs_next[i];
if (prev == -1)
dev_list = j;
else
state.devs_next[prev] = j;
#ifdef USE_MsgWaitForMultipleObjects
state.devs_next[i] = -1;
#endif
i = j;
}
}
pass++;
}
___absolute_time_to_relative_time (state.timeout, &delta);
if (___time_less (state.relative_timeout, delta))
{
delta = state.relative_timeout;
state.timeout = ___time_mod.time_neg_infinity;
}
else
state.relative_timeout = NEG_INFINITY;
#ifdef USE_select
{
struct timeval delta_tv_struct;
struct timeval *delta_tv = &delta_tv_struct;
int result;
___absolute_time_to_nonnegative_timeval (delta, &delta_tv);
if (delta_tv != NULL &&
state.highest_fd_plus_1 == 0)
{
/*
* ___device_select is only being called for sleeping until a
* certain timeout or interrupt occurs. This is a case that
* can be optimized.
*/
if (delta_tv->tv_sec < 0 ||
(delta_tv->tv_sec == 0 &&
delta_tv->tv_usec == 0))
{
/*
* The timeout has already passed, so we don't need to
* sleep. This simple optimization avoids doing a system
* call to the select or nanosleep functions (which can be
* expensive on some operating systems).
*/
result = 0;
goto select_done;
}
#ifdef USE_nanosleep
else
{
/*
* For better timeout resolution, the nanosleep function
* is used instead of the select function. On some
* operating systems (e.g. OpenBSD 4.5) the nanosleep
* function can be more expensive than a call to select,
* but the better timeout resolution outweighs the run
* time cost.
*/
struct timespec delta_ts_struct;
delta_ts_struct.tv_sec = delta_tv->tv_sec;
delta_ts_struct.tv_nsec = delta_tv->tv_usec * 1000;
result = nanosleep (&delta_ts_struct, NULL);
goto select_done;
}
#endif
}
/*
* Heartbeat interrupts must be disabled in case they are based on the
* real-time timer. This is needed to bypass issues in two buggy
* operating systems:
*
* - On MacOS X, the virtual-time timer does not fire at the correct
* rate (apparently this happens only on machines with more than
* one core).
*
* - On CYGWIN, the select system call can be interrupted by the
* timer and in some cases the error "No child processes" will
* be returned by select.
*/
___disable_heartbeat_interrupts ();
result =
select (state.highest_fd_plus_1,
&state.readfds,
&state.writefds,
&state.exceptfds,
delta_tv);
___enable_heartbeat_interrupts ();
select_done:
if (result < 0)
return err_code_from_errno ();
state.timeout_reached = (result == 0);
}
#endif
#ifdef USE_MsgWaitForMultipleObjects
{
DWORD delta_msecs;
int first_iteration = TRUE;
___absolute_time_to_nonnegative_msecs (delta, &delta_msecs);
state.timeout_reached = 0;
while (state.nb_wait_objs > 0 || state.message_queue_mask != 0)
{
DWORD n;
n = MsgWaitForMultipleObjects
(state.nb_wait_objs,
state.wait_objs_buffer,
FALSE,
delta_msecs,
state.message_queue_mask);
if (n == WAIT_FAILED)
return err_code_from_GetLastError ();
if ((n - WAIT_OBJECT_0) <= WAIT_OBJECT_0 + state.nb_wait_objs)
n -= WAIT_OBJECT_0;
else if (n >= WAIT_ABANDONED_0 &&
n <= WAIT_ABANDONED_0+state.nb_wait_objs-1)
n -= WAIT_ABANDONED_0;
else
{
/* n == WAIT_TIMEOUT */
/*
* The call to MsgWaitForMultipleObjects timed out. Mark
* the appropriate device "ready".
*/
if (first_iteration)
{
/* first call to MsgWaitForMultipleObjects */
state.timeout_reached = 1;
}
break;
}
if (n == state.nb_wait_objs)
{
/*
* The message queue contains a message that is of interest.
* Mark the appropriate device "ready".
*/
i = state.message_queue_dev_pos;
if (i >= 0)
state.devs_next[i] = 0;
/*
* Don't check if other devices are ready because this might
* cause an infinite loop.
*/
break;
}
else if (n == 0)
{
/*
* The call to ___device_select must be aborted because the
* abort_select event is set. This occurs when an interrupt
* (such as a CTRL-C user interrupt) needs to be serviced
* promptly by the main program.
*/
ResetEvent (___io_mod.abort_select); /* ignore error */
return ___FIX(___ERRNO_ERR(EINTR));
}
else if (n == 1)
{
/*
* The heartbeat thread has died. This is normally due to
* the program being terminated abruptly by the user (for
* example by using the thread manager or the "shutdown"
* item in the start menu). When this happens we must
* initiate a clean termination of the program.
*/
return ___FIX(___UNKNOWN_ERR);
}
else
{
/* Mark the appropriate device "ready". */
i = state.wait_obj_to_dev_pos[n];
if (i >= 0)
state.devs_next[i] = 0;
/* Prepare to check remaining devices. */
state.nb_wait_objs--;
state.wait_objs_buffer[n] =
state.wait_objs_buffer[state.nb_wait_objs];
state.wait_obj_to_dev_pos[n] =
state.wait_obj_to_dev_pos[state.nb_wait_objs];
}
first_iteration = FALSE;
delta_msecs = 0; /* next MsgWaitForMultipleObjects will only poll */
}
}
#endif
for (i=nb_devs-1; i>=0; i--)
{
___SCMOBJ e;
___device *d = devs[i];
if (d != NULL)
if ((e = ___device_select_virt
(d,
i>=nb_read_devs,
i,
___SELECT_PASS_CHECK,
&state))
!= ___FIX(___NO_ERR))
return e;
}
return ___FIX(___NO_ERR);
}
void ___device_select_add_relative_timeout
___P((___device_select_state *state,
int i,
___F64 seconds),
(state,
i,
seconds)
___device_select_state *state;
int i;
___F64 seconds;)
{
if (seconds < state->relative_timeout)
state->relative_timeout = seconds;
}
void ___device_select_add_timeout
___P((___device_select_state *state,
int i,
___time timeout),
(state,
i,
timeout)
___device_select_state *state;
int i;
___time timeout;)
{
if (___time_less (timeout, state->timeout))
state->timeout = timeout;
}
#ifdef USE_select
void ___device_select_add_fd
___P((___device_select_state *state,
int fd,
___BOOL for_writing),
(state,
fd,
for_writing)
___device_select_state *state;
int fd;
___BOOL for_writing;)
{
if (for_writing)
FD_SET(fd, &state->writefds);
else
FD_SET(fd, &state->readfds);
if (fd >= state->highest_fd_plus_1)
state->highest_fd_plus_1 = fd+1;
}
#endif
#ifdef USE_MsgWaitForMultipleObjects
void ___device_select_add_wait_obj
___P((___device_select_state *state,
int i,
HANDLE wait_obj),
(state,
i,
wait_obj)
___device_select_state *state;
int i;
HANDLE wait_obj;)
{
DWORD j = state->nb_wait_objs;
if (j < MAXIMUM_WAIT_OBJECTS)
{
state->wait_objs_buffer[j] = wait_obj;
state->wait_obj_to_dev_pos[j] = i;
state->nb_wait_objs = j+1;
}
}
#endif
___SCMOBJ ___device_force_output
___P((___device *self,
int level),
(self,
level)
___device *self;
int level;)
{
return ___device_force_output_virt (self, level);
}
___SCMOBJ ___device_close
___P((___device *self,
int direction),
(self,
direction)
___device *self;
int direction;)
{
return ___device_close_virt (self, direction);
}
___HIDDEN void device_transfer_close_responsibility
___P((___device *self),
(self)
___device *self;)
{
/*
* Transfer responsibility for closing device to the runtime system.
*/
self->close_direction = self->direction;
}
___HIDDEN void device_add_ref
___P((___device *self),
(self)
___device *self;)
{
#ifdef USE_PUMPS
InterlockedIncrement (&self->refcount);
#else
self->refcount++;
#endif
}
___SCMOBJ ___device_release
___P((___device *self),
(self)
___device *self;)
{
___SCMOBJ e = ___FIX(___NO_ERR);
if (
#ifdef USE_PUMPS
InterlockedDecrement (&self->refcount) == 0
#else
--self->refcount == 0
#endif
)
{
e = ___device_release_virt (self);
___free_mem (self);
}
return e;
}
___SCMOBJ ___device_cleanup
___P((___device *self),
(self)
___device *self;)
{
___SCMOBJ e;
___device *devs[1];
if (self->group == NULL)
return ___FIX(___UNKNOWN_ERR);
___device_remove_from_group (self);
for (;;)
{
e = ___device_close (self, ___DIRECTION_RD);
if (e == ___FIX(___NO_ERR))
break;
if (e != ___ERR_CODE_EAGAIN)
return e;
devs[0] = self;
e = ___device_select (devs, 1, 0, ___time_mod.time_pos_infinity);
if (e != ___FIX(___NO_ERR))
return e;
}
for (;;)
{
e = ___device_close (self, ___DIRECTION_WR);
if (e == ___FIX(___NO_ERR))
break;
if (e != ___ERR_CODE_EAGAIN)
return e;
devs[0] = self;
e = ___device_select (devs, 0, 1, ___time_mod.time_pos_infinity);
if (e != ___FIX(___NO_ERR))
return e;
}
return ___device_release (self);
}
/*
* Procedure called by the Scheme runtime when a device is no longer
* reachable.
*/
___SCMOBJ ___device_cleanup_from_ptr
___P((void *ptr),
(ptr)
void *ptr;)
{
return ___device_cleanup (___CAST(___device*,ptr));
}
/* - - - - - - - - - - - - - - - - - - */
/* Timer device. */
/*
* Timer devices are not particularly useful, given that the scheduler
* implements timeouts. Use this as an example.
*/
___HIDDEN int device_timer_kind
___P((___device *self),
(self)
___device *self;)
{
return ___TIMER_KIND;
}
___HIDDEN ___SCMOBJ device_timer_select_virt
___P((___device *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
___device_timer *d = ___CAST(___device_timer*,self);
if (pass == ___SELECT_PASS_1)
{
if (___time_less (d->expiry, state->timeout))
state->timeout = d->expiry;
return ___FIX(___SELECT_SETUP_DONE);
}
/* pass == ___SELECT_PASS_CHECK */
if (state->timeout_reached)
{
if (___time_equal (d->expiry, state->timeout))
state->devs[i] = NULL;
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ device_timer_release_virt
___P((___device *self),
(self)
___device *self;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ device_timer_force_output_virt
___P((___device *self,
int level),
(self,
level)
___device *self;
int level;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ device_timer_close_virt
___P((___device *self,
int direction),
(self,
direction)
___device *self;
int direction;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___device_timer_vtbl ___device_timer_table =
{
{
device_timer_kind,
device_timer_select_virt,
device_timer_release_virt,
device_timer_force_output_virt,
device_timer_close_virt
}
};
___SCMOBJ ___device_timer_setup
___P((___device_timer **dev,
___device_group *dgroup),
(dev,
dgroup)
___device_timer **dev;
___device_group *dgroup;)
{
___device_timer *d;
d = ___CAST(___device_timer*,
___alloc_mem (sizeof (___device_timer)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
d->base.vtbl = &___device_timer_table;
d->base.refcount = 1;
d->base.direction = ___DIRECTION_RD | ___DIRECTION_WR;
d->base.close_direction = 0; /* prevent closing on errors */
d->base.read_stage = ___STAGE_OPEN;
d->base.write_stage = ___STAGE_OPEN;
d->expiry = ___time_mod.time_pos_infinity;
*dev = d;
___device_add_to_group (dgroup, &d->base);
return ___FIX(___NO_ERR);
}
void ___device_timer_set_expiry
___P((___device_timer *dev,
___time expiry),
(dev,
expiry)
___device_timer *dev;
___time expiry;)
{
dev->expiry = expiry;
}
/* - - - - - - - - - - - - - - - - - - */
/* Byte stream devices. */
#ifdef USE_PUMPS
___HIDDEN ___SCMOBJ ___device_stream_pump_setup
___P((___device_stream_pump **pump,
DWORD committed_stack_size,
LPTHREAD_START_ROUTINE proc,
LPVOID arg),
(pump,
committed_stack_size,
proc,
arg)
___device_stream_pump **pump;
DWORD committed_stack_size;
LPTHREAD_START_ROUTINE proc;
LPVOID arg;)
{
___SCMOBJ e;
___device_stream_pump *p;
HANDLE thread_handle;
DWORD thread_id;
p = ___CAST(___device_stream_pump*,
___alloc_mem (sizeof (___device_stream_pump)));
if (p == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
e = ___nonblocking_pipe_setup (&p->pipe, PIPE_BUFFER_SIZE+1);
if (e != ___FIX(___NO_ERR))
{
___free_mem (p);
return e;
}
*pump = p; /* set before thread created to avoid race condition */
thread_handle =
CreateThread (NULL, /* no security attributes */
committed_stack_size, /* committed stack size */
proc, /* thread procedure */
arg, /* argument to thread procedure */
0, /* use default creation flags */
&thread_id);
if (thread_handle == NULL ||
!SetThreadPriority (thread_handle, PUMP_PRIORITY))
{
e = err_code_from_GetLastError ();
___nonblocking_pipe_cleanup (&p->pipe);
___free_mem (p);
*pump = NULL; /* make sure caller does not think a pump was created */
return e;
}
p->thread = thread_handle;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_stream_pump_reader_kill
___P((___device_stream_pump *pump),
(pump)
___device_stream_pump *pump;)
{
return ___nonblocking_pipe_set_reader_err
(&pump->pipe,
___FIX(___KILL_PUMP));
}
___HIDDEN ___SCMOBJ ___device_stream_pump_writer_kill
___P((___device_stream_pump *pump),
(pump)
___device_stream_pump *pump;)
{
return ___nonblocking_pipe_set_writer_err
(&pump->pipe,
___FIX(___KILL_PUMP));
}
___HIDDEN ___SCMOBJ ___device_stream_pump_wait
___P((___device_stream_pump *pump),
(pump)
___device_stream_pump *pump;)
{
DWORD code;
code = WaitForSingleObject (pump->thread, 0);
if (code == WAIT_FAILED)
return err_code_from_GetLastError ();
if (code == WAIT_TIMEOUT)
return ___ERR_CODE_EAGAIN;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_stream_pump_cleanup
___P((___device_stream_pump *pump),
(pump)
___device_stream_pump *pump;)
{
CloseHandle (pump->thread); /* ignore error */
___nonblocking_pipe_cleanup (&pump->pipe); /* ignore error */
___free_mem (pump);
return ___FIX(___NO_ERR);
}
#endif
___SCMOBJ ___device_stream_select_virt
___P((___device *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
___device_stream *d = ___CAST(___device_stream*,self);
#ifdef USE_PUMPS
int stage = (for_writing
? d->base.write_stage
: d->base.read_stage);
___device_stream_pump *p = (for_writing
? d->write_pump
: d->read_pump);
if (p != NULL)
{
if (pass == ___SELECT_PASS_1)
{
HANDLE wait_obj;
if (stage != ___STAGE_OPEN)
wait_obj = p->thread;
else
{
if (for_writing)
wait_obj = p->pipe.wevent;
else
wait_obj = p->pipe.revent;
}
___device_select_add_wait_obj (state, i, wait_obj);
return ___FIX(___SELECT_SETUP_DONE);
}
/* pass == ___SELECT_PASS_CHECK */
if (stage != ___STAGE_OPEN)
state->devs[i] = NULL;
else
{
if (state->devs_next[i] != -1)
state->devs[i] = NULL;
}
return ___FIX(___NO_ERR);
}
#endif
return ___device_stream_select_raw_virt
(d,
for_writing,
i,
pass,
state);
}
___SCMOBJ ___device_stream_release_virt
___P((___device *self),
(self)
___device *self;)
{
___SCMOBJ e;
___device_stream *d = ___CAST(___device_stream*,self);
e = ___device_stream_release_raw_virt (d);
#ifdef USE_PUMPS
{
___device_stream_pump *p;
p = d->read_pump;
if (p != NULL)
___device_stream_pump_cleanup (p); /* ignore error */
p = d->write_pump;
if (p != NULL)
___device_stream_pump_cleanup (p); /* ignore error */
}
#endif
return e;
}
___SCMOBJ ___device_stream_force_output_virt
___P((___device *self,
int level),
(self,
level)
___device *self;
int level;)
{
___device_stream *d = ___CAST(___device_stream*,self);
#ifdef USE_PUMPS
{
___device_stream_pump *p = d->write_pump;
if (p != NULL)
{
___nonblocking_pipe_oob_msg oob_msg;
oob_msg.op = OOB_FORCE_OUTPUT0 + level;
return ___nonblocking_pipe_write_oob (&p->pipe, &oob_msg);
}
}
#endif
return ___device_stream_force_output_raw_virt (d, level);
}
___SCMOBJ ___device_stream_close_virt
___P((___device *self,
int direction),
(self,
direction)
___device *self;
int direction;)
{
___device_stream *d = ___CAST(___device_stream*,self);
#ifdef USE_PUMPS
if (direction & ___DIRECTION_RD)
{
___device_stream_pump *p = d->read_pump;
if (p != NULL)
___device_stream_pump_reader_kill (p);
}
if (direction & ___DIRECTION_WR)
{
___device_stream_pump *p = d->write_pump;
if (p != NULL)
___device_stream_pump_writer_kill (p);
}
#endif
return ___device_stream_close_raw_virt (d, direction);
}
___SCMOBJ ___device_stream_seek
___P((___device_stream *self,
___stream_index *pos,
int whence),
(self,
pos,
whence)
___device_stream *self;
___stream_index *pos;
int whence;)
{
#ifdef USE_PUMPS
{
___device_stream_pump *p = self->write_pump;
if (p != NULL)
{
___nonblocking_pipe_oob_msg oob_msg;
oob_msg.op = OOB_SEEK_ABS + whence;
oob_msg.stream_index_param = *pos;
return ___nonblocking_pipe_write_oob (&p->pipe, &oob_msg);
}
}
#endif
return ___device_stream_seek_raw_virt (self, pos, whence);
}
___SCMOBJ ___device_stream_read
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
#ifdef USE_PUMPS
{
___device_stream_pump *p = self->read_pump;
if (p != NULL)
{
___nonblocking_pipe_oob_msg oob_msg;
return ___nonblocking_pipe_read
(&p->pipe,
buf,
len,
len_done,
&oob_msg);
}
}
#endif
return ___device_stream_read_raw_virt (self, buf, len, len_done);
}
___SCMOBJ ___device_stream_write
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
#ifdef USE_PUMPS
{
___device_stream_pump *p = self->write_pump;
if (p != NULL)
return ___nonblocking_pipe_write (&p->pipe, buf, len, len_done);
}
#endif
return ___device_stream_write_raw_virt (self, buf, len, len_done);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef USE_PUMPS
___HIDDEN DWORD WINAPI ___device_stream_read_pump_proc
___P((LPVOID param),
(param)
LPVOID param;)
{
___device_stream *dev = ___CAST(___device_stream*,param);
___nonblocking_pipe *p = &dev->read_pump->pipe;
___SCMOBJ e;
___stream_index len;
___stream_index n;
___stream_index i;
___U8 buf[PIPE_BUFFER_SIZE];
___nonblocking_pipe_oob_msg oob_msg;
for (;;)
{
/* wait until the reader needs some data */
e = ___nonblocking_pipe_write_ready_wait (p);
/* read some characters from device */
if (e == ___FIX(___NO_ERR))
e = ___device_stream_read_raw_virt (dev, buf, PIPE_BUFFER_SIZE, &len);
if (e == ___FIX(___NO_ERR))
{
if (len == 0)
{
/* we reached the end-of-stream */
oob_msg.op = OOB_EOS;
while ((e = ___nonblocking_pipe_write_oob (p, &oob_msg))
== ___ERR_CODE_EAGAIN)
{
/* suspend thread until operation can be performed */
e = ___nonblocking_pipe_write_ready_wait (p);
if (e != ___FIX(___NO_ERR))
break;
}
if (e != ___FIX(___NO_ERR))
break;
}
else
{
/* write to the pipe the bytes that were read */
i = 0;
while (i < len)
{
while ((e = ___nonblocking_pipe_write (p, buf+i, len-i, &n))
== ___ERR_CODE_EAGAIN)
{
/* suspend thread until operation can be performed */
e = ___nonblocking_pipe_write_ready_wait (p);
if (e != ___FIX(___NO_ERR))
break;
}
if (e != ___FIX(___NO_ERR))
break;
i += n;
}
}
}
if (e != ___FIX(___NO_ERR))
{
if (e == ___FIX(___KILL_PUMP)) /* terminate? */
break;
if (e == ___ERR_CODE_EAGAIN)
continue;
/* report the failure back through the pipe */
e = ___nonblocking_pipe_set_writer_err (p, e);
if (e != ___FIX(___NO_ERR))
{
/*
* The failure could not be reported. To avoid an
* infinite loop the thread is terminated.
*/
ExitThread (0);
}
}
}
___device_release (&dev->base); /* ignore error */
return 0;
}
___HIDDEN DWORD WINAPI ___device_stream_write_pump_proc
___P((LPVOID param),
(param)
LPVOID param;)
{
___device_stream *dev = ___CAST(___device_stream*,param);
___nonblocking_pipe *p = &dev->write_pump->pipe;
___SCMOBJ e;
___stream_index len;
___stream_index n;
___stream_index i;
___U8 buf[PIPE_BUFFER_SIZE];
___nonblocking_pipe_oob_msg oob_msg;
for (;;)
{
/* get from the pipe some bytes to write to the device */
while ((e = ___nonblocking_pipe_read
(p,
buf,
PIPE_BUFFER_SIZE,
&len,
&oob_msg))
== ___ERR_CODE_EAGAIN)
{
/* suspend thread until operation can be performed */
e = ___nonblocking_pipe_read_ready_wait (p);
if (e != ___FIX(___NO_ERR))
break;
}
if (e == ___FIX(___NO_ERR))
{
if (len > 0)
{
/* write to the device the bytes that were read from the pipe */
i = 0;
while (i < len)
{
e = ___device_stream_write_raw_virt (dev, buf+i, len-i, &n);
if (e != ___FIX(___NO_ERR))
break;
i += n;
}
}
else
{
switch (oob_msg.op)
{
case OOB_FORCE_OUTPUT0:
case OOB_FORCE_OUTPUT1:
case OOB_FORCE_OUTPUT2:
case OOB_FORCE_OUTPUT3:
#ifdef ___DEBUG
___printf ("***** got OOB_FORCE_OUTPUT%d\n",
oob_msg.op - OOB_FORCE_OUTPUT0);
#endif
e = ___device_stream_force_output_raw_virt (dev, oob_msg.op - OOB_FORCE_OUTPUT0);
break;
case OOB_SEEK_ABS:
case OOB_SEEK_REL:
case OOB_SEEK_REL_END:
#ifdef ___DEBUG
___printf ("***** got OOB_SEEK %d %d\n",
oob_msg.stream_index_param,
oob_msg.op - OOB_SEEK_ABS);
#endif
e = ___device_stream_seek_raw_virt
(dev,
&oob_msg.stream_index_param,
oob_msg.op - OOB_SEEK_ABS);
break;
case OOB_EOS:
#ifdef ___DEBUG
___printf ("***** got OOB_EOS\n");
#endif
break;
}
}
}
if (e != ___FIX(___NO_ERR))
{
if (e == ___FIX(___KILL_PUMP)) /* terminate? */
break;
/* report the failure back through the pipe */
e = ___nonblocking_pipe_set_reader_err (p, e);
if (e != ___FIX(___NO_ERR))
{
/*
* The failure could not be reported. To avoid an
* infinite loop the thread is terminated.
*/
ExitThread (0);
}
}
}
___device_release (&dev->base); /* ignore error */
return 0;
}
#endif
___SCMOBJ ___device_stream_setup
___P((___device_stream *dev,
___device_group *dgroup,
int direction,
int pumps_on),
(dev,
dgroup,
direction,
pumps_on)/*********************/
___device_stream *dev;
___device_group *dgroup;
int direction;
int pumps_on;)
{
dev->base.refcount = 1;
dev->base.direction = direction;
dev->base.close_direction = 0; /* prevent closing on errors */
dev->base.read_stage = ___STAGE_CLOSED;
dev->base.write_stage = ___STAGE_CLOSED;
#ifdef USE_PUMPS
dev->read_pump = NULL;
dev->write_pump = NULL;
#endif
___device_add_to_group (dgroup, &dev->base);
if (direction & ___DIRECTION_RD)
{
dev->base.read_stage = ___STAGE_OPEN;
#ifdef USE_PUMPS
if (pumps_on & ___DIRECTION_RD)
{
___SCMOBJ e;
device_add_ref (&dev->base);
if ((e = ___device_stream_pump_setup
(&dev->read_pump,
65536,
___device_stream_read_pump_proc,
dev))
!= ___FIX(___NO_ERR))
{
___device_release (&dev->base); /* ignore error */
___device_cleanup (&dev->base); /* ignore error */
return e;
}
}
#endif
}
if (direction & ___DIRECTION_WR)
{
dev->base.write_stage = ___STAGE_OPEN;
#ifdef USE_PUMPS
if (pumps_on & ___DIRECTION_WR)
{
___SCMOBJ e;
device_add_ref (&dev->base);
if ((e = ___device_stream_pump_setup
(&dev->write_pump,
65536,
___device_stream_write_pump_proc,
dev))
!= ___FIX(___NO_ERR))
{
___device_release (&dev->base); /* ignore error */
___device_cleanup (&dev->base); /* ignore error */
return e;
}
}
#endif
}
return ___FIX(___NO_ERR);
}
/*---------------------------------------------------------------------------*/
/* Serial stream device */
#ifdef USE_WIN32
typedef struct ___device_serial_struct
{
___device_stream base;
HANDLE h;
} ___device_serial;
typedef struct ___device_serial_vtbl_struct
{
___device_stream_vtbl base;
} ___device_serial_vtbl;
___HIDDEN int ___device_serial_kind
___P((___device *self),
(self)
___device *self;)
{
return ___SERIAL_DEVICE_KIND;
}
___HIDDEN ___SCMOBJ ___device_serial_close_raw_virt
___P((___device_stream *self,
int direction),
(self,
direction)
___device_stream *self;
int direction;)
{
___device_serial *d = ___CAST(___device_serial*,self);
int is_not_closed = 0;
if (d->base.base.read_stage != ___STAGE_CLOSED)
is_not_closed |= ___DIRECTION_RD;
if (d->base.base.write_stage != ___STAGE_CLOSED)
is_not_closed |= ___DIRECTION_WR;
if (is_not_closed == 0)
return ___FIX(___NO_ERR);
if (is_not_closed == (___DIRECTION_RD|___DIRECTION_WR))
{
d->base.base.read_stage = ___STAGE_CLOSED;
d->base.base.write_stage = ___STAGE_CLOSED;
if ((d->base.base.close_direction & (___DIRECTION_RD|___DIRECTION_WR))
== (___DIRECTION_RD|___DIRECTION_WR))
{
if (!CloseHandle (d->h))
return err_code_from_GetLastError ();
}
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_select_raw_virt
___P((___device_stream *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device_stream *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
___device_serial *d = ___CAST(___device_serial*,self);
int stage = (for_writing
? d->base.base.write_stage
: d->base.base.read_stage);
if (pass == ___SELECT_PASS_1)
{
if (stage != ___STAGE_OPEN)
state->timeout = ___time_mod.time_neg_infinity;
return ___FIX(___SELECT_SETUP_DONE);
}
/* pass == ___SELECT_PASS_CHECK */
if (stage != ___STAGE_OPEN)
state->devs[i] = NULL;
else
{
if (state->devs_next[i] != -1)
state->devs[i] = NULL;
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_release_raw_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_force_output_raw_virt
___P((___device_stream *self,
int level),
(self,
level)
___device_stream *self;
int level;)
{
___device_serial *d = ___CAST(___device_serial*,self);
if (d->base.base.write_stage == ___STAGE_OPEN)
{
if (level > 0)
{
if (!FlushFileBuffers (d->h))
return err_code_from_GetLastError ();
}
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_seek_raw_virt
___P((___device_stream *self,
___stream_index *pos,
int whence),
(self,
pos,
whence)
___device_stream *self;
___stream_index *pos;
int whence;)
{
return ___FIX(___INVALID_OP_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_read_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_serial *d = ___CAST(___device_serial*,self);
if (d->base.base.read_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
{
DWORD n;
if (!ReadFile (d->h, buf, len, &n, NULL))
return err_code_from_GetLastError ();
if (n == 0)
return ___ERR_CODE_EAGAIN;
*len_done = n;
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_write_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_serial *d = ___CAST(___device_serial*,self);
if (d->base.base.write_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
{
DWORD n;
if (!WriteFile (d->h, buf, len, &n, NULL))
{
/*
* Even though WriteFile has reported a failure, the operation
* was executed correctly (i.e. len_done contains the number
* of bytes written) if GetLastError returns ERROR_SUCCESS.
*/
if (GetLastError () != ERROR_SUCCESS)
return err_code_from_GetLastError ();
}
*len_done = n;
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_serial_width_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(80);
}
___HIDDEN ___SCMOBJ ___device_serial_default_options_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
int char_encoding_errors = ___CHAR_ENCODING_ERRORS_ON;
int char_encoding = ___CHAR_ENCODING_ISO_8859_1;
int eol_encoding = ___EOL_ENCODING_LF;
int buffering = ___FULL_BUFFERING;
return ___FIX(___STREAM_OPTIONS(char_encoding_errors,
char_encoding,
eol_encoding,
buffering,
char_encoding_errors,
char_encoding,
eol_encoding,
buffering));
}
___HIDDEN ___SCMOBJ ___device_serial_options_set_virt
___P((___device_stream *self,
___SCMOBJ options),
(self,
options)
___device_stream *self;
___SCMOBJ options;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___device_serial_vtbl ___device_serial_table =
{
{
{
___device_serial_kind,
___device_stream_select_virt,
___device_stream_release_virt,
___device_stream_force_output_virt,
___device_stream_close_virt
},
___device_serial_select_raw_virt,
___device_serial_release_raw_virt,
___device_serial_force_output_raw_virt,
___device_serial_close_raw_virt,
___device_serial_seek_raw_virt,
___device_serial_read_raw_virt,
___device_serial_write_raw_virt,
___device_serial_width_virt,
___device_serial_default_options_virt,
___device_serial_options_set_virt
}
};
___HIDDEN ___SCMOBJ ___device_serial_set_comm_state
___P((___device_serial *dev,
LPCTSTR def),
(dev,
def)
___device_serial *dev;
LPCTSTR def;)
{
DCB dcb;
FillMemory (&dcb, sizeof (dcb), 0);
if (!GetCommState (dev->h, &dcb) ||
!BuildCommDCB (def, &dcb) ||
!SetCommState (dev->h, &dcb))
return err_code_from_GetLastError ();
return ___FIX(___NO_ERR);
}
___SCMOBJ ___device_serial_setup_from_handle
___P((___device_serial **dev,
___device_group *dgroup,
HANDLE h,
int direction),
(dev,
dgroup,
h,
direction)
___device_serial **dev;
___device_group *dgroup;
HANDLE h;
int direction;)
{
___device_serial *d;
___SCMOBJ e;
COMMTIMEOUTS cto;
d = ___CAST(___device_serial*,
___alloc_mem (sizeof (___device_serial)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
d->base.base.vtbl = &___device_serial_table;
d->h = h;
*dev = d;
e = ___device_serial_set_comm_state (d, _T("baud=38400 parity=N data=8 stop=1"));
if (e != ___FIX(___NO_ERR))
{
___free_mem (d);
return e;
}
/*
* Setup serial device so that ReadFile will return as soon as a
* character is available.
*/
cto.ReadIntervalTimeout = MAXDWORD;
cto.ReadTotalTimeoutMultiplier = MAXDWORD;
cto.ReadTotalTimeoutConstant = 1; /* wait no more than 1 ms */
cto.WriteTotalTimeoutMultiplier = 0;
cto.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts (h, &cto)
#ifdef USE_BIG_SERIAL_BUFFERS
|| !SetupComm (h, 65536, 65536))
#endif
)
{
e = err_code_from_GetLastError ();
___free_mem (d);
return e;
}
return ___device_stream_setup
(&d->base,
dgroup,
direction,
___DIRECTION_RD|___DIRECTION_WR);
}
#endif
/*---------------------------------------------------------------------------*/
/* Pipe stream device */
/*
* Pipes may be unidirectional or bidirectional. Bidirectional pipes
* are implemented with 2 OS pipes: a "write" pipe and a "read" pipe.
*/
typedef struct ___device_pipe_struct
{
___device_stream base;
#ifdef USE_POSIX
int fd_wr; /* file descriptor for "write" pipe (-1 if none) */
int fd_rd; /* file descriptor for "read" pipe (-1 if none) */
#endif
#ifdef USE_WIN32
HANDLE h_wr; /* handle for "write" pipe (NULL if none) */
HANDLE h_rd; /* handle for "read" pipe (NULL if none) */
int poll_interval_nsecs; /* interval between read attempts */
#endif
} ___device_pipe;
typedef struct ___device_pipe_vtbl_struct
{
___device_stream_vtbl base;
} ___device_pipe_vtbl;
___HIDDEN int ___device_pipe_kind
___P((___device *self),
(self)
___device *self;)
{
return ___PIPE_DEVICE_KIND;
}
___SCMOBJ ___device_pipe_cleanup
___P((___device_pipe *dev),
(dev)
___device_pipe *dev;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_close_raw_virt
___P((___device_stream *self,
int direction),
(self,
direction)
___device_stream *self;
int direction;)
{
___device_pipe *d = ___CAST(___device_pipe*,self);
int is_not_closed = 0;
if (d->base.base.read_stage != ___STAGE_CLOSED)
is_not_closed |= ___DIRECTION_RD;
if (d->base.base.write_stage != ___STAGE_CLOSED)
is_not_closed |= ___DIRECTION_WR;
if (is_not_closed == 0)
return ___FIX(___NO_ERR);
if (is_not_closed & direction & ___DIRECTION_RD)
{
/* Close "read" pipe */
d->base.base.read_stage = ___STAGE_CLOSED;
if ((d->base.base.close_direction & ___DIRECTION_RD)
== ___DIRECTION_RD)
{
#ifdef USE_POSIX
if (d->fd_rd >= 0 &&
d->fd_rd != d->fd_wr &&
close_no_EINTR (d->fd_rd) < 0)
return err_code_from_errno ();
#endif
#ifdef USE_WIN32
if (d->h_rd != NULL &&
d->h_rd != d->h_wr)
CloseHandle (d->h_rd); /* ignore error */
#endif
}
}
if (is_not_closed & direction & ___DIRECTION_WR)
{
/* Close "write" pipe */
d->base.base.write_stage = ___STAGE_CLOSED;
if ((d->base.base.close_direction & ___DIRECTION_WR)
== ___DIRECTION_WR)
{
#ifdef USE_POSIX
if (d->fd_wr >= 0 &&
close_no_EINTR (d->fd_wr) < 0)
return err_code_from_errno ();
#endif
#ifdef USE_WIN32
if (d->h_wr != NULL)
CloseHandle (d->h_wr); /* ignore error */
#endif
}
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_select_raw_virt
___P((___device_stream *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device_stream *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
___device_pipe *d = ___CAST(___device_pipe*,self);
int stage = (for_writing
? d->base.base.write_stage
: d->base.base.read_stage);
if (pass == ___SELECT_PASS_1)
{
if (stage != ___STAGE_OPEN)
state->timeout = ___time_mod.time_neg_infinity;
else
{
#ifdef USE_POSIX
if (for_writing)
{
if (d->fd_wr >= 0)
___device_select_add_fd (state, d->fd_wr, 1);
}
else
{
if (d->fd_rd >= 0)
___device_select_add_fd (state, d->fd_rd, 0);
}
#endif
#ifdef USE_WIN32
if (for_writing)
{
if (d->h_wr != NULL)
___device_select_add_wait_obj (state, i, d->h_wr);
}
else
{
if (d->h_rd != NULL)
{
int interval = d->poll_interval_nsecs * 6 / 5;
if (interval < 1000000)
interval = 1000000; /* min interval = 0.001 secs */
else if (interval > 200000000)
interval = 200000000; /* max interval = 0.2 sec */
d->poll_interval_nsecs = interval;
___device_select_add_relative_timeout (state, i, interval * 1e-9);
}
}
#endif
}
return ___FIX(___SELECT_SETUP_DONE);
}
/* pass == ___SELECT_PASS_CHECK */
if (stage != ___STAGE_OPEN)
state->devs[i] = NULL;
else
{
#ifdef USE_POSIX
if (for_writing)
{
if (d->fd_wr < 0 || FD_ISSET(d->fd_wr, &state->writefds))
state->devs[i] = NULL;
}
else
{
if (d->fd_rd < 0 || FD_ISSET(d->fd_rd, &state->readfds))
state->devs[i] = NULL;
}
#endif
#ifdef USE_WIN32
if (for_writing)
{
if (d->h_wr != NULL && state->devs_next[i] != -1)
state->devs[i] = NULL;
}
else
{
if (d->h_rd != NULL)
state->devs[i] = NULL;
}
#endif
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_release_raw_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_force_output_raw_virt
___P((___device_stream *self,
int level),
(self,
level)
___device_stream *self;
int level;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_seek_raw_virt
___P((___device_stream *self,
___stream_index *pos,
int whence),
(self,
pos,
whence)
___device_stream *self;
___stream_index *pos;
int whence;)
{
return ___FIX(___INVALID_OP_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_read_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_pipe *d = ___CAST(___device_pipe*,self);
___SCMOBJ e = ___FIX(___NO_ERR);
if (d->base.base.read_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
#ifdef USE_POSIX
if (d->fd_rd < 0)
*len_done = 0;
else
{
int n = 0;
if ((n = read (d->fd_rd, buf, len)) < 0)
{
#if 0
if (errno == EIO) errno = EAGAIN;
#else
if (errno == EIO) /* on linux, treating EIO as EAGAIN gives an infinite loop */
n = 0;
else
#endif
e = err_code_from_errno ();
}
*len_done = n;
}
#endif
#ifdef USE_WIN32
if (d->h_rd == NULL)
*len_done = 0;
else
{
DWORD n = 0;
if (!PeekNamedPipe (d->h_rd, NULL, 0, NULL, &n, NULL))
e = err_code_from_GetLastError ();
else if (n == 0)
e = ___ERR_CODE_EAGAIN;
else
{
if (len > n)
len = n;
if (!ReadFile (d->h_rd, buf, len, &n, NULL))
e = err_code_from_GetLastError ();
else
d->poll_interval_nsecs = 0;
}
if (e == ___FIX(___WIN32_ERR(ERROR_BROKEN_PIPE)))
e = ___FIX(___NO_ERR); /* generate end-of-file on broken pipe */
*len_done = n;
}
#endif
return e;
}
___HIDDEN ___SCMOBJ ___device_pipe_write_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_pipe *d = ___CAST(___device_pipe*,self);
if (d->base.base.write_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
#ifdef USE_POSIX
if (d->fd_wr < 0)
*len_done = len;
else
{
int n;
if ((n = write (d->fd_wr, buf, len)) < 0)
return err_code_from_errno ();
*len_done = n;
}
#endif
#ifdef USE_WIN32
if (d->h_wr == NULL)
*len_done = len;
else
{
DWORD n;
if (!WriteFile (d->h_wr, buf, len, &n, NULL))
return err_code_from_GetLastError ();
*len_done = n;
}
#endif
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_pipe_width_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(80);
}
___HIDDEN ___SCMOBJ ___device_pipe_default_options_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
int char_encoding_errors = ___CHAR_ENCODING_ERRORS_ON;
int char_encoding = ___CHAR_ENCODING_ISO_8859_1;
int eol_encoding = ___EOL_ENCODING_LF;
int buffering = ___FULL_BUFFERING;
return ___FIX(___STREAM_OPTIONS(char_encoding_errors,
char_encoding,
eol_encoding,
buffering,
char_encoding_errors,
char_encoding,
eol_encoding,
buffering));
}
___HIDDEN ___SCMOBJ ___device_pipe_options_set_virt
___P((___device_stream *self,
___SCMOBJ options),
(self,
options)
___device_stream *self;
___SCMOBJ options;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___device_pipe_vtbl ___device_pipe_table =
{
{
{
___device_pipe_kind,
___device_stream_select_virt,
___device_stream_release_virt,
___device_stream_force_output_virt,
___device_stream_close_virt
},
___device_pipe_select_raw_virt,
___device_pipe_release_raw_virt,
___device_pipe_force_output_raw_virt,
___device_pipe_close_raw_virt,
___device_pipe_seek_raw_virt,
___device_pipe_read_raw_virt,
___device_pipe_write_raw_virt,
___device_pipe_width_virt,
___device_pipe_default_options_virt,
___device_pipe_options_set_virt
}
};
#ifdef USE_POSIX
___HIDDEN ___SCMOBJ ___device_pipe_setup_from_fd
___P((___device_pipe **dev,
___device_group *dgroup,
int fd_rd,
int fd_wr,
int direction),
(dev,
dgroup,
fd_rd,
fd_wr,
direction)
___device_pipe **dev;
___device_group *dgroup;
int fd_rd;
int fd_wr;
int direction;)
{
___device_pipe *d;
d = ___CAST(___device_pipe*,
___alloc_mem (sizeof (___device_pipe)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
d->base.base.vtbl = &___device_pipe_table;
d->fd_rd = fd_rd;
d->fd_wr = fd_wr;
*dev = d;
return ___device_stream_setup
(&d->base,
dgroup,
direction,
0);
}
#endif
#ifdef USE_WIN32
___HIDDEN ___SCMOBJ ___device_pipe_setup_from_handle
___P((___device_pipe **dev,
___device_group *dgroup,
HANDLE h_rd,
HANDLE h_wr,
int direction,
int pumps_on),
(dev,
dgroup,
h_rd,
h_wr,
direction,
pumps_on)
___device_pipe **dev;
___device_group *dgroup;
HANDLE h_rd;
HANDLE h_wr;
int direction;
int pumps_on;)
{
___device_pipe *d;
d = ___CAST(___device_pipe*,
___alloc_mem (sizeof (___device_pipe)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
d->base.base.vtbl = &___device_pipe_table;
d->h_rd = h_rd;
d->h_wr = h_wr;
d->poll_interval_nsecs = 0;
*dev = d;
return ___device_stream_setup
(&d->base,
dgroup,
direction,
pumps_on);
}
#endif
/*---------------------------------------------------------------------------*/
/* Process stream device */
typedef struct ___device_process_struct
{
___device_pipe base;
#ifdef USE_POSIX
pid_t pid; /* pid of the process */
#endif
#ifdef USE_WIN32
PROCESS_INFORMATION pi; /* process information */
#endif
int status; /* process status */
___BOOL got_status; /* was the status retrieved? */
___BOOL cleanuped; /* has process been cleaned-up? */
} ___device_process;
typedef struct ___device_process_vtbl_struct
{
___device_stream_vtbl base;
} ___device_process_vtbl;
___HIDDEN int ___device_process_kind
___P((___device *self),
(self)
___device *self;)
{
return ___PROCESS_DEVICE_KIND;
}
___SCMOBJ ___device_process_cleanup
___P((___device_process *dev),
(dev)
___device_process *dev;)
{
if (!dev->cleanuped)
{
dev->cleanuped = 1;
#ifdef USE_POSIX
#endif
#ifdef USE_WIN32
CloseHandle (dev->pi.hProcess); /* ignore error */
CloseHandle (dev->pi.hThread); /* ignore error */
#endif
}
return ___FIX(___NO_ERR);
}
___SCMOBJ ___device_process_status_set
___P((___device_process *dev,
int status),
(dev,
status)
___device_process *dev;
int status;)
{
___SCMOBJ e = ___FIX(___NO_ERR);
if (!dev->got_status)
{
dev->status = status;
dev->got_status = 1;
e = ___device_process_cleanup (dev); /* ignore error */
}
return e;
}
___SCMOBJ ___device_process_status_poll
___P((___device_process *dev),
(dev)
___device_process *dev;)
{
if (!dev->got_status)
{
#ifdef USE_POSIX
/*
* The process status is updated asynchronously by
* sigchld_signal_handler.
*/
#endif
#ifdef USE_WIN32
DWORD status;
if (!GetExitCodeProcess (dev->pi.hProcess, &status))
return err_code_from_GetLastError ();
if (status != STILL_ACTIVE)
___device_process_status_set (dev, status << 8); /* ignore error */
#endif
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_process_close_raw_virt
___P((___device_stream *self,
int direction),
(self,
direction)
___device_stream *self;
int direction;)
{
___device_process *d = ___CAST(___device_process*,self);
___SCMOBJ e = ___device_pipe_close_raw_virt (self, direction);
if (e == ___FIX(___NO_ERR))
{
if (d->base.base.base.read_stage == ___STAGE_CLOSED &&
d->base.base.base.write_stage == ___STAGE_CLOSED)
___device_process_status_poll (d); /* ignore error */
}
return e;
}
___HIDDEN ___SCMOBJ ___device_process_select_raw_virt
___P((___device_stream *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device_stream *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
return ___device_pipe_select_raw_virt (self, for_writing, i, pass, state);
}
___HIDDEN ___SCMOBJ ___device_process_release_raw_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
___device_process *d = ___CAST(___device_process*,self);
___SCMOBJ e1 = ___device_pipe_release_raw_virt (self);
___SCMOBJ e2 = ___device_process_cleanup (d);
if (e1 == ___FIX(___NO_ERR))
e1 = e2;
return e1;
}
___HIDDEN ___SCMOBJ ___device_process_force_output_raw_virt
___P((___device_stream *self,
int level),
(self,
level)
___device_stream *self;
int level;)
{
return ___device_pipe_force_output_raw_virt (self, level);
}
___HIDDEN ___SCMOBJ ___device_process_seek_raw_virt
___P((___device_stream *self,
___stream_index *pos,
int whence),
(self,
pos,
whence)
___device_stream *self;
___stream_index *pos;
int whence;)
{
return ___device_pipe_seek_raw_virt (self, pos, whence);
}
___HIDDEN ___SCMOBJ ___device_process_read_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
return ___device_pipe_read_raw_virt (self, buf, len, len_done);
}
___HIDDEN ___SCMOBJ ___device_process_write_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
return ___device_pipe_write_raw_virt (self, buf, len, len_done);
}
___HIDDEN ___SCMOBJ ___device_process_width_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(80);
}
___HIDDEN ___SCMOBJ ___device_process_default_options_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___device_pipe_default_options_virt (self);
}
___HIDDEN ___SCMOBJ ___device_process_options_set_virt
___P((___device_stream *self,
___SCMOBJ options),
(self,
options)
___device_stream *self;
___SCMOBJ options;)
{
return ___device_pipe_options_set_virt (self, options);
}
___HIDDEN ___device_process_vtbl ___device_process_table =
{
{
{
___device_process_kind,
___device_stream_select_virt,
___device_stream_release_virt,
___device_stream_force_output_virt,
___device_stream_close_virt
},
___device_process_select_raw_virt,
___device_process_release_raw_virt,
___device_process_force_output_raw_virt,
___device_process_close_raw_virt,
___device_process_seek_raw_virt,
___device_process_read_raw_virt,
___device_process_write_raw_virt,
___device_process_width_virt,
___device_process_default_options_virt,
___device_process_options_set_virt
}
};
#ifdef USE_POSIX
___SCMOBJ ___device_process_setup_from_pid
___P((___device_process **dev,
___device_group *dgroup,
pid_t pid,
int fd_stdin,
int fd_stdout,
int direction),
(dev,
dgroup,
pid,
fd_stdin,
fd_stdout,
direction)
___device_process **dev;
___device_group *dgroup;
pid_t pid;
int fd_stdin;
int fd_stdout;
int direction;)
{
___device_process *d;
d = ___CAST(___device_process*,
___alloc_mem (sizeof (___device_process)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
/*
* Setup file descriptors to perform nonblocking I/O.
*/
if ((fd_stdout >= 0 &&
(direction & ___DIRECTION_RD) &&
(set_fd_blocking_mode (fd_stdout, 0) < 0)) ||
(fd_stdin >= 0 &&
(direction & ___DIRECTION_WR) &&
(set_fd_blocking_mode (fd_stdin, 0) < 0)))
{
___SCMOBJ e = err_code_from_errno ();
___free_mem (d);
return e;
}
d->base.base.base.vtbl = &___device_process_table;
d->base.fd_rd = fd_stdout;
d->base.fd_wr = fd_stdin;
d->pid = pid;
d->status = -1;
d->got_status = 0;
d->cleanuped = 0;
*dev = d;
return ___device_stream_setup
(&d->base.base,
dgroup,
direction,
0);
}
#endif
#ifdef USE_WIN32
___SCMOBJ ___device_process_setup_from_process
___P((___device_process **dev,
___device_group *dgroup,
PROCESS_INFORMATION pi,
HANDLE hstdin,
HANDLE hstdout,
int direction),
(dev,
dgroup,
pi,
hstdin,
hstdout,
direction)
___device_process **dev;
___device_group *dgroup;
PROCESS_INFORMATION pi;
HANDLE hstdin;
HANDLE hstdout;
int direction;)
{
___device_process *d;
d = ___CAST(___device_process*,
___alloc_mem (sizeof (___device_process)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
d->base.base.base.vtbl = &___device_process_table;
d->base.h_rd = hstdout;
d->base.h_wr = hstdin;
d->pi = pi;
d->status = -1;
d->got_status = 0;
d->cleanuped = 0;
*dev = d;
return ___device_stream_setup
(&d->base.base,
dgroup,
direction,
0);
}
#endif
/*---------------------------------------------------------------------------*/
#ifdef USE_NETWORKING
/* Socket utilities */
#ifdef USE_POSIX
#define SOCKET_TYPE int
#define SOCKET_CALL_ERROR(s) ((s) < 0)
#define SOCKET_CALL_ERROR2(s) ((s) < 0)
#define CONNECT_IN_PROGRESS (errno == EINPROGRESS)
#define CONNECT_WOULD_BLOCK (errno == EAGAIN)
#define NOT_CONNECTED(e) ((e) == ___FIX(___ERRNO_ERR(ENOTCONN)))
#define CLOSE_SOCKET(s) close_no_EINTR (s)
#define ERR_CODE_FROM_SOCKET_CALL err_code_from_errno ()
#define IOCTL_SOCKET(s,cmd,argp) ioctl (s,cmd,argp)
#define SOCKET_LEN_TYPE socklen_t
#endif
#ifdef USE_WIN32
#define SOCKET_TYPE SOCKET
#define SOCKET_CALL_ERROR(s) ((s) == SOCKET_ERROR)
#define SOCKET_CALL_ERROR2(s) ((s) == INVALID_SOCKET)
#define CONNECT_IN_PROGRESS ((WSAGetLastError () == WSAEALREADY) || \
(WSAGetLastError () == WSAEISCONN))
#define CONNECT_WOULD_BLOCK ((WSAGetLastError () == WSAEWOULDBLOCK) || \
(WSAGetLastError () == WSAEINVAL))
#define NOT_CONNECTED(e) ((e) == ___FIX(___WIN32_ERR(WSAENOTCONN)))
#define CLOSE_SOCKET(s) closesocket (s)
#define ERR_CODE_FROM_SOCKET_CALL err_code_from_WSAGetLastError ()
#define IOCTL_SOCKET(s,cmd,argp) ioctlsocket (s,cmd,argp)
#define SOCKET_LEN_TYPE int
#endif
#ifdef SHUT_RD
#define SHUTDOWN_RD SHUT_RD
#else
#ifdef SD_RECEIVE
#define SHUTDOWN_RD SD_RECEIVE
#else
#define SHUTDOWN_RD 0
#endif
#endif
#ifdef SHUT_WR
#define SHUTDOWN_WR SHUT_WR
#else
#ifdef SD_SEND
#define SHUTDOWN_WR SD_SEND
#else
#define SHUTDOWN_WR 1
#endif
#endif
#endif
/*---------------------------------------------------------------------------*/
#ifdef USE_NETWORKING
/* TCP client stream device */
typedef struct ___device_tcp_client_struct
{
___device_stream base;
SOCKET_TYPE s;
struct sockaddr server_addr;
SOCKET_LEN_TYPE server_addrlen;
int try_connect_again;
int connect_done;
#ifdef USE_POSIX
int try_connect_interval_nsecs;
#endif
#ifdef USE_WIN32
long io_events; /* used by ___device_tcp_client_select_raw_virt */
HANDLE io_event; /* used by ___device_tcp_client_select_raw_virt */
#endif
} ___device_tcp_client;
typedef struct ___device_tcp_client_vtbl_struct
{
___device_stream_vtbl base;
} ___device_tcp_client_vtbl;
___HIDDEN int try_connect
___P((___device_tcp_client *dev),
(dev)
___device_tcp_client *dev;)
{
if (!SOCKET_CALL_ERROR(connect (dev->s,
&dev->server_addr,
dev->server_addrlen)) ||
CONNECT_IN_PROGRESS || /* establishing connection in background */
dev->try_connect_again == 2) /* last connect attempt? */
{
dev->try_connect_again = 0; /* we're done waiting */
return 0;
}
if (CONNECT_WOULD_BLOCK) /* connect can't be performed now */
return 0;
return -1;
}
___HIDDEN int ___device_tcp_client_kind
___P((___device *self),
(self)
___device *self;)
{
return ___TCP_CLIENT_DEVICE_KIND;
}
___HIDDEN ___SCMOBJ ___device_tcp_client_close_raw_virt
___P((___device_stream *self,
int direction),
(self,
direction)
___device_stream *self;
int direction;)
{
___device_tcp_client *d = ___CAST(___device_tcp_client*,self);
int is_not_closed = 0;
if (d->base.base.read_stage != ___STAGE_CLOSED)
is_not_closed |= ___DIRECTION_RD;
if (d->base.base.write_stage != ___STAGE_CLOSED)
is_not_closed |= ___DIRECTION_WR;
if (is_not_closed == 0)
return ___FIX(___NO_ERR);
if ((is_not_closed & ~direction) == 0)
{
/* Close socket when both sides are closed. */
d->base.base.read_stage = ___STAGE_CLOSED; /* avoid multiple closes */
d->base.base.write_stage = ___STAGE_CLOSED;
#ifdef USE_WIN32
if (d->io_event != NULL)
CloseHandle (d->io_event); /* ignore error */
#endif
if ((d->base.base.close_direction & (___DIRECTION_RD|___DIRECTION_WR))
== (___DIRECTION_RD|___DIRECTION_WR))
{
if (CLOSE_SOCKET(d->s) != 0)
return ERR_CODE_FROM_SOCKET_CALL;
}
}
else if (is_not_closed & direction & ___DIRECTION_RD)
{
/* Shutdown receiving side. */
if ((d->base.base.close_direction & ___DIRECTION_RD)
== ___DIRECTION_RD)
{
if (shutdown (d->s, SHUTDOWN_RD) != 0)
{
___SCMOBJ e = ERR_CODE_FROM_SOCKET_CALL;
if (!NOT_CONNECTED(e))
return e;
}
}
d->base.base.read_stage = ___STAGE_CLOSED;
}
else if (is_not_closed & direction & ___DIRECTION_WR)
{
/* Shutdown sending side. */
if ((d->base.base.close_direction & ___DIRECTION_WR)
== ___DIRECTION_WR)
{
if (shutdown (d->s, SHUTDOWN_WR) != 0)
{
___SCMOBJ e = ERR_CODE_FROM_SOCKET_CALL;
if (!NOT_CONNECTED(e))
return e;
}
}
d->base.base.write_stage = ___STAGE_CLOSED;
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_select_raw_virt
___P((___device_stream *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device_stream *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
___device_tcp_client *d = ___CAST(___device_tcp_client*,self);
int stage = (for_writing
? d->base.base.write_stage
: d->base.base.read_stage);
if (pass == ___SELECT_PASS_1)
{
if (stage != ___STAGE_OPEN)
{
state->timeout = ___time_mod.time_neg_infinity;
return ___FIX(___SELECT_SETUP_DONE);
}
else
{
#ifdef USE_POSIX
if (d->try_connect_again != 0)
{
int interval = d->try_connect_interval_nsecs * 6 / 5;
if (interval > 200000000) /* max interval = 0.2 sec */
interval = 200000000;
d->try_connect_interval_nsecs = interval;
___device_select_add_relative_timeout (state, i, interval * 1e-9);
}
else
___device_select_add_fd (state, d->s, for_writing);
return ___FIX(___SELECT_SETUP_DONE);
#endif
#ifdef USE_WIN32
d->io_events = 0;
return ___FIX(___NO_ERR);
#endif
}
}
#ifdef USE_WIN32
else if (pass == ___SELECT_PASS_2)
{
if (d->try_connect_again != 0)
d->io_events = (FD_CONNECT | FD_CLOSE);
else if (for_writing)
d->io_events |= (FD_WRITE | FD_CLOSE);
else
d->io_events |= (FD_READ | FD_CLOSE);
return ___FIX(___NO_ERR);
}
else if (pass == ___SELECT_PASS_3)
{
HANDLE wait_obj = d->io_event;
ResetEvent (wait_obj); /* ignore error */
WSAEventSelect (d->s, wait_obj, d->io_events);
___device_select_add_wait_obj (state, i, wait_obj);
return ___FIX(___SELECT_SETUP_DONE);
}
#endif
/* pass == ___SELECT_PASS_CHECK */
if (stage != ___STAGE_OPEN)
state->devs[i] = NULL;
else
{
#ifdef USE_POSIX
if (d->try_connect_again != 0 ||
(for_writing
? FD_ISSET(d->s, &state->writefds)
: FD_ISSET(d->s, &state->readfds)))
{
d->connect_done = 1;
state->devs[i] = NULL;
}
#endif
#ifdef USE_WIN32
if (state->devs_next[i] != -1)
{
state->devs[i] = NULL;
if (d->try_connect_again != 0)
{
d->connect_done = 1;
d->try_connect_again = 2;
}
}
#endif
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_release_raw_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_force_output_raw_virt
___P((___device_stream *self,
int level),
(self,
level)
___device_stream *self;
int level;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_seek_raw_virt
___P((___device_stream *self,
___stream_index *pos,
int whence),
(self,
pos,
whence)
___device_stream *self;
___stream_index *pos;
int whence;)
{
return ___FIX(___INVALID_OP_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_read_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_tcp_client *d = ___CAST(___device_tcp_client*,self);
int n;
if (d->base.base.read_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
if (d->try_connect_again != 0)
{
if (try_connect (d) == 0)
{
if (d->try_connect_again != 0)
return ___ERR_CODE_EAGAIN;
}
else
return ERR_CODE_FROM_SOCKET_CALL;
}
if (SOCKET_CALL_ERROR(n = recv (d->s, ___CAST(char*,buf), len, 0)))
{
___SCMOBJ e = ERR_CODE_FROM_SOCKET_CALL;
if (NOT_CONNECTED(e) && !d->connect_done)
e = ___ERR_CODE_EAGAIN;
return e;
}
*len_done = n;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_write_raw_virt
___P((___device_stream *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_stream *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_tcp_client *d = ___CAST(___device_tcp_client*,self);
int n;
if (d->base.base.write_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
if (d->try_connect_again != 0)
{
if (try_connect (d) == 0)
{
if (d->try_connect_again != 0)
return ___ERR_CODE_EAGAIN;
}
else
return ERR_CODE_FROM_SOCKET_CALL;
}
if (SOCKET_CALL_ERROR(n = send (d->s, ___CAST(char*,buf), len, 0)))
{
___SCMOBJ e = ERR_CODE_FROM_SOCKET_CALL;
if (NOT_CONNECTED(e) && !d->connect_done)
e = ___ERR_CODE_EAGAIN;
return e;
}
*len_done = n;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_width_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
return ___FIX(80);
}
___HIDDEN ___SCMOBJ ___device_tcp_client_default_options_virt
___P((___device_stream *self),
(self)
___device_stream *self;)
{
int char_encoding_errors = ___CHAR_ENCODING_ERRORS_ON;
int char_encoding = ___CHAR_ENCODING_ISO_8859_1;
int eol_encoding = ___EOL_ENCODING_LF;
int buffering = ___FULL_BUFFERING;
return ___FIX(___STREAM_OPTIONS(char_encoding_errors,
char_encoding,
eol_encoding,
buffering,
char_encoding_errors,
char_encoding,
eol_encoding,
buffering));
}
___HIDDEN ___SCMOBJ ___device_tcp_client_options_set_virt
___P((___device_stream *self,
___SCMOBJ options),
(self,
options)
___device_stream *self;
___SCMOBJ options;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___device_tcp_client_vtbl ___device_tcp_client_table =
{
{
{
___device_tcp_client_kind,
___device_stream_select_virt,
___device_stream_release_virt,
___device_stream_force_output_virt,
___device_stream_close_virt
},
___device_tcp_client_select_raw_virt,
___device_tcp_client_release_raw_virt,
___device_tcp_client_force_output_raw_virt,
___device_tcp_client_close_raw_virt,
___device_tcp_client_seek_raw_virt,
___device_tcp_client_read_raw_virt,
___device_tcp_client_write_raw_virt,
___device_tcp_client_width_virt,
___device_tcp_client_default_options_virt,
___device_tcp_client_options_set_virt
}
};
#define ___SOCK_KEEPALIVE_FLAG(options) (((options) & 1) != 0)
#define ___SOCK_NO_COALESCE_FLAG(options) (((options) & 2) != 0)
#define ___SOCK_REUSE_ADDRESS_FLAG(options) (((options) & 2048) != 0)
___HIDDEN ___SCMOBJ create_tcp_socket
___P((SOCKET_TYPE *sock,
int options),
(sock,
options)
SOCKET_TYPE *sock;
int options;)
{
int keepalive_flag = ___SOCK_KEEPALIVE_FLAG(options);
int no_coalesce_flag = ___SOCK_NO_COALESCE_FLAG(options);
int reuse_address_flag = ___SOCK_REUSE_ADDRESS_FLAG(options);
SOCKET_TYPE s;
if (SOCKET_CALL_ERROR2(s = socket (AF_INET, SOCK_STREAM, 0)))
return ERR_CODE_FROM_SOCKET_CALL;
#ifndef TCP_NODELAY
#define TCP_NODELAY 1
#endif
if ((keepalive_flag != 0 &&
setsockopt (s, /* keep connection alive or not */
SOL_SOCKET,
SO_KEEPALIVE,
___CAST(char*,&keepalive_flag),
sizeof (keepalive_flag)) != 0) ||
(reuse_address_flag != 0 &&
setsockopt (s, /* allow reusing the same address */
SOL_SOCKET,
SO_REUSEADDR,
___CAST(char*,&reuse_address_flag),
sizeof (reuse_address_flag)) != 0) ||
(no_coalesce_flag != 0 &&
setsockopt (s, /* enable or disable packet coalescing algorithm */
IPPROTO_TCP,
TCP_NODELAY,
___CAST(char*,&no_coalesce_flag),
sizeof (no_coalesce_flag)) != 0))
{
___SCMOBJ e = ERR_CODE_FROM_SOCKET_CALL;
CLOSE_SOCKET(s); /* ignore error */
return e;
}
*sock = s;
return ___FIX(___NO_ERR);
}
___HIDDEN int set_socket_non_blocking
___P((SOCKET_TYPE s),
(s)
SOCKET_TYPE s;)
{
#ifndef USE_ioctl
#undef FIONBIO
#endif
#ifdef FIONBIO
unsigned long param = 1;
return SOCKET_CALL_ERROR(IOCTL_SOCKET(s, FIONBIO, &param));
#else
return set_fd_blocking_mode (s, 0);
#endif
}
___SCMOBJ ___device_tcp_client_setup_from_socket
___P((___device_tcp_client **dev,
___device_group *dgroup,
SOCKET_TYPE s,
struct sockaddr *server_addr,
SOCKET_LEN_TYPE server_addrlen,
int try_connect_again,
int direction),
(dev,
dgroup,
s,
server_addr,
server_addrlen,
try_connect_again,
direction)
___device_tcp_client **dev;
___device_group *dgroup;
SOCKET_TYPE s;
struct sockaddr *server_addr;
SOCKET_LEN_TYPE server_addrlen;
int try_connect_again;
int direction;)
{
___SCMOBJ e;
___device_tcp_client *d;
d = ___CAST(___device_tcp_client*,
___alloc_mem (sizeof (___device_tcp_client)));
if (d == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
/*
* Setup socket to perform nonblocking I/O.
*/
if (set_socket_non_blocking (s) != 0) /* set nonblocking mode */
{
e = ERR_CODE_FROM_SOCKET_CALL;
___free_mem (d);
return e;
}
d->base.base.vtbl = &___device_tcp_client_table;
d->s = s;
d->server_addr = *server_addr;
d->server_addrlen = server_addrlen;
d->try_connect_again = try_connect_again;
d->connect_done = 0;
#ifdef USE_POSIX
d->try_connect_interval_nsecs = 1000000; /* 0.001 secs */
#endif
#ifdef USE_WIN32
d->io_event =
CreateEvent (NULL, /* can't inherit */
TRUE, /* manual reset */
FALSE, /* not signaled */
NULL); /* no name */
if (d->io_event == NULL)
{
e = err_code_from_GetLastError ();
___free_mem (d);
return e;
}
#endif
*dev = d;
return ___device_stream_setup
(&d->base,
dgroup,
direction,
0);
}
___SCMOBJ ___device_tcp_client_setup_from_sockaddr
___P((___device_tcp_client **dev,
___device_group *dgroup,
struct sockaddr *server_addr,
SOCKET_LEN_TYPE server_addrlen,
int options,
int direction),
(dev,
dgroup,
server_addr,
server_addrlen,
options,
direction)
___device_tcp_client **dev;
___device_group *dgroup;
struct sockaddr *server_addr;
SOCKET_LEN_TYPE server_addrlen;
int options;
int direction;)
{
___SCMOBJ e;
SOCKET_TYPE s;
___device_tcp_client *d;
if ((e = create_tcp_socket (&s, options)) != ___FIX(___NO_ERR))
return e;
if ((e = ___device_tcp_client_setup_from_socket
(&d,
dgroup,
s,
server_addr,
server_addrlen,
1,
direction))
!= ___FIX(___NO_ERR))
{
CLOSE_SOCKET(s); /* ignore error */
return e;
}
device_transfer_close_responsibility (___CAST(___device*,d));
*dev = d;
if (try_connect (d) != 0)
{
e = ERR_CODE_FROM_SOCKET_CALL;
___device_cleanup (&d->base.base); /* ignore error */
return e;
}
return ___FIX(___NO_ERR);
}
#endif
/*---------------------------------------------------------------------------*/
#ifdef USE_NETWORKING
/* TCP server device. */
typedef struct ___device_tcp_server_struct
{
___device base;
SOCKET_TYPE s;
#ifdef USE_WIN32
HANDLE io_event; /* used by ___device_tcp_server_select_raw_virt */
#endif
} ___device_tcp_server;
typedef struct ___device_tcp_server_vtbl_struct
{
___device_vtbl base;
} ___device_tcp_server_vtbl;
___HIDDEN int ___device_tcp_server_kind
___P((___device *self),
(self)
___device *self;)
{
return ___TCP_SERVER_DEVICE_KIND;
}
___HIDDEN ___SCMOBJ ___device_tcp_server_close_virt
___P((___device *self,
int direction),
(self,
direction)
___device *self;
int direction;)
{
___device_tcp_server *d = ___CAST(___device_tcp_server*,self);
if (d->base.read_stage == ___STAGE_CLOSED)
return ___FIX(___NO_ERR);
if (direction & ___DIRECTION_RD)
{
d->base.read_stage = ___STAGE_CLOSED; /* avoid multiple closes */
#ifdef USE_WIN32
if (d->io_event != NULL)
CloseHandle (d->io_event); /* ignore error */
#endif
if ((d->base.close_direction & ___DIRECTION_RD)
== ___DIRECTION_RD)
{
if (CLOSE_SOCKET(d->s) != 0)
return ERR_CODE_FROM_SOCKET_CALL;
}
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_server_select_virt
___P((___device *self,
___BOOL for_writing,
int i,
int pass,
___device_select_state *state),
(self,
for_writing,
i,
pass,
state)
___device *self;
___BOOL for_writing;
int i;
int pass;
___device_select_state *state;)
{
___device_tcp_server *d = ___CAST(___device_tcp_server*,self);
int stage = (for_writing
? d->base.write_stage
: d->base.read_stage);
if (pass == ___SELECT_PASS_1)
{
if (stage != ___STAGE_OPEN)
state->timeout = ___time_mod.time_neg_infinity;
else
{
#ifdef USE_POSIX
___device_select_add_fd (state, d->s, for_writing);
#endif
#ifdef USE_WIN32
HANDLE wait_obj = d->io_event;
ResetEvent (wait_obj); /* ignore error */
WSAEventSelect (d->s, wait_obj, FD_ACCEPT);
___device_select_add_wait_obj (state, i, wait_obj);
#endif
}
return ___FIX(___SELECT_SETUP_DONE);
}
/* pass == ___SELECT_PASS_CHECK */
if (stage != ___STAGE_OPEN)
state->devs[i] = NULL;
else
{
#ifdef USE_POSIX
if (FD_ISSET(d->s, &state->readfds))
state->devs[i] = NULL;
#endif
#ifdef USE_WIN32
if (state->devs_next[i] != -1)
state->devs[i] = NULL;
#endif
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_server_release_virt
___P((___device *self),
(self)
___device *self;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tcp_server_force_output_virt
___P((___device *self,
int level),
(self,
level)
___device *self;
int level;)
{
return ___FIX(___NO_ERR);
}
___HIDDEN ___device_tcp_server_vtbl ___device_tcp_server_table =
{
{
___device_tcp_server_kind,
___device_tcp_server_select_virt,
___device_tcp_server_release_virt,
___device_tcp_server_force_output_virt,
___device_tcp_server_close_virt
}
};
___SCMOBJ ___device_tcp_server_setup
___P((___device_tcp_server **dev,
___device_group *dgroup,
struct sockaddr *server_addr,
SOCKET_LEN_TYPE server_addrlen,
int backlog,
int options),