Subject: pthreads in userland, signals, and itimer.
To: None <tech-kern@netbsd.org>
From: Michael Graff <explorer@flame.org>
List: tech-kern
Date: 11/08/1999 10:36:37
I have added to the -current kernel two new signals, and two new
itimers.  The goal is to have user space threads packages stop using
SIGALRM and/or SIGVTALRM.  This simplifies things in many ways, in
that threads can use any signal (other than the new ones, SIGTHREAD
and SIGTHREADV) and any itimer (other than ITIMER_THREAD and
ITIMER_THREADV) and the threads package doesn't have to perform black
magic, and HARD black magic, to route signals properly.

The changes are fairly minor, in that they just create a virtual timer
and a real timer that only a threading package may use.  And the man
pages mark them as "internal to NetBSD libraries" and warn that other
libraries and applications should not touch them.

Ideally, if a program is multithreaded (using our pthreads, once we
have it) applications and other libraries will not be allowed to use
these timers/signals.

Comments?

Index: lib/libc/shlib_version
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/shlib_version,v
retrieving revision 1.77
diff -u -r1.77 shlib_version
--- shlib_version	1999/09/16 12:54:26	1.77
+++ shlib_version	1999/11/08 15:46:39
@@ -2,4 +2,4 @@
 #	Remember to update distrib/sets/lists/base/shl.* when changing
 #
 major=12
-minor=49
+minor=50
Index: lib/libc/gen/__siglist14.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/gen/__siglist14.c,v
retrieving revision 1.1
diff -u -r1.1 __siglist14.c
--- __siglist14.c	1998/11/30 20:45:40	1.1
+++ __siglist14.c	1999/11/08 15:46:52
@@ -79,6 +79,8 @@
 	"User defined signal 1",	/* SIGUSR1 */
 	"User defined signal 2",	/* SIGUSR2 */
 	"Power fail/restart",		/* SIGPWR */
+	"Thread real timer expired",	/* SIGTHREAD */
+	"Thread virtual timer expired",	/* SIGTHREADV */
 };
 
 const int __sys_nsig14 = sizeof(__siglist14) / sizeof(__siglist14[0]);
Index: lib/libc/gen/__signame14.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/gen/__signame14.c,v
retrieving revision 1.1
diff -u -r1.1 __signame14.c
--- __signame14.c	1998/11/30 20:45:40	1.1
+++ __signame14.c	1999/11/08 15:46:52
@@ -79,6 +79,8 @@
 	"USR1",		/* SIGUSR1 */
 	"USR2",		/* SIGUSR2 */
 	"PWR",		/* SIGPWR */
+	"THREAD",	/* SIGTHREAD */
+	"THREADV",	/* SIGTHREADV */
 };
 
 const char * const *__sys_signame14 = __signame14;
Index: lib/libc/sys/getitimer.2
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/sys/getitimer.2,v
retrieving revision 1.10
diff -u -r1.10 getitimer.2
--- getitimer.2	1999/03/22 19:45:03	1.10
+++ getitimer.2	1999/11/08 15:46:54
@@ -45,6 +45,8 @@
 .Fd #define ITIMER_REAL		0
 .Fd #define ITIMER_VIRTUAL	1
 .Fd #define ITIMER_PROF		2
+.Fd #define ITIMER_THREAD	3
+.Fd #define ITIMER_THREADV	4
 .Ft int
 .Fn getitimer "int which" "struct itimerval *value"
 .Ft int
@@ -126,6 +128,29 @@
 delivered.  Because this signal may interrupt in-progress
 system calls, programs using this timer must be prepared to
 restart interrupted system calls.
+.Pp
+The
+.Dv ITIMER_THREAD
+and
+.Dv ITIMER_THREADV
+timers are for use in the system thread library.  They must not be used by
+applications or other libraries.
+.Dv ITIMER_THREAD
+behaves in other ways as
+.Dv ITIMER_REAL ,
+while
+.Dv ITIMER_THREADV
+bahaves like
+.Dv ITIMER_VIRTUAL .
+When the
+.Dv ITIMER_THREAD
+timer expires, the
+.Dv SIGTHREAD
+signal is delivered, and
+.Dv ITIMER_THREADV
+timer expires, the
+.Dv SIGTHREADV
+signal is delivered.
 .Sh NOTES
 Three macros for manipulating time values are defined in
 .Ao Pa sys/time.h Ac .
