diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 4f42cf1b8..34b209f5d 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1323,17 +1323,33 @@ struct fifo_reader_id_t class fifo_shmem_t { LONG _nreaders; - fifo_reader_id_t _owner; + fifo_reader_id_t _owner, _prev_owner; af_unix_spinlock_t _owner_lock; + /* Info about shared memory block used for temporary storage of the + owner's fc_handler list. */ + LONG _sh_nhandlers, _sh_shandlers, _sh_fc_handler_committed; + public: int inc_nreaders () { return (int) InterlockedIncrement (&_nreaders); } int dec_nreaders () { return (int) InterlockedDecrement (&_nreaders); } fifo_reader_id_t get_owner () const { return _owner; } void set_owner (fifo_reader_id_t fr_id) { _owner = fr_id; } + fifo_reader_id_t get_prev_owner () const { return _prev_owner; } + void set_prev_owner (fifo_reader_id_t fr_id) { _prev_owner = fr_id; } + void owner_lock () { _owner_lock.lock (); } void owner_unlock () { _owner_lock.unlock (); } + + int get_shared_nhandlers () const { return (int) _sh_nhandlers; } + void set_shared_nhandlers (int n) { InterlockedExchange (&_sh_nhandlers, n); } + int get_shared_shandlers () const { return (int) _sh_shandlers; } + void set_shared_shandlers (int n) { InterlockedExchange (&_sh_shandlers, n); } + size_t get_shared_fc_handler_committed () const + { return (size_t) _sh_fc_handler_committed; } + void set_shared_fc_handler_committed (size_t n) + { InterlockedExchange (&_sh_fc_handler_committed, (LONG) n); } }; class fhandler_fifo: public fhandler_base @@ -1360,24 +1376,47 @@ class fhandler_fifo: public fhandler_base HANDLE shmem_handle; fifo_shmem_t *shmem; + HANDLE shared_fc_hdl; + /* Dynamically growing array in shared memory. */ + fifo_client_handler *shared_fc_handler; bool __reg2 wait (HANDLE); static NTSTATUS npfs_handle (HANDLE &); HANDLE create_pipe_instance (); NTSTATUS open_pipe (HANDLE&); NTSTATUS wait_open_pipe (HANDLE&); - int add_client_handler (); + int add_client_handler (bool new_pipe_instance = true); void delete_client_handler (int); + void cleanup_handlers (); + void close_all_handlers (); void cancel_reader_thread (); void record_connection (fifo_client_handler&, fifo_client_connect_state = fc_connected); int create_shmem (); int reopen_shmem (); + int create_shared_fc_handler (); + int reopen_shared_fc_handler (); + int remap_shared_fc_handler (size_t); int inc_nreaders () { return shmem->inc_nreaders (); } int dec_nreaders () { return shmem->dec_nreaders (); } + fifo_reader_id_t get_prev_owner () const { return shmem->get_prev_owner (); } + void set_prev_owner (fifo_reader_id_t fr_id) + { shmem->set_prev_owner (fr_id); } + + int get_shared_nhandlers () { return shmem->get_shared_nhandlers (); } + void set_shared_nhandlers (int n) { shmem->set_shared_nhandlers (n); } + int get_shared_shandlers () { return shmem->get_shared_shandlers (); } + void set_shared_shandlers (int n) { shmem->set_shared_shandlers (n); } + size_t get_shared_fc_handler_committed () const + { return shmem->get_shared_fc_handler_committed (); } + void set_shared_fc_handler_committed (size_t n) + { shmem->set_shared_fc_handler_committed (n); } + int update_my_handlers (); + int update_shared_handlers (); + public: fhandler_fifo (); bool hit_eof (); diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc index 595e55ad9..fd5d9f805 100644 --- a/winsup/cygwin/fhandler_fifo.cc +++ b/winsup/cygwin/fhandler_fifo.cc @@ -21,6 +21,7 @@ #include "shared_info.h" #include "ntdll.h" #include "cygwait.h" +#include /* Overview: @@ -65,6 +66,9 @@ STATUS_PIPE_EMPTY simply means there's no data to be read. */ || _s == STATUS_PIPE_NOT_AVAILABLE \ || _s == STATUS_PIPE_BUSY; }) +/* Number of pages reserved for shared_fc_handler. */ +#define SH_FC_HANDLER_PAGES 100 + static NO_COPY fifo_reader_id_t null_fr_id = { .winpid = 0, .fh = NULL }; fhandler_fifo::fhandler_fifo (): @@ -74,7 +78,8 @@ fhandler_fifo::fhandler_fifo (): fc_handler (NULL), shandlers (0), nhandlers (0), reader (false), writer (false), duplexer (false), max_atomic_write (DEFAULT_PIPEBUFSIZE), - me (null_fr_id), shmem_handle (NULL), shmem (NULL) + me (null_fr_id), shmem_handle (NULL), shmem (NULL), + shared_fc_hdl (NULL), shared_fc_handler (NULL) { pipe_name_buf[0] = L'\0'; need_fork_fixup (true); @@ -286,10 +291,9 @@ fhandler_fifo::wait_open_pipe (HANDLE& ph) } int -fhandler_fifo::add_client_handler () +fhandler_fifo::add_client_handler (bool new_pipe_instance) { fifo_client_handler fc; - HANDLE ph = NULL; if (nhandlers >= shandlers) { @@ -303,11 +307,14 @@ fhandler_fifo::add_client_handler () } fc_handler = (fifo_client_handler *) temp; } - ph = create_pipe_instance (); - if (!ph) - return -1; - fc.h = ph; - fc.state = fc_listening; + if (new_pipe_instance) + { + HANDLE ph = create_pipe_instance (); + if (!ph) + return -1; + fc.h = ph; + fc.state = fc_listening; + } fc_handler[nhandlers++] = fc; return 0; } @@ -321,6 +328,21 @@ fhandler_fifo::delete_client_handler (int i) (nhandlers - i) * sizeof (fc_handler[i])); } +/* Delete invalid handlers. */ +void +fhandler_fifo::cleanup_handlers () +{ + int i = 0; + + while (i < nhandlers) + { + if (fc_handler[i].state < fc_connected) + delete_client_handler (i); + else + i++; + } +} + void fhandler_fifo::record_connection (fifo_client_handler& fc, fifo_client_connect_state s) @@ -331,6 +353,65 @@ fhandler_fifo::record_connection (fifo_client_handler& fc, set_pipe_non_blocking (fc.h, true); } +/* Called from fifo_reader_thread_func with owner_lock in place. */ +int +fhandler_fifo::update_my_handlers () +{ + close_all_handlers (); + fifo_reader_id_t prev = get_prev_owner (); + if (!prev) + { + debug_printf ("No previous owner to copy handles from"); + return 0; + } + HANDLE prev_proc; + if (prev.winpid == me.winpid) + prev_proc = GetCurrentProcess (); + else + prev_proc = OpenProcess (PROCESS_DUP_HANDLE, false, prev.winpid); + if (!prev_proc) + { + debug_printf ("Can't open process of previous owner, %E"); + __seterrno (); + return -1; + } + + for (int i = 0; i < get_shared_nhandlers (); i++) + { + /* Should never happen. */ + if (shared_fc_handler[i].state < fc_connected) + continue; + if (add_client_handler (false) < 0) + api_fatal ("Can't add client handler, %E"); + fifo_client_handler &fc = fc_handler[nhandlers - 1]; + if (!DuplicateHandle (prev_proc, shared_fc_handler[i].h, + GetCurrentProcess (), &fc.h, 0, + !close_on_exec (), DUPLICATE_SAME_ACCESS)) + { + debug_printf ("Can't duplicate handle of previous owner, %E"); + --nhandlers; + __seterrno (); + return -1; + } + fc.state = shared_fc_handler[i].state; + } + return 0; +} + +int +fhandler_fifo::update_shared_handlers () +{ + cleanup_handlers (); + if (nhandlers > get_shared_shandlers ()) + { + if (remap_shared_fc_handler (nhandlers * sizeof (fc_handler[0])) < 0) + return -1; + } + set_shared_nhandlers (nhandlers); + memcpy (shared_fc_handler, fc_handler, nhandlers * sizeof (fc_handler[0])); + return 0; +} + static DWORD WINAPI fifo_reader_thread (LPVOID param) { @@ -355,6 +436,8 @@ fhandler_fifo::fifo_reader_thread_func () if (!cur_owner) { set_owner (me); + if (update_my_handlers () < 0) + api_fatal ("Can't update my handlers, %E"); owner_unlock (); continue; } @@ -368,19 +451,7 @@ fhandler_fifo::fifo_reader_thread_func () { /* I'm the owner */ fifo_client_lock (); - - /* Cleanup the fc_handler list. */ - fifo_client_lock (); - int i = 0; - while (i < nhandlers) - { - if (fc_handler[i].state < fc_connected) - delete_client_handler (i); - else - i++; - } - - /* Create a new client handler. */ + cleanup_handlers (); if (add_client_handler () < 0) api_fatal ("Can't add a client handler, %E"); @@ -391,6 +462,7 @@ fhandler_fifo::fifo_reader_thread_func () NTSTATUS status; IO_STATUS_BLOCK io; bool cancel = false; + bool update = false; status = NtFsControlFile (fc.h, conn_evt, NULL, NULL, &io, FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0); @@ -407,6 +479,7 @@ fhandler_fifo::fifo_reader_thread_func () case WAIT_OBJECT_0 + 1: status = STATUS_THREAD_IS_TERMINATING; cancel = true; + update = true; break; default: api_fatal ("WFMO failed, %E"); @@ -459,6 +532,8 @@ fhandler_fifo::fifo_reader_thread_func () fifo_client_unlock (); if (ph) NtClose (ph); + if (update && update_shared_handlers () < 0) + api_fatal ("Can't update shared handlers, %E"); if (cancel) goto canceled; } @@ -532,6 +607,100 @@ fhandler_fifo::reopen_shmem () return 0; } +/* On first creation, map and commit one page of memory. */ +int +fhandler_fifo::create_shared_fc_handler () +{ + HANDLE sect; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + LARGE_INTEGER size + = { .QuadPart = (LONGLONG) (SH_FC_HANDLER_PAGES * wincap.page_size ()) }; + SIZE_T viewsize = get_shared_fc_handler_committed () ?: wincap.page_size (); + PVOID addr = NULL; + UNICODE_STRING uname; + WCHAR shared_fc_name[MAX_PATH]; + + __small_swprintf (shared_fc_name, L"fifo-shared-fc.%08x.%016X", get_dev (), + get_ino ()); + RtlInitUnicodeString (&uname, shared_fc_name); + InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, + get_shared_parent_dir (), NULL); + status = NtCreateSection (§, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY + | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr, + &size, PAGE_READWRITE, SEC_RESERVE, NULL); + if (status == STATUS_OBJECT_NAME_COLLISION) + status = NtOpenSection (§, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY + | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + status = NtMapViewOfSection (sect, NtCurrentProcess (), &addr, 0, viewsize, + NULL, &viewsize, ViewShare, 0, PAGE_READWRITE); + if (!NT_SUCCESS (status)) + { + NtClose (sect); + __seterrno_from_nt_status (status); + return -1; + } + shared_fc_hdl = sect; + shared_fc_handler = (fifo_client_handler *) addr; + if (!get_shared_fc_handler_committed ()) + set_shared_fc_handler_committed (viewsize); + set_shared_shandlers (viewsize / sizeof (fifo_client_handler)); + return 0; +} + +/* shared_fc_hdl must be valid when this is called. */ +int +fhandler_fifo::reopen_shared_fc_handler () +{ + NTSTATUS status; + SIZE_T viewsize = get_shared_fc_handler_committed (); + PVOID addr = NULL; + + status = NtMapViewOfSection (shared_fc_hdl, NtCurrentProcess (), + &addr, 0, viewsize, NULL, &viewsize, + ViewShare, 0, PAGE_READWRITE); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + shared_fc_handler = (fifo_client_handler *) addr; + return 0; +} + +int +fhandler_fifo::remap_shared_fc_handler (size_t nbytes) +{ + NTSTATUS status; + SIZE_T viewsize = roundup2 (nbytes, wincap.page_size ()); + PVOID addr = NULL; + + if (viewsize > SH_FC_HANDLER_PAGES * wincap.page_size ()) + { + set_errno (ENOMEM); + return -1; + } + + NtUnmapViewOfSection (NtCurrentProcess (), shared_fc_handler); + status = NtMapViewOfSection (shared_fc_hdl, NtCurrentProcess (), + &addr, 0, viewsize, NULL, &viewsize, + ViewShare, 0, PAGE_READWRITE); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + shared_fc_handler = (fifo_client_handler *) addr; + set_shared_fc_handler_committed (viewsize); + set_shared_shandlers (viewsize / sizeof (fc_handler[0])); + return 0; +} + int fhandler_fifo::open (int flags, mode_t) { @@ -599,6 +768,8 @@ fhandler_fifo::open (int flags, mode_t) SetEvent (read_ready); if (create_shmem () < 0) goto err_close_writer_opening; + if (create_shared_fc_handler () < 0) + goto err_close_shmem; inc_nreaders (); if (!(cancel_evt = create_event ())) goto err_dec_nreaders; @@ -724,7 +895,10 @@ err_close_cancel_evt: err_dec_nreaders: if (dec_nreaders () == 0) ResetEvent (read_ready); -/* err_close_shmem: */ +/* err_close_shared_fc_handler: */ + NtUnmapViewOfSection (NtCurrentProcess (), shared_fc_handler); + NtClose (shared_fc_hdl); +err_close_shmem: NtUnmapViewOfSection (NtCurrentProcess (), shmem); NtClose (shmem_handle); err_close_writer_opening: @@ -1012,6 +1186,14 @@ fhandler_fifo::fstatvfs (struct statvfs *sfs) return fh.fstatvfs (sfs); } +void +fhandler_fifo::close_all_handlers () +{ + for (int i = 0; i < nhandlers; i++) + fc_handler[i].close (); + nhandlers = 0; +} + int fifo_client_handler::pipe_state () { @@ -1062,6 +1244,10 @@ fhandler_fifo::close () NtUnmapViewOfSection (NtCurrentProcess (), shmem); if (shmem_handle) NtClose (shmem_handle); + if (shared_fc_handler) + NtUnmapViewOfSection (NtCurrentProcess (), shared_fc_handler); + if (shared_fc_hdl) + NtClose (shared_fc_hdl); } if (read_ready) NtClose (read_ready); @@ -1069,8 +1255,7 @@ fhandler_fifo::close () NtClose (write_ready); if (writer_opening) NtClose (writer_opening); - for (int i = 0; i < nhandlers; i++) - fc_handler[i].close (); + close_all_handlers (); if (fc_handler) free (fc_handler); return fhandler_base::close (); @@ -1144,8 +1329,17 @@ fhandler_fifo::dup (fhandler_base *child, int flags) } if (fhf->reopen_shmem () < 0) goto err_close_shmem_handle; + if (!DuplicateHandle (GetCurrentProcess (), shared_fc_hdl, + GetCurrentProcess (), &fhf->shared_fc_hdl, + 0, !(flags & O_CLOEXEC), DUPLICATE_SAME_ACCESS)) + { + __seterrno (); + goto err_close_shmem; + } + if (fhf->reopen_shared_fc_handler () < 0) + goto err_close_shared_fc_hdl; if (!(fhf->cancel_evt = create_event ())) - goto err_close_shmem; + goto err_close_shared_fc_handler; if (!(fhf->thr_sync_evt = create_event ())) goto err_close_cancel_evt; inc_nreaders (); @@ -1155,6 +1349,10 @@ fhandler_fifo::dup (fhandler_base *child, int flags) return 0; err_close_cancel_evt: NtClose (fhf->cancel_evt); +err_close_shared_fc_handler: + NtUnmapViewOfSection (GetCurrentProcess (), fhf->shared_fc_handler); +err_close_shared_fc_hdl: + NtClose (fhf->shared_fc_hdl); err_close_shmem: NtUnmapViewOfSection (GetCurrentProcess (), fhf->shmem); err_close_shmem_handle: @@ -1184,6 +1382,9 @@ fhandler_fifo::fixup_after_fork (HANDLE parent) fork_fixup (parent, shmem_handle, "shmem_handle"); if (reopen_shmem () < 0) api_fatal ("Can't reopen shared memory during fork, %E"); + fork_fixup (parent, shared_fc_hdl, "shared_fc_hdl"); + if (reopen_shared_fc_handler () < 0) + api_fatal ("Can't reopen shared fc_handler memory during fork, %E"); if (close_on_exec ()) /* Prevent a later attempt to close the non-inherited pipe-instance handles copied from the parent. */ @@ -1209,6 +1410,8 @@ fhandler_fifo::fixup_after_exec () if (reopen_shmem () < 0) api_fatal ("Can't reopen shared memory during exec, %E"); + if (reopen_shared_fc_handler () < 0) + api_fatal ("Can't reopen shared fc_handler memory during exec, %E"); fc_handler = NULL; nhandlers = shandlers = 0; me.winpid = GetCurrentProcessId ();