runtime: turn "too many EPIPE" into real SIGPIPE

Tested on Linux and OS X, amd64 and 386.

R=r, iant
CC=golang-dev
https://golang.org/cl/4452046
diff --git a/src/pkg/runtime/darwin/386/signal.c b/src/pkg/runtime/darwin/386/signal.c
index 35bbb17..29170b6 100644
--- a/src/pkg/runtime/darwin/386/signal.c
+++ b/src/pkg/runtime/darwin/386/signal.c
@@ -185,3 +185,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/darwin/386/sys.s b/src/pkg/runtime/darwin/386/sys.s
index 08eca9d..87fbdbb 100644
--- a/src/pkg/runtime/darwin/386/sys.s
+++ b/src/pkg/runtime/darwin/386/sys.s
@@ -33,6 +33,16 @@
 	INT	$0x80
 	RET
 
+TEXT runtime·raisesigpipe(SB),7,$8
+	get_tls(CX)
+	MOVL	m(CX), DX
+	MOVL	m_procid(DX), DX
+	MOVL	DX, 0(SP)	// thread_port
+	MOVL	$13, 4(SP)	// signal: SIGPIPE
+	MOVL	$328, AX	// __pthread_kill
+	INT	$0x80
+	RET
+
 TEXT runtime·mmap(SB),7,$0
 	MOVL	$197, AX
 	INT	$0x80
diff --git a/src/pkg/runtime/darwin/amd64/signal.c b/src/pkg/runtime/darwin/amd64/signal.c
index 3a99d23..036a3ac 100644
--- a/src/pkg/runtime/darwin/amd64/signal.c
+++ b/src/pkg/runtime/darwin/amd64/signal.c
@@ -195,3 +195,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/darwin/amd64/sys.s b/src/pkg/runtime/darwin/amd64/sys.s
index 39398e0..8d1b20f 100644
--- a/src/pkg/runtime/darwin/amd64/sys.s
+++ b/src/pkg/runtime/darwin/amd64/sys.s
@@ -38,6 +38,15 @@
 	SYSCALL
 	RET
 
+TEXT runtime·raisesigpipe(SB),7,$24
+	get_tls(CX)
+	MOVQ	m(CX), DX
+	MOVL	$13, DI	// arg 1 SIGPIPE
+	MOVQ	m_procid(DX), SI	// arg 2 thread_port
+	MOVL	$(0x2000000+328), AX	// syscall entry __pthread_kill
+	SYSCALL
+	RET
+
 TEXT runtime·setitimer(SB), 7, $0
 	MOVL	8(SP), DI
 	MOVQ	16(SP), SI
diff --git a/src/pkg/runtime/darwin/os.h b/src/pkg/runtime/darwin/os.h
index 339768e..db3c2e8 100644
--- a/src/pkg/runtime/darwin/os.h
+++ b/src/pkg/runtime/darwin/os.h
@@ -27,3 +27,5 @@
 void	runtime·sigtramp(void);
 void	runtime·sigpanic(void);
 void	runtime·setitimer(int32, Itimerval*, Itimerval*);
+
+void	runtime·raisesigpipe(void);
diff --git a/src/pkg/runtime/freebsd/386/signal.c b/src/pkg/runtime/freebsd/386/signal.c
index 1ae2554..3600f07 100644
--- a/src/pkg/runtime/freebsd/386/signal.c
+++ b/src/pkg/runtime/freebsd/386/signal.c
@@ -182,3 +182,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/freebsd/386/sys.s b/src/pkg/runtime/freebsd/386/sys.s
index c4715b6..765e2fc 100644
--- a/src/pkg/runtime/freebsd/386/sys.s
+++ b/src/pkg/runtime/freebsd/386/sys.s
@@ -60,6 +60,20 @@
 	INT	$0x80
 	RET
 