Index: share/man/man7/signal.7
===================================================================
RCS file: /cvsroot/sharesrc/share/man/man7/signal.7,v
retrieving revision 1.1
diff -u -r1.1 signal.7
--- signal.7	1999/10/06 17:05:42	1.1
+++ signal.7	1999/11/08 15:47:30
@@ -83,6 +83,8 @@
 .It Li SIGUSR1 Ta "terminate process" Ta "user-defined signal 1"
 .It Li SIGUSR2 Ta "terminate process" Ta "user-defined signal 2"
 .It Li SIGPWR Ta "discard signal" Ta "power failure/restart"
+.It Li SIGTHREAD Ta "discard signal" Ta "thread timer expired (see"
+.Xr setitimer 2 )
 .El
 .Sh STANDARDS
 The signals conform to
@@ -103,16 +105,20 @@
 .Dv SIGINFO
 which are Berkeley extensions (available on most
 .Bx Ns \-derived
-systems)
-and
+systems),
 .Dv SIGPWR
-which comes from System V. On NetBSD,
+which comes from System V, and
+.Dv SIGTHREAD
+which is NetBSD specific. On NetBSD,
 .Dv SIGPWR
 was introduced in version 1.4.
 .Sh NOTES
 The current NetBSD kernel never generates the
 .Dv SIGPWR
 signal.
+.Dv SIGTHREAD
+is used by the NetBSD Posix threads library (libthread) and must not be used by applications
+or other libraries.
 .Sh SEE ALSO
 .Xr kill 1 ,
 .Xr kill 2 ,
Index: sys/kern/kern_clock.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_clock.c,v
retrieving revision 1.50
diff -u -r1.50 kern_clock.c
--- kern_clock.c	1999/09/06 20:44:02	1.50
+++ kern_clock.c	1999/11/08 15:48:42
@@ -398,10 +398,14 @@
 		 * Run current process's virtual and profile time, as needed.
 		 */
 		pstats = p->p_stats;
-		if (CLKF_USERMODE(frame) &&
-		    timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
-		    itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
-			psignal(p, SIGVTALRM);
+		if (CLKF_USERMODE(frame)) {
+			if (timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) &&
+			    itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0)
+				psignal(p, SIGVTALRM);
+			if (timerisset(&pstats->p_timer[ITIMER_THREADV].it_value) &&
+			    itimerdecr(&pstats->p_timer[ITIMER_THREADV], tick) == 0)
+				psignal(p, SIGTHREADV);
+		}
 		if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) &&
 		    itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0)
 			psignal(p, SIGPROF);
Index: sys/kern/kern_exit.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_exit.c,v
retrieving revision 1.74
diff -u -r1.74 kern_exit.c
--- kern_exit.c	1999/09/28 14:47:03	1.74
+++ kern_exit.c	1999/11/08 15:48:42
@@ -177,6 +177,7 @@
 	sigemptyset(&p->p_siglist);
 	p->p_sigcheck = 0;
 	untimeout(realitexpire, (caddr_t)p);
+	untimeout(threaditexpire, (caddr_t)p);
 
 	/*
 	 * Close open files and release open-file table.
Index: sys/kern/kern_time.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_time.c,v
retrieving revision 1.41
diff -u -r1.41 kern_time.c
--- kern_time.c	1999/10/10 18:41:53	1.41
+++ kern_time.c	1999/11/08 15:48:44
@@ -423,16 +423,17 @@
 	struct itimerval aitv;
 	int s;
 
-	if ((u_int)which > ITIMER_PROF)
+	if ((u_int)which > ITIMER_THREADV)
 		return (EINVAL);
 	s = splclock();
-	if (which == ITIMER_REAL) {
+	switch (which) {
 		/*
 		 * Convert from absolute to relative time in .it_value
 		 * part of real time timer.  If time for real time timer
 		 * has passed return 0, else return difference between
 		 * current time and time for the timer to go off.
 		 */
