xref: /illumos-gate/usr/src/lib/libc/port/rt/clock_timer.c (revision 27954b0d964ffcb749cf19296906e7fecdf3da1b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "lint.h"
28 #include <time.h>
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include "sigev_thread.h"
34 
35 /*
36  * System call wrappers found elsewhere in libc (common/sys/__clock_timer.s).
37  */
38 extern int __clock_getres(clockid_t, timespec_t *);
39 extern int __clock_gettime(clockid_t, timespec_t *);
40 extern int __clock_settime(clockid_t, const timespec_t *);
41 extern int __timer_create(clockid_t, struct sigevent *, timer_t *);
42 extern int __timer_delete(timer_t);
43 extern int __timer_getoverrun(timer_t);
44 extern int __timer_gettime(timer_t, itimerspec_t *);
45 extern int __timer_settime(timer_t, int, const itimerspec_t *, itimerspec_t *);
46 
47 /*
48  * Array of pointers to tcd's, indexed by timer id.
49  * No more than 'timer_max' timers can be created by any process.
50  */
51 int timer_max = 0;
52 thread_communication_data_t **timer_tcd;
53 static pthread_once_t timer_once = PTHREAD_ONCE_INIT;
54 
55 static void
56 timer_init(void)
57 {
58 	timer_max = (int)_sysconf(_SC_TIMER_MAX);
59 	timer_tcd = malloc(timer_max * sizeof (*timer_tcd));
60 	(void) memset(timer_tcd, 0, timer_max * sizeof (*timer_tcd));
61 }
62 
63 int
64 clock_getres(clockid_t clock_id, timespec_t *res)
65 {
66 	return (__clock_getres(clock_id, res));
67 }
68 
69 int
70 clock_gettime(clockid_t clock_id, timespec_t *tp)
71 {
72 	return (__clock_gettime(clock_id, tp));
73 }
74 
75 int
76 clock_settime(clockid_t clock_id, const timespec_t *tp)
77 {
78 	return (__clock_settime(clock_id, tp));
79 }
80 
81 int
82 timer_create(clockid_t clock_id, struct sigevent *sigevp, timer_t *timerid)
83 {
84 	struct sigevent sigevent;
85 	port_notify_t port_notify;
86 	thread_communication_data_t *tcdp;
87 	int sigev_thread = 0;
88 	int rc;
89 
90 	(void) pthread_once(&timer_once, timer_init);
91 
92 	if (sigevp != NULL &&
93 	    sigevp->sigev_notify == SIGEV_THREAD &&
94 	    sigevp->sigev_notify_function != NULL) {
95 		sigev_thread = 1;
96 		tcdp = setup_sigev_handler(sigevp, TIMER);
97 		if (tcdp == NULL)
98 			return (-1);
99 		/* copy the sigevent structure so we can modify it */
100 		sigevent = *sigevp;
101 		sigevp = &sigevent;
102 		port_notify.portnfy_port = tcdp->tcd_port;
103 		port_notify.portnfy_user = NULL;
104 		sigevp->sigev_value.sival_ptr = &port_notify;
105 	}
106 
107 	rc = __timer_create(clock_id, sigevp, timerid);
108 
109 	if (sigev_thread) {
110 		if (rc == 0) {
111 			if ((rc = launch_spawner(tcdp)) != 0)
112 				(void) __timer_delete(*timerid);
113 			else
114 				timer_tcd[*timerid] = tcdp;
115 		}
116 		if (rc != 0)
117 			free_sigev_handler(tcdp);
118 	}
119 
120 	return (rc);
121 }
122 
123 int
124 timer_delete(timer_t timerid)
125 {
126 	int rc;
127 
128 	if ((rc = del_sigev_timer(timerid)) == 0)
129 		return (__timer_delete(timerid));
130 	else
131 		return (rc);
132 }
133 
134 int
135 timer_getoverrun(timer_t timerid)
136 {
137 	return (__timer_getoverrun(timerid) + sigev_timer_getoverrun(timerid));
138 }
139 
140 int
141 timer_gettime(timer_t timerid, itimerspec_t *value)
142 {
143 	return (__timer_gettime(timerid, value));
144 }
145 
146 int
147 timer_settime(timer_t timerid, int flags, const itimerspec_t *value,
148 	itimerspec_t *ovalue)
149 {
150 	return (__timer_settime(timerid, flags, value, ovalue));
151 }
152 
153 /*
154  * Cleanup after fork1() in the child process.
155  */
156 void
157 postfork1_child_sigev_timer(void)
158 {
159 	thread_communication_data_t *tcdp;
160 	int timer;
161 
162 	for (timer = 0; timer < timer_max; timer++) {
163 		if ((tcdp = timer_tcd[timer]) != NULL) {
164 			timer_tcd[timer] = NULL;
165 			tcd_teardown(tcdp);
166 		}
167 	}
168 }
169