+TEXT runtime·raisesigpipe(SB),7,$12
+	// thr_self(&8(SP))
+	LEAL	8(SP), AX
+	MOVL	AX, 0(SP)
+	MOVL	$432, AX
+	INT	$0x80
+	// thr_kill(self, SIGPIPE)
+	MOVL	8(SP), AX
+	MOVL	AX, 0(SP)
+	MOVL	$13, 4(SP)
+	MOVL	$433, AX
+	INT	$0x80
+	RET
+
 TEXT runtime·notok(SB),7,$0
 	MOVL	$0xf1, 0xf1
 	RET
diff --git a/src/pkg/runtime/freebsd/amd64/signal.c b/src/pkg/runtime/freebsd/amd64/signal.c
index 9d8e5e6..85cb1d8 100644
--- a/src/pkg/runtime/freebsd/amd64/signal.c
+++ b/src/pkg/runtime/freebsd/amd64/signal.c
@@ -190,3 +190,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/freebsd/amd64/sys.s b/src/pkg/runtime/freebsd/amd64/sys.s
index 9a6fdf1..c5cc082 100644
--- a/src/pkg/runtime/freebsd/amd64/sys.s
+++ b/src/pkg/runtime/freebsd/amd64/sys.s
@@ -65,6 +65,18 @@
 	SYSCALL
 	RET
 
+TEXT runtime·raisesigpipe(SB),7,$16
+	// thr_self(&8(SP))
+	LEAQ	8(SP), DI	// arg 1 &8(SP)
+	MOVL	$432, AX
+	SYSCALL
+	// thr_kill(self, SIGPIPE)
+	MOVQ	8(SP), DI	// arg 1 id
+	MOVQ	$13, SI	// arg 2 SIGPIPE
+	MOVL	$433, AX
+	SYSCALL
+	RET
+
 TEXT runtime·setitimer(SB), 7, $-8
 	MOVL	8(SP), DI
 	MOVQ	16(SP), SI
diff --git a/src/pkg/runtime/freebsd/os.h b/src/pkg/runtime/freebsd/os.h
index 1375468..007856c 100644
--- a/src/pkg/runtime/freebsd/os.h
+++ b/src/pkg/runtime/freebsd/os.h
@@ -8,3 +8,5 @@
 void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
 void	runtiem·setitimerval(int32, Itimerval*, Itimerval*);
 void	runtime·setitimer(int32, Itimerval*, Itimerval*);
+
+void	runtime·raisesigpipe(void);
diff --git a/src/pkg/runtime/linux/386/signal.c b/src/pkg/runtime/linux/386/signal.c
index 9b72ecb..8916e10 100644
--- a/src/pkg/runtime/linux/386/signal.c
+++ b/src/pkg/runtime/linux/386/signal.c
@@ -175,3 +175,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s
index c39ce25..868a0d9 100644
--- a/src/pkg/runtime/linux/386/sys.s
+++ b/src/pkg/runtime/linux/386/sys.s
@@ -30,6 +30,14 @@
 	INT	$0x80
 	RET
 
+TEXT runtime·raisesigpipe(SB),7,$12
+	MOVL	$224, AX	// syscall - gettid
+	INT	$0x80
+	MOVL	AX, 0(SP)	// arg 1 tid
+	MOVL	$13, 4(SP)	// arg 2 SIGPIPE
+	MOVL	$238, AX	// syscall - tkill
+	INT	$0x80
+	RET
 
 TEXT runtime·setitimer(SB),7,$0-24
 	MOVL	$104, AX			// syscall - setitimer
diff --git a/src/pkg/runtime/linux/amd64/signal.c b/src/pkg/runtime/linux/amd64/signal.c
index 1db9c95..ee90271 100644
--- a/src/pkg/runtime/linux/amd64/signal.c
+++ b/src/pkg/runtime/linux/amd64/signal.c
@@ -185,3 +185,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/linux/amd64/sys.s b/src/pkg/runtime/linux/amd64/sys.s
index 11df1f8..eadd300 100644
--- a/src/pkg/runtime/linux/amd64/sys.s
+++ b/src/pkg/runtime/linux/amd64/sys.s
@@ -36,6 +36,15 @@
 	SYSCALL
 	RET
 