+	case ITIMER_REAL:
 		aitv = p->p_realtimer;
 		if (timerisset(&aitv.it_value)) {
 			if (timercmp(&aitv.it_value, &time, <))
@@ -440,8 +441,19 @@
 			else
 				timersub(&aitv.it_value, &time, &aitv.it_value);
 		}
-	} else
+		break;
+	case ITIMER_THREAD:
+		aitv = p->p_threadtimer;
+		if (timerisset(&aitv.it_value)) {
+			if (timercmp(&aitv.it_value, &time, <))
+				timerclear(&aitv.it_value);
+			else
+				timersub(&aitv.it_value, &time, &aitv.it_value);
+		}
+		break;
+	default:
 		aitv = p->p_stats->p_timer[which];
+	}
 	splx(s);
 	return (copyout(&aitv, SCARG(uap, itv), sizeof(struct itimerval)));
 }
@@ -464,7 +476,7 @@
 	register const struct itimerval *itvp;
 	int s, error;
 
-	if ((u_int)which > ITIMER_PROF)
+	if ((u_int)which > ITIMER_THREADV)
 		return (EINVAL);
 	itvp = SCARG(uap, itv);
 	if (itvp && (error = copyin(itvp, &aitv, sizeof(struct itimerval))))
@@ -480,15 +492,26 @@
 	if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval))
 		return (EINVAL);
 	s = splclock();
-	if (which == ITIMER_REAL) {
+	switch (which) {
+	case ITIMER_REAL:
 		untimeout(realitexpire, p);
 		if (timerisset(&aitv.it_value)) {
 			timeradd(&aitv.it_value, &time, &aitv.it_value);
 			timeout(realitexpire, p, hzto(&aitv.it_value));
 		}
 		p->p_realtimer = aitv;
-	} else
+		break;
+	case ITIMER_THREAD:
+		untimeout(threaditexpire, p);
+		if (timerisset(&aitv.it_value)) {
+			timeradd(&aitv.it_value, &time, &aitv.it_value);
+			timeout(threaditexpire, p, hzto(&aitv.it_value));
+		}
+		p->p_threadtimer = aitv;
+		break;
+	default:
 		p->p_stats->p_timer[which] = aitv;
+	}
 	splx(s);
 	return (0);
 }
