4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-02-23 00:59:51 +08:00
Takashi Yano 7ed9adb356 Cygwin: pipe: Switch pipe mode to blocking mode by default
Previously, cygwin read pipe used non-blocking mode although non-
cygwin app uses blocking-mode by default. Despite this requirement,
if a cygwin app is executed from a non-cygwin app and the cygwin
app exits, read pipe remains on non-blocking mode because of the
commit fc691d0246b9. Due to this behaviour, the non-cygwin app
cannot read the pipe correctly after that. Similarly, if a non-
cygwin app is executed from a cygwin app and the non-cygwin app
exits, the read pipe mode remains on blocking mode although cygwin
read pipe should be non-blocking mode.

These bugs were provoked by pipe mode toggling between cygwin and
non-cygwin apps. To make management of pipe mode simpler, this
patch has re-designed the pipe implementation. In this new
implementation, both read and write pipe basically use only blocking
mode and the behaviour corresponding to the pipe mode is simulated
in raw_read() and raw_write(). Only when NtQueryInformationFile
(FilePipeLocalInformation) fails for some reasons, the raw_read()/
raw_write() cannot simulate non-blocking access. Therefore, the pipe
mode is temporarily changed to non-blocking mode.

Moreover, because the fact that NtSetInformationFile() in
set_pipe_non_blocking(true) fails with STATUS_PIPE_BUSY if the pipe
is not empty has been found, query handle is not necessary anymore.
This allows the implementation much simpler than before.

Addresses: https://github.com/git-for-windows/git/issues/5115
Fixes: fc691d0246b9 ("Cygwin: pipe: Make sure to set read pipe non-blocking for cygwin apps.");
Reported-by: isaacag, Johannes Schindelin <Johannes.Schindelin@gmx.de>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>, Ken Brown <kbrown@cornell.edu>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
2024-11-01 04:50:45 +09:00

183 lines
3.9 KiB
C++

/* sigproc.h
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#pragma once
#include <signal.h>
#include "sync.h"
#ifdef _NSIG
enum
{
__SIGFLUSH = -(_NSIG + 1),
__SIGSTRACE = -(_NSIG + 2),
__SIGCOMMUNE = -(_NSIG + 3),
__SIGPENDING = -(_NSIG + 4),
__SIGDELETE = -(_NSIG + 5), /* Not currently used */
__SIGFLUSHFAST = -(_NSIG + 6),
__SIGHOLD = -(_NSIG + 7),
__SIGNOHOLD = -(_NSIG + 8),
__SIGSETPGRP = -(_NSIG + 9),
__SIGTHREADEXIT = -(_NSIG + 10),
__SIGPENDINGALL = -(_NSIG + 11),
};
#endif
#define SIG_BAD_MASK (1 << (SIGKILL - 1))
enum procstuff
{
PROC_ADD_CHILD = 1, // set up a new child
PROC_ATTACH_CHILD = 2, // attach child or reattach after exec
PROC_EXEC_CLEANUP = 3, // cleanup waiting children after exec
PROC_CLEARWAIT = 4, // clear all waits - signal arrived
PROC_WAIT = 5, // setup for wait() for subproc
PROC_EXECING = 6, // used to get a lock when execing
PROC_NOTHING = 7 // nothing, really
};
struct sigpacket
{
siginfo_t si;
pid_t pid;
class _cygtls *sigtls;
sigset_t *mask;
union
{
HANDLE wakeup;
HANDLE thread_handle;
struct sigpacket *next;
};
int process ();
int setup_handler (void *, struct sigaction&, _cygtls *);
};
void sig_dispatch_pending (bool fast = false);
void set_signal_mask (sigset_t&, sigset_t);
int handle_sigprocmask (int sig, const sigset_t *set,
sigset_t *oldset, sigset_t& opmask);
void sig_clear (int);
void sig_set_pending (int);
int handle_sigsuspend (sigset_t);
int proc_subproc (DWORD, uintptr_t);
class _pinfo;
void proc_terminate ();
void sigproc_init ();
bool pid_exists (pid_t);
sigset_t sig_send (_pinfo *, siginfo_t&, class _cygtls * = NULL);
sigset_t sig_send (_pinfo *, int, class _cygtls * = NULL);
void signal_fixup_after_exec ();
void sigalloc ();
int kill_pgrp (pid_t, siginfo_t&);
void exit_thread (DWORD) __attribute__ ((noreturn));
void setup_signal_exit (int);
int sigwait_common (const sigset_t *, siginfo_t *, PLARGE_INTEGER);
class no_thread_exit_protect
{
static bool flag;
bool modify;
public:
no_thread_exit_protect (int) {flag = true; modify = true;}
~no_thread_exit_protect ()
{
if (modify)
flag = false;
}
no_thread_exit_protect () {modify = false;}
operator int () {return flag;}
};
extern "C" void sigdelayed ();
extern char myself_nowait_dummy[];
extern struct sigaction *global_sigs;
class lock_signals
{
bool worked;
public:
lock_signals ()
{
worked = (bool) sig_send (NULL, __SIGHOLD) == 0;
}
operator int () const
{
return worked;
}
void dont_bother ()
{
worked = false;
}
~lock_signals ()
{
if (worked)
sig_send (NULL, __SIGNOHOLD);
}
};
class lock_pthread
{
bool bother;
public:
lock_pthread (): bother (1)
{
pthread::atforkprepare ();
}
void dont_bother ()
{
bother = false;
}
~lock_pthread ()
{
if (bother)
pthread::atforkparent ();
}
};
class hold_everything
{
bool& ischild;
/* Note the order of the locks below. It is important,
to avoid races, that the lock order be preserved.
pthread is first because it serves as a master lock
against other forks being attempted while this one is active.
signals is next to stop signal processing for the duration
of the fork.
process is last. If it is put before signals, then a deadlock
could be introduced if the process attempts to exit due to a signal. */
lock_pthread pthread;
lock_signals signals;
lock_process process;
public:
hold_everything (bool& x): ischild (x) {}
operator int () const {return signals;}
~hold_everything()
{
if (ischild)
{
pthread.dont_bother ();
process.dont_bother ();
signals.dont_bother ();
}
}
};
#define myself_nowait ((_pinfo *) myself_nowait_dummy)