+TEXT runtime·raisesigpipe(SB),7,$12
+	MOVL	$186, AX	// syscall - gettid
+	SYSCALL
+	MOVL	AX, DI	// arg 1 tid
+	MOVL	$13, SI	// arg 2 SIGPIPE
+	MOVL	$200, AX	// syscall - tkill
+	SYSCALL
+	RET
+
 TEXT runtime·setitimer(SB),7,$0-24
 	MOVL	8(SP), DI
 	MOVQ	16(SP), SI
diff --git a/src/pkg/runtime/linux/arm/signal.c b/src/pkg/runtime/linux/arm/signal.c
index 05c6b02..88a84d1 100644
--- a/src/pkg/runtime/linux/arm/signal.c
+++ b/src/pkg/runtime/linux/arm/signal.c
@@ -180,3 +180,10 @@
 	}
 	m->profilehz = hz;
 }
+
+void
+os·sigpipe(void)
+{
+	sigaction(SIGPIPE, SIG_DFL, false);
+	runtime·raisesigpipe();
+}
diff --git a/src/pkg/runtime/linux/arm/sys.s b/src/pkg/runtime/linux/arm/sys.s
index b9767a0..3fe7d4a 100644
--- a/src/pkg/runtime/linux/arm/sys.s
+++ b/src/pkg/runtime/linux/arm/sys.s
@@ -27,6 +27,8 @@
 #define SYS_exit_group (SYS_BASE + 248)
 #define SYS_munmap (SYS_BASE + 91)
 #define SYS_setitimer (SYS_BASE + 104)
+#define SYS_gettid (SYS_BASE + 224)
+#define SYS_tkill (SYS_BASE + 238)
 
 #define ARM_BASE (SYS_BASE + 0x0f0000)
 #define SYS_ARM_cacheflush (ARM_BASE + 2)
@@ -55,6 +57,15 @@
 	MOVW	$1003, R1
 	MOVW	R0, (R1)	// fail hard
 
+TEXT	runtime·raisesigpipe(SB),7,$-4
+	MOVW	$SYS_gettid, R7
+	SWI	$0
+	// arg 1 tid already in R0 from gettid
+	MOVW	$13, R1	// arg 2 SIGPIPE
+	MOVW	$SYS_tkill, R7
+	SWI	$0
+	RET
+
 TEXT runtime·mmap(SB),7,$0
 	MOVW	0(FP), R0
 	MOVW	4(FP), R1
diff --git a/src/pkg/runtime/linux/os.h b/src/pkg/runtime/linux/os.h
index 6ae0889..0bb8d03 100644
--- a/src/pkg/runtime/linux/os.h
+++ b/src/pkg/runtime/linux/os.h
@@ -15,3 +15,5 @@
 void	runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
 void	runtime·sigpanic(void);
 void runtime·setitimer(int32, Itimerval*, Itimerval*);
+
+void	runtime·raisesigpipe(void);
diff --git a/src/pkg/runtime/plan9/thread.c b/src/pkg/runtime/plan9/thread.c
index fa96552..7c6ca45 100644
--- a/src/pkg/runtime/plan9/thread.c
+++ b/src/pkg/runtime/plan9/thread.c
@@ -138,3 +138,8 @@
 	runtime·usemrelease(&n->sema);
 }
 
+void
+os·sigpipe(void)
+{
+	runtime·throw("too many writes on closed pipe");
+}
diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c
index aedd242..2ce92dc 100644
--- a/src/pkg/runtime/windows/thread.c
+++ b/src/pkg/runtime/windows/thread.c
@@ -378,3 +378,9 @@
 
 	return ret;
 }
+
+void
+os·sigpipe(void)
+{
+	runtime·throw("too many writes on closed pipe");
+}