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); | ||
|  | } |