@@ -521,6 +544,41 @@
 		if (timercmp(&p->p_realtimer.it_value, &time, >)) {
 			timeout(realitexpire, p,
 			    hzto(&p->p_realtimer.it_value));
+			splx(s);
+			return;
+		}
+		splx(s);
+	}
+}
+
+/*
+ * Thread real time interval timer expired:
+ * send process whose timer expired an alarm signal.
+ * If time is not set up to reload, then just return.
+ * Else compute next time timer should go off which is > current time.
+ * This is where delay in processing this timeout causes multiple
+ * SIGTHREAD calls to be compressed into one.
+ */
+void
+threaditexpire(arg)
+	void *arg;
+{
+	register struct proc *p;
+	int s;
+
+	p = (struct proc *)arg;
+	psignal(p, SIGTHREAD);
+	if (!timerisset(&p->p_threadtimer.it_interval)) {
+		timerclear(&p->p_threadtimer.it_value);
+		return;
+	}
+	for (;;) {
+		s = splclock();
+		timeradd(&p->p_threadtimer.it_value,
+		    &p->p_threadtimer.it_interval, &p->p_threadtimer.it_value);
+		if (timercmp(&p->p_threadtimer.it_value, &time, >)) {
+			timeout(realitexpire, p,
+			    hzto(&p->p_threadtimer.it_value));
 			splx(s);
 			return;
 		}
Index: sys/sys/proc.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/proc.h,v
retrieving revision 1.84
diff -u -r1.84 proc.h
--- proc.h	1999/09/28 14:47:04	1.84
+++ proc.h	1999/11/08 15:48:55
@@ -160,6 +160,7 @@
 	u_int	p_slptime;	 /* Time since last blocked. */
 
 	struct	itimerval p_realtimer;	/* Alarm timer. */
+	struct	itimerval p_threadtimer;	/* Thread alarm timer. */
 	struct	timeval p_rtime;	/* Real time. */
 	u_quad_t p_uticks;		/* Statclock hits in user mode. */
 	u_quad_t p_sticks;		/* Statclock hits in system mode. */
Index: sys/sys/resourcevar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/resourcevar.h,v
retrieving revision 1.14
diff -u -r1.14 resourcevar.h
--- resourcevar.h	1999/09/28 14:47:04	1.14
+++ resourcevar.h	1999/11/08 15:48:55
@@ -49,7 +49,7 @@
 #define	pstat_endzero	pstat_startcopy
 
 #define	pstat_startcopy	p_timer
-	struct	itimerval p_timer[3];	/* virtual-time timers */
+	struct	itimerval p_timer[5];	/* virtual-time timers */
 
 	struct uprof {			/* profile arguments */
 		caddr_t	pr_base;	/* buffer base */
Index: sys/sys/signal.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/signal.h,v
retrieving revision 1.42
diff -u -r1.42 signal.h
--- signal.h	1998/12/21 10:35:00	1.42
+++ signal.h	1999/11/08 15:48:55
@@ -45,7 +45,7 @@
 
 #include <sys/featuretest.h>
 
-#define _NSIG		33
+#define _NSIG		35
 
 #if !defined(_ANSI_SOURCE) && !defined(_POSIX_C_SOURCE) && \
     !defined(_XOPEN_SOURCE)
@@ -104,6 +104,8 @@
 #define SIGUSR2		31	/* user defined signal 2 */
 #ifndef _POSIX_SOURCE
 #define	SIGPWR		32	/* power fail/restart (not reset when caught) */
+#define SIGTHREAD	33	/* thread library private alarm */
+#define SIGTHREADV	33	/* thread library private alarm */
 #endif
 
 #ifndef _KERNEL
Index: sys/sys/signalvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/signalvar.h,v
retrieving revision 1.22
diff -u -r1.22 signalvar.h
--- signalvar.h	1999/04/30 21:23:50	1.22
+++ signalvar.h	1999/11/08 15:48:55
@@ -131,6 +131,8 @@
 	SA_KILL,			/* SIGUSR1 */
 	SA_KILL,			/* SIGUSR2 */
 	SA_IGNORE|SA_NORESET,		/* SIGPWR */
+	SA_KILL,			/* SIGTHREAD */
+	SA_KILL				/* SIGTHREADV */
 };
 #endif /* SIGPROP */
 
Index: sys/sys/systm.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/systm.h,v
retrieving revision 1.97
diff -u -r1.97 systm.h
--- systm.h	1999/10/14 18:42:16	1.97
+++ systm.h	1999/11/08 15:48:55
@@ -225,6 +225,7 @@
 void	timeout __P((void (*func)(void *), void *arg, int ticks));
 void	untimeout __P((void (*func)(void *), void *arg));
 void	realitexpire __P((void *));
+void	threaditexpire __P((void *));
 
 struct clockframe;
 void	hardclock __P((struct clockframe *frame));
Index: sys/sys/time.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/time.h,v
retrieving revision 1.28
diff -u -r1.28 time.h
--- time.h	1999/08/16 18:42:25	1.28
+++ time.h	1999/11/08 15:48:55
@@ -129,11 +129,14 @@
 
 /*
  * Names of the interval timers, and structure
- * defining a timer setting.
+ * defining a timer setting.  See man page for cautions on using
+ * ITIMER_THREAD and ITIMER_THREADV
  */
 #define	ITIMER_REAL	0
 #define	ITIMER_VIRTUAL	1
 #define	ITIMER_PROF	2
+#define	ITIMER_THREAD	3	/* libpthread */
+#define	ITIMER_THREADV	4	/* libpthread */
 
 struct	itimerval {
 	struct	timeval it_interval;	/* timer interval */