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 */