Mercurial > hg > xemacs-beta
diff src/event-msw.c @ 276:6330739388db r21-0b36
Import from CVS: tag r21-0b36
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:30:37 +0200 |
parents | ca9a9ec9c1c1 |
children | 90d73dddcdc4 |
line wrap: on
line diff
--- a/src/event-msw.c Mon Aug 13 10:29:43 2007 +0200 +++ b/src/event-msw.c Mon Aug 13 10:30:37 2007 +0200 @@ -54,7 +54,7 @@ #include "systime.h" #include "events-mod.h" - +#include <io.h> #include <errno.h> #ifdef BROKEN_CYGWIN @@ -141,9 +141,19 @@ #define NTPIPE_SLURP_STREAM_DATA(stream) \ LSTREAM_TYPE_DATA (stream, ntpipe_slurp) -struct ntpipe_slurp_stream +/* This structure is allocated by the main thread, and is deallocated + in the thread upon exit. There are situations when a thread + remains blocked for a long time, much longer than the lstream + exists. For exmaple, "start notepad" command is issued from the + shell, then the shell is closed by C-c C-d. Although the shell + process exits, its output pipe will not get closed until the + notepad process exits also, because it inherits the pipe form the + shell. In this case, we abandon the thread, and let it live until + all such processes exit. While struct ntpipe_slurp_stream is + deallocated in this case, ntpipe_slurp_stream_shared_data are not. */ + +struct ntpipe_slurp_stream_shared_data { - LPARAM user_data; /* Any user data stored in the stream object */ HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ /* This is a manual-reset object. */ HANDLE hev_caller; /* Caller blocks on this, and we signal it */ @@ -151,20 +161,62 @@ HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */ /* This is a manual-reset object. */ HANDLE hpipe; /* Pipe read end handle. */ - HANDLE hthread; /* Reader thread handle. */ - BYTE onebyte; /* One byte buffer read by thread */ - DWORD die_p; /* Thread must exit ASAP if non-zero */ + LONG die_p; /* Thread must exit ASAP if non-zero */ BOOL eof_p : 1; /* Set when thread saw EOF */ BOOL error_p : 1; /* Read error other than EOF/broken pipe */ + BOOL inuse_p : 1; /* this structure is in use */ + LONG lock_count; /* Client count of this struct, 0=safe to free */ + BYTE onebyte; /* One byte buffer read by thread */ +}; + +#define MAX_SLURP_STREAMS 32 +struct ntpipe_slurp_stream_shared_data +shared_data_block[MAX_SLURP_STREAMS]={0}; + +struct ntpipe_slurp_stream +{ + LPARAM user_data; /* Any user data stored in the stream object */ + struct ntpipe_slurp_stream_shared_data* thread_data; }; DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp, sizeof (struct ntpipe_slurp_stream)); +/* This function is thread-safe, and is called from either thread + context. It serializes freeing shared dtata structure */ +static void +slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s) +{ + if (InterlockedDecrement (&s->lock_count) == 0) + { + /* Destroy events */ + CloseHandle (s->hev_thread); + CloseHandle (s->hev_caller); + CloseHandle (s->hev_unsleep); + s->inuse_p = 0; + } +} + +static struct ntpipe_slurp_stream_shared_data* +slurper_allocate_shared_data() +{ + int i=0; + for (i=0; i<MAX_SLURP_STREAMS; i++) + { + if (!shared_data_block[i].inuse_p) + { + shared_data_block[i].inuse_p=1; + return &shared_data_block[i]; + } + } + return (struct ntpipe_slurp_stream_shared_data*)0; +} + static DWORD WINAPI slurp_thread (LPVOID vparam) { - struct ntpipe_slurp_stream *s = (struct ntpipe_slurp_stream*)vparam; + struct ntpipe_slurp_stream_shared_data *s = + (struct ntpipe_slurp_stream_shared_data*)vparam; for (;;) { @@ -192,7 +244,7 @@ error/eof indication. Before we do, allow internal pipe buffer to accumulate little bit more data. Reader function pulses this event before waiting for - a character, to avoid pipde delay, and to get the byte + a character, to avoid pipe delay, and to get the byte immediately. */ if (!s->die_p) WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY); @@ -210,6 +262,8 @@ WaitForSingleObject (s->hev_thread, INFINITE); } + slurper_free_shared_data_maybe (s); + return 0; } @@ -220,35 +274,43 @@ Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r"); struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA (lstr); DWORD thread_id_unused; + HANDLE hthread; /* We deal only with pipes, for we're using PeekNamedPipe api */ assert (GetFileType (hpipe) == FILE_TYPE_PIPE); - s->die_p = 0; - s->eof_p = FALSE; - s->error_p = FALSE; - s->hpipe = hpipe; - s->user_data = param; + s->thread_data = slurper_allocate_shared_data(); - /* Create reader thread. This could fail, so do not - create events until thread is created */ - s->hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s, - CREATE_SUSPENDED, &thread_id_unused); - if (s->hthread == NULL) + /* Create reader thread. This could fail, so do not create events + until thread is created */ + hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s->thread_data, + CREATE_SUSPENDED, &thread_id_unused); + if (hthread == NULL) { Lstream_delete (lstr); + s->thread_data->inuse_p=0; return Qnil; } + /* Shared data are initially owned by both main and slurper + threads. */ + s->thread_data->lock_count = 2; + s->thread_data->die_p = 0; + s->thread_data->eof_p = FALSE; + s->thread_data->error_p = FALSE; + s->thread_data->hpipe = hpipe; + s->user_data = param; + /* hev_thread is a manual-reset event, initially signaled */ - s->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL); + s->thread_data->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL); /* hev_caller is a manual-reset event, initially nonsignaled */ - s->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL); + s->thread_data->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL); /* hev_unsleep is a manual-reset event, initially nonsignaled */ - s->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL); + s->thread_data->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL); /* Now let it go */ - ResumeThread (s->hthread); + ResumeThread (hthread); + CloseHandle (hthread); lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; XSETLSTREAM (obj, lstr); @@ -266,23 +328,25 @@ get_ntpipe_input_stream_waitable (Lstream *stream) { struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); - return s->hev_caller; + return s->thread_data->hev_caller; } static int ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) { /* This function must be called from the main thread only */ - struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); + struct ntpipe_slurp_stream_shared_data* s = + NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; if (!s->die_p) { DWORD wait_result; - /* Disallow pipe read delay for the thread: we need a character ASAP */ + /* Disallow pipe read delay for the thread: we need a character + ASAP */ SetEvent (s->hev_unsleep); - /* Check if we have a character ready. Give it a short delay, for - the thread to awake from pipe delay, just ion case*/ + /* Check if we have a character ready. Give it a short delay, + for the thread to awake from pipe delay, just ion case*/ wait_result = WaitForSingleObject (s->hev_caller, 2); /* Revert to the normal sleep behavior. */ @@ -308,59 +372,54 @@ if (s->error_p || s->die_p) return -1; - /* Ok, there were no error neither eof - we've got a byte from the pipe */ + /* Ok, there were no error neither eof - we've got a byte from the + pipe */ *(data++) = s->onebyte; --size; - if (size > 0) - { - DWORD bytes_available, bytes_read; + { + DWORD bytes_read = 0; + if (size > 0) + { + DWORD bytes_available; - /* If the api call fails, return at least one byte already read. - ReadFile in thread will return error */ - if (!PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL)) - return 1; + /* If the api call fails, return at least one byte already + read. ReadFile in thread will return error */ + if (PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL)) + { - /* Fetch available bytes. The same consideration applies, so do - not check for errors. ReadFile in the thread will fail if the - next call fails. */ - ReadFile (s->hpipe, data, min (bytes_available, size), &bytes_read, NULL); - - /* Now we can unblock thread, so it attempts to read more */ - SetEvent (s->hev_thread); - return bytes_read + 1; - } - else - { - SetEvent (s->hev_thread); - return 1; - } + /* Fetch available bytes. The same consideration applies, + so do not check for errors. ReadFile in the thread will + fail if the next call fails. */ + if (bytes_available) + ReadFile (s->hpipe, data, min (bytes_available, size), + &bytes_read, NULL); + } + + /* Now we can unblock thread, so it attempts to read more */ + SetEvent (s->hev_thread); + return bytes_read + 1; + } + } } static int ntpipe_slurp_closer (Lstream *stream) { /* This function must be called from the main thread only */ - struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); + struct ntpipe_slurp_stream_shared_data* s = + NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; /* Force thread to stop */ InterlockedIncrement (&s->die_p); - /* Break the pipe, in case the thread still blocked on read */ - CloseHandle (s->hpipe); - - /* Set events which could possibly block slurper */ + /* Set events which could possibly block slurper. Let it finish soon + or later. */ SetEvent (s->hev_unsleep); SetEvent (s->hev_thread); - /* Wait while thread terminates */ - WaitForSingleObject (s->hthread, INFINITE); - CloseHandle (s->hthread); - - /* Destroy events */ - CloseHandle (s->hev_thread); - CloseHandle (s->hev_caller); - CloseHandle (s->hev_unsleep); + /* Unlock and maybe free shared data */ + slurper_free_shared_data_maybe (s); return 0; } @@ -381,6 +440,8 @@ #define NTPIPE_SHOVE_STREAM_DATA(stream) \ LSTREAM_TYPE_DATA (stream, ntpipe_shove) +#define MAX_SHOVE_BUFFER_SIZE 128 + struct ntpipe_shove_stream { LPARAM user_data; /* Any user data stored in the stream object */ @@ -388,10 +449,10 @@ /* This is an auto-reset object. */ HANDLE hpipe; /* Pipe write end handle. */ HANDLE hthread; /* Reader thread handle. */ - LPVOID buffer; /* Buffer being written */ + char buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written */ DWORD size; /* Number of bytes to write */ - DWORD die_p; /* Thread must exit ASAP if non-zero */ - DWORD idle_p; /* Non-zero if thread is waiting for job */ + LONG die_p; /* Thread must exit ASAP if non-zero */ + LONG idle_p; /* Non-zero if thread is waiting for job */ BOOL error_p : 1; /* Read error other than EOF/broken pipe */ BOOL blocking_p : 1;/* Last write attempt would cause blocking */ }; @@ -423,9 +484,6 @@ InterlockedIncrement (&s->die_p); } - /* free it */ - LocalFree ((HLOCAL)s->buffer); - if (s->die_p) break; } @@ -486,11 +544,9 @@ if (s->blocking_p) return 0; - /* Make a copy of data to be written. We intentionally avoid using - xalloc/xfree, because gnu malloc is not thread-safe */ - s->buffer = (LPVOID) LocalAlloc (LMEM_FIXED, size); - if (s->buffer == NULL) - return -1; + if (size>MAX_SHOVE_BUFFER_SIZE) + return 0; + memcpy (s->buffer, data, size); s->size = size; @@ -577,7 +633,7 @@ /* * Add an emacs event to the proper dispatch queue */ -void +static void mswindows_enqueue_dispatch_event (Lisp_Object event) { int user_p = mswindows_user_event_p (XEVENT(event)); @@ -587,12 +643,23 @@ user_p ? &mswindows_u_dispatch_event_queue_tail : &mswindows_s_dispatch_event_queue_tail); - /* This one does not go to window procedure, hence does not - generate XM_BUMPQUEUE magic event! */ + /* Avoid blocking on WaitMessage */ PostMessage (NULL, XM_BUMPQUEUE, 0, 0); } void +mswindows_bump_queue (void) +{ + /* Bump queue, by putting in an empty event */ + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event* event = XEVENT (emacs_event); + + event->event_type = empty_event; + + mswindows_enqueue_dispatch_event (emacs_event); +} + +static void mswindows_enqueue_magic_event (HWND hwnd, UINT message) { Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); @@ -915,8 +982,6 @@ return result; } - - static void mswindows_drain_windows_queue () { @@ -1031,10 +1096,12 @@ /* None. This means that the process handle itself has signaled. Remove the handle from the wait vector, and make status_ntoify note the exited process */ - CloseHandle (mswindows_waitable_handles[ix]); mswindows_waitable_handles [ix] = mswindows_waitable_handles [--mswindows_waitable_count]; kick_status_notify (); + /* Have to return something: there may be no accompanying + process event */ + mswindows_bump_queue (); } } } /* while */ @@ -1187,7 +1254,7 @@ case WM_CLOSE: fobj = mswindows_find_frame (hwnd); enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt)); - mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE); + mswindows_bump_queue (); break; case WM_KEYDOWN: @@ -1611,7 +1678,7 @@ msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); msframe->sizing = 0; /* Queue noop event */ - mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE); + mswindows_bump_queue (); return 0; #ifdef HAVE_SCROLLBARS @@ -1654,12 +1721,6 @@ } break; - case WM_EXITMENULOOP: - if (UNBOUNDP (mswindows_handle_wm_exitmenuloop ( - XFRAME (mswindows_find_frame (hwnd))))) - SendMessage (hwnd, WM_CANCELMODE, 0, 0); - break; - #endif /* HAVE_MENUBARS */ case WM_COMMAND: @@ -1667,15 +1728,16 @@ WORD id = LOWORD (wParam); frame = XFRAME (mswindows_find_frame (hwnd)); +#ifdef HAVE_TOOLBARS + if (!NILP (mswindows_handle_toolbar_wm_command (frame, id))) + break; +#endif + #ifdef HAVE_MENUBARS if (!NILP (mswindows_handle_wm_command (frame, id))) break; #endif -#ifdef HAVE_TOOLBARS - /* O Toolbar Implementor, this place may have something for you!;*/ -#endif - /* Bite me - a spurious command. This cannot happen. */ error ("XEMACS BUG: Cannot decode command message"); } @@ -1929,8 +1991,8 @@ /* We are in progress of frame creation. Return the frame being created, as it still not remembered in the window extra storage. */ - assert (!NILP (mswindows_frame_being_created)); - return mswindows_frame_being_created; + assert (!NILP (Vmswindows_frame_being_created)); + return Vmswindows_frame_being_created; } VOID_TO_LISP (f, l); return f; @@ -2039,10 +2101,6 @@ } break; - case XM_BUMPQUEUE: - /* This is a nice event, when we're in need to queue *something* */ - break; - case XM_MAPFRAME: case XM_UNMAPFRAME: { @@ -2074,52 +2132,24 @@ return get_ntpipe_input_stream_waitable (XLSTREAM (instr)); } -static HANDLE -get_process_handle (struct Lisp_Process *p) -{ - /* #### The guess is that cygwin uses the same algorithm for - computing pids: negate if less than zero, '95 case */ - Lisp_Object process, pid; - XSETPROCESS (process, p); - pid = Fprocess_id (process); - if (INTP (pid)) - { - HANDLE hproc; - int ipid = XINT (pid); - if (ipid < 0) - ipid = -ipid; - hproc = OpenProcess (SYNCHRONIZE, FALSE, ipid); - /* #### This is WRONG! The process migh have ended before we got here. */ - /* assert (hproc != NULL); */ - /* Instead, we fake "a signaled hadle", which will trigger - immediately upon entering the message loop */ - if (hproc = NULL) - hproc = CreateEvent (NULL, TRUE, TRUE, NULL); - return hproc; - } - else - return NULL; -} - static void emacs_mswindows_select_process (struct Lisp_Process *process) { HANDLE hev = get_process_input_waitable (process); - HANDLE hprocess; if (!add_waitable_handle (hev)) error ("Too many active processes"); - hprocess = get_process_handle (process); - if (hprocess) - { - if (!add_waitable_handle (hprocess)) - { - remove_waitable_handle (hev); - CloseHandle (hprocess); - error ("Too many active processes"); - } - } +#ifdef HAVE_WIN32_PROCESSES + { + HANDLE hprocess = get_nt_process_handle (process); + if (!add_waitable_handle (hprocess)) + { + remove_waitable_handle (hev); + error ("Too many active processes"); + } + } +#endif } static void @@ -2144,6 +2174,11 @@ static void emacs_mswindows_quit_p (void) { + /* Quit cannot happen in modal loop: all program + input is dedicated to Windows. */ + if (mswindows_in_modal_loop) + return; + /* Drain windows queue. This sets up number of quit characters in in the queue */ mswindows_drain_windows_queue (); @@ -2191,8 +2226,10 @@ #elif defined (HAVE_UNIX_PROCESSES) /* We are passed UNIX fds. This must be Cygwin. Fetch os handles */ - hin = inhandle >= 0 ? get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE; - hout = outhandle >= 0 ? get_sfhandle ((int)outhandle) : INVALID_HANDLE_VALUE; + hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE; + hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE; + fdi=(int)inhandle; + fdo=(int)outhandle; #else #error "So, WHICH kind of processes do you want?" #endif @@ -2201,9 +2238,25 @@ ? make_ntpipe_input_stream (hin, fdi) : Qnil); +#ifdef HAVE_WIN32_PROCESSES *outstream = (hout != INVALID_HANDLE_VALUE ? make_ntpipe_output_stream (hout, fdo) : Qnil); +#elif defined (HAVE_UNIX_PROCESSES) + *outstream = (fdo >= 0 + ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK) + : Qnil); + +#if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS) + /* FLAGS is process->pty_flag for UNIX_PROCESSES */ + if (flags && fdo >= 0) + { + Bufbyte eof_char = get_eof_char (fdo); + int pty_max_bytes = get_pty_max_bytes (fdo); + filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char); + } +#endif +#endif return (NILP (*instream) ? USID_ERROR : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream)))); @@ -2218,7 +2271,7 @@ int in = (NILP(instream) ? -1 : get_ntpipe_input_stream_param (XLSTREAM (instream))); int out = (NILP(outstream) ? -1 - : get_ntpipe_output_stream_param (XLSTREAM (outstream))); + : filedesc_stream_fd (XLSTREAM (outstream))); if (in >= 0) close (in);