291 lines
6.4 KiB
C
291 lines
6.4 KiB
C
|
/* $Id$
|
||
|
|
||
|
Part of SWI-Prolog
|
||
|
|
||
|
Author: Jan Wielemaker
|
||
|
E-mail: jan@swi.psy.uva.nl
|
||
|
WWW: http://www.swi-prolog.org
|
||
|
Copyright (C): 1985-2004, University of Amsterdam
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser General Public
|
||
|
License along with this library; if not, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
|
||
|
As a special exception, if you link this library with other files,
|
||
|
compiled with a Free Software compiler, to produce an executable, this
|
||
|
library does not by itself cause the resulting executable to be covered
|
||
|
by the GNU General Public License. This exception does not however
|
||
|
invalidate any other reasons why the executable file might be covered by
|
||
|
the GNU General Public License.
|
||
|
*/
|
||
|
|
||
|
#include <SWI-Stream.h>
|
||
|
#include <SWI-Prolog.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#ifdef __WINDOWS__
|
||
|
#include <windows.h>
|
||
|
|
||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||
|
This file provides a windows alternative to an anonymous pipe that can
|
||
|
be waited for in the same pool as sockets using by tcp_select/3. It is a
|
||
|
work-around that allows a socket-based server using tcp_select/3 for IO
|
||
|
multiplexing to create an additional communication channal for
|
||
|
controlling the server.
|
||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#define PIPE_MAGIC 0x6b3a914cL
|
||
|
#define ISPIPE(p) if ( p->magic != PIPE_MAGIC ) \
|
||
|
{ errno = EINVAL; \
|
||
|
return -1; \
|
||
|
}
|
||
|
|
||
|
typedef struct pipe
|
||
|
{ long magic; /* magic code */
|
||
|
int size; /* size of the pipe */
|
||
|
char *buf; /* Buffer */
|
||
|
int in;
|
||
|
int out;
|
||
|
int writers; /* write open count */
|
||
|
int readers; /* read open count */
|
||
|
int blocking; /* pipe is blocking */
|
||
|
|
||
|
CRITICAL_SECTION mutex;
|
||
|
HANDLE event; /* signal if data is available */
|
||
|
} pipe;
|
||
|
|
||
|
|
||
|
static pipe *
|
||
|
create_pipe(int size, int blocking)
|
||
|
{ pipe *p = PL_malloc(sizeof(*p));
|
||
|
|
||
|
p->buf = PL_malloc(size);
|
||
|
p->size = size;
|
||
|
p->in = p->out = 0;
|
||
|
p->readers = p->writers = 0;
|
||
|
p->blocking = blocking;
|
||
|
|
||
|
InitializeCriticalSection(&p->mutex);
|
||
|
p->event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
p->magic = PIPE_MAGIC;
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
destroy_pipe(pipe *p)
|
||
|
{ ISPIPE(p);
|
||
|
|
||
|
EnterCriticalSection(&p->mutex);
|
||
|
|
||
|
PL_free(p->buf);
|
||
|
CloseHandle(p->event);
|
||
|
LeaveCriticalSection(&p->mutex);
|
||
|
DeleteCriticalSection(&p->mutex);
|
||
|
p->magic = 0;
|
||
|
PL_free(p);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
write_pipe(pipe *pipe, char *buf, int size)
|
||
|
{ ISPIPE(pipe);
|
||
|
|
||
|
EnterCriticalSection(&pipe->mutex);
|
||
|
|
||
|
for(;;)
|
||
|
{ if ( pipe->in + size <= pipe->size )
|
||
|
{ memcpy(pipe->buf+pipe->in, buf, size);
|
||
|
pipe->in += size;
|
||
|
|
||
|
SetEvent(pipe->event);
|
||
|
LeaveCriticalSection(&pipe->mutex);
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
if ( pipe->out > 0 )
|
||
|
{ memmove(pipe->buf, pipe->buf+pipe->out, pipe->in - pipe->out);
|
||
|
pipe->in -= pipe->out;
|
||
|
pipe->out = 0;
|
||
|
}
|
||
|
|
||
|
if ( size > pipe->size )
|
||
|
size = pipe->size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||
|
Non-blocking read from our pipe.
|
||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
static int
|
||
|
read_pipe(pipe *pipe, char *buf, int size)
|
||
|
{ int avail;
|
||
|
|
||
|
ISPIPE(pipe);
|
||
|
|
||
|
retry:
|
||
|
EnterCriticalSection(&pipe->mutex);
|
||
|
avail = pipe->in - pipe->out;
|
||
|
|
||
|
if ( avail > 0 )
|
||
|
{ if ( size < avail )
|
||
|
avail = size;
|
||
|
|
||
|
memcpy(buf, pipe->buf+pipe->out, avail);
|
||
|
pipe->out += avail;
|
||
|
if ( pipe->in == pipe->out )
|
||
|
pipe->in = pipe->out = 0;
|
||
|
|
||
|
LeaveCriticalSection(&pipe->mutex);
|
||
|
return avail;
|
||
|
}
|
||
|
|
||
|
if ( pipe->writers == 0 )
|
||
|
{ LeaveCriticalSection(&pipe->mutex);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ( pipe->blocking )
|
||
|
{ int rc;
|
||
|
|
||
|
LeaveCriticalSection(&pipe->mutex);
|
||
|
rc = MsgWaitForMultipleObjects(1,
|
||
|
&pipe->event,
|
||
|
FALSE, /* wait for either event */
|
||
|
INFINITE,
|
||
|
QS_ALLINPUT);
|
||
|
if ( rc == WAIT_OBJECT_0+1 ) /* message arrived */
|
||
|
{ MSG msg;
|
||
|
|
||
|
while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
|
||
|
{ TranslateMessage(&msg);
|
||
|
DispatchMessage(&msg);
|
||
|
if ( PL_handle_signals() < 0 )
|
||
|
{ errno = EINTR; /* exception */
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
goto retry;
|
||
|
}
|
||
|
|
||
|
errno = EWOULDBLOCK;
|
||
|
LeaveCriticalSection(&pipe->mutex);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
reader_close_pipe(pipe *p)
|
||
|
{ ISPIPE(p);
|
||
|
p->readers--;
|
||
|
|
||
|
if ( p->readers <= 0 && p->writers <= 0 )
|
||
|
return destroy_pipe(p);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
writer_close_pipe(pipe *p)
|
||
|
{ ISPIPE(p);
|
||
|
p->writers--;
|
||
|
|
||
|
if ( p->readers <= 0 && p->writers <= 0 )
|
||
|
return destroy_pipe(p);
|
||
|
if ( p->writers <= 0 )
|
||
|
{ SetEvent(p->event); /* Signal EOF */
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static IOFUNCTIONS pipe_read_functions =
|
||
|
{ (Sread_function) read_pipe,
|
||
|
(Swrite_function) write_pipe,
|
||
|
(Sseek_function) 0,
|
||
|
(Sclose_function) reader_close_pipe,
|
||
|
(Scontrol_function) 0
|
||
|
};
|
||
|
|
||
|
|
||
|
static IOFUNCTIONS pipe_write_functions =
|
||
|
{ (Sread_function) read_pipe,
|
||
|
(Swrite_function) write_pipe,
|
||
|
(Sseek_function) 0,
|
||
|
(Sclose_function) writer_close_pipe,
|
||
|
(Scontrol_function) 0
|
||
|
};
|
||
|
|
||
|
|
||
|
static foreign_t
|
||
|
tcp_pipe(term_t in, term_t out)
|
||
|
{ pipe *p = create_pipe(4096, TRUE);
|
||
|
IOSTREAM *sin, *sout;
|
||
|
|
||
|
sin = Snew(p, SIO_FBUF|SIO_INPUT|SIO_RECORDPOS, &pipe_read_functions);
|
||
|
p->readers++;
|
||
|
sout = Snew(p, SIO_FBUF|SIO_OUTPUT|SIO_RECORDPOS, &pipe_write_functions);
|
||
|
p->writers++;
|
||
|
|
||
|
if ( !PL_unify_stream(in, sin) ||
|
||
|
!PL_unify_stream(out, sout) )
|
||
|
{ Sclose(sin);
|
||
|
Sclose(sout);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#else /*__WINDOWS__*/
|
||
|
|
||
|
static foreign_t
|
||
|
tcp_pipe(term_t Read, term_t Write)
|
||
|
{ int fd[2];
|
||
|
IOSTREAM *in, *out;
|
||
|
|
||
|
if ( pipe(fd) != 0 )
|
||
|
return pl_error("pipe", 2, NULL, ERR_ERRNO, errno, "create", "pipe", 0);
|
||
|
|
||
|
in = Sfdopen(fd[0], "r");
|
||
|
out = Sfdopen(fd[1], "w");
|
||
|
|
||
|
if ( PL_unify_stream(Read, in) &&
|
||
|
PL_unify_stream(Write, out) )
|
||
|
return TRUE;
|
||
|
|
||
|
Sclose(in);
|
||
|
Sclose(out);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#endif /*__WINDOWS__*/
|
||
|
|
||
|
install_t
|
||
|
install_winpipe()
|
||
|
{ PL_register_foreign("tcp_pipe", 2, tcp_pipe, 0);
|
||
|
}
|