]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/mk_timer.c
xnu-517.3.15.tar.gz
[apple/xnu.git] / osfmk / kern / mk_timer.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
27 *
28 * HISTORY
29 *
30 * 29 June 2000 (debo)
31 * Created.
32 */
33
34 #include <mach/mach_types.h>
35 #include <mach/mach_port_server.h>
36
37 #include <mach/mk_timer.h>
38
39 #include <ipc/ipc_space.h>
40
41 #include <kern/mk_timer.h>
42 #include <kern/thread_call.h>
43
44 static zone_t mk_timer_zone;
45
46 static mach_port_qos_t mk_timer_qos = {
47 FALSE, TRUE, 0, sizeof (mk_timer_expire_msg_t)
48 };
49
50 static void mk_timer_expire(
51 void *p0,
52 void *p1);
53
54 mach_port_name_t
55 mk_timer_create(void)
56 {
57 mk_timer_t timer;
58 ipc_space_t myspace = current_space();
59 mach_port_name_t name = MACH_PORT_NULL;
60 ipc_port_t port;
61 kern_return_t result;
62
63 timer = (mk_timer_t)zalloc(mk_timer_zone);
64 if (timer == NULL)
65 return (MACH_PORT_NULL);
66
67 result = mach_port_allocate_qos(myspace, MACH_PORT_RIGHT_RECEIVE,
68 &mk_timer_qos, &name);
69 if (result == KERN_SUCCESS)
70 result = ipc_port_translate_receive(myspace, name, &port);
71
72 if (result != KERN_SUCCESS) {
73 zfree(mk_timer_zone, (vm_offset_t)timer);
74
75 return (MACH_PORT_NULL);
76 }
77
78 simple_lock_init(&timer->lock, ETAP_MISC_TIMER);
79 call_entry_setup(&timer->call_entry, mk_timer_expire, timer);
80 timer->is_armed = timer->is_dead = FALSE;
81 timer->active = 0;
82
83 timer->port = port;
84 ipc_kobject_set_atomically(port, (ipc_kobject_t)timer, IKOT_TIMER);
85
86 port->ip_srights++;
87 ip_reference(port);
88 ip_unlock(port);
89
90 return (name);
91 }
92
93 void
94 mk_timer_port_destroy(
95 ipc_port_t port)
96 {
97 mk_timer_t timer = NULL;
98
99 ip_lock(port);
100 if (ip_kotype(port) == IKOT_TIMER) {
101 timer = (mk_timer_t)port->ip_kobject;
102 assert(timer != NULL);
103 ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE);
104 simple_lock(&timer->lock);
105 assert(timer->port == port);
106 }
107 ip_unlock(port);
108
109 if (timer != NULL) {
110 if (thread_call_cancel(&timer->call_entry))
111 timer->active--;
112 timer->is_armed = FALSE;
113
114 timer->is_dead = TRUE;
115 if (timer->active == 0) {
116 simple_unlock(&timer->lock);
117 zfree(mk_timer_zone, (vm_offset_t)timer);
118
119 ipc_port_release_send(port);
120 return;
121 }
122
123 simple_unlock(&timer->lock);
124 }
125 }
126
127 void
128 mk_timer_init(void)
129 {
130 int s = sizeof (mk_timer_data_t);
131
132 assert(!(mk_timer_zone != NULL));
133
134 mk_timer_zone = zinit(s, (4096 * s), (16 * s), "mk_timer");
135 }
136
137 static void
138 mk_timer_expire(
139 void *p0,
140 void *p1)
141 {
142 mk_timer_t timer = p0;
143 ipc_port_t port;
144
145 simple_lock(&timer->lock);
146
147 if (timer->active > 1) {
148 timer->active--;
149 simple_unlock(&timer->lock);
150 return;
151 }
152
153 port = timer->port;
154 assert(port != IP_NULL);
155 assert(timer->active == 1);
156
157 while (timer->is_armed && timer->active == 1) {
158 mk_timer_expire_msg_t msg;
159
160 timer->is_armed = FALSE;
161 simple_unlock(&timer->lock);
162
163 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
164 msg.header.msgh_remote_port = port;
165 msg.header.msgh_local_port = MACH_PORT_NULL;
166 msg.header.msgh_reserved = msg.header.msgh_id = 0;
167
168 msg.unused[0] = msg.unused[1] = msg.unused[2] = 0;
169
170 (void) mach_msg_send_from_kernel(&msg.header, sizeof (msg));
171
172 simple_lock(&timer->lock);
173 }
174
175 if (--timer->active == 0 && timer->is_dead) {
176 simple_unlock(&timer->lock);
177 zfree(mk_timer_zone, (vm_offset_t)timer);
178
179 ipc_port_release_send(port);
180 return;
181 }
182
183 simple_unlock(&timer->lock);
184 }
185
186 kern_return_t
187 mk_timer_destroy(
188 mach_port_name_t name)
189 {
190 ipc_space_t myspace = current_space();
191 ipc_port_t port;
192 kern_return_t result;
193
194 result = ipc_port_translate_receive(myspace, name, &port);
195 if (result != KERN_SUCCESS)
196 return (result);
197
198 if (ip_kotype(port) == IKOT_TIMER) {
199 ip_unlock(port);
200 result = mach_port_destroy(myspace, name);
201 }
202 else {
203 ip_unlock(port);
204 result = KERN_INVALID_ARGUMENT;
205 }
206
207 return (result);
208 }
209
210 kern_return_t
211 mk_timer_arm(
212 mach_port_name_t name,
213 uint64_t expire_time)
214 {
215 uint64_t time_of_arming;
216 mk_timer_t timer;
217 ipc_space_t myspace = current_space();
218 ipc_port_t port;
219 kern_return_t result;
220
221 clock_get_uptime(&time_of_arming);
222
223 result = ipc_port_translate_receive(myspace, name, &port);
224 if (result != KERN_SUCCESS)
225 return (result);
226
227 if (ip_kotype(port) == IKOT_TIMER) {
228 timer = (mk_timer_t)port->ip_kobject;
229 assert(timer != NULL);
230 simple_lock(&timer->lock);
231 assert(timer->port == port);
232 ip_unlock(port);
233
234 if (!timer->is_dead) {
235 timer->time_of_arming = time_of_arming;
236 timer->is_armed = TRUE;
237
238 if (!thread_call_enter_delayed(&timer->call_entry, expire_time))
239 timer->active++;
240 }
241
242 simple_unlock(&timer->lock);
243 }
244 else {
245 ip_unlock(port);
246 result = KERN_INVALID_ARGUMENT;
247 }
248
249 return (result);
250 }
251
252 kern_return_t
253 mk_timer_cancel(
254 mach_port_name_t name,
255 uint64_t *result_time)
256 {
257 uint64_t armed_time = 0;
258 mk_timer_t timer;
259 ipc_space_t myspace = current_space();
260 ipc_port_t port;
261 kern_return_t result;
262
263 result = ipc_port_translate_receive(myspace, name, &port);
264 if (result != KERN_SUCCESS)
265 return (result);
266
267 if (ip_kotype(port) == IKOT_TIMER) {
268 timer = (mk_timer_t)port->ip_kobject;
269 assert(timer != NULL);
270 simple_lock(&timer->lock);
271 assert(timer->port == port);
272 ip_unlock(port);
273
274 if (timer->is_armed) {
275 armed_time = timer->call_entry.deadline;
276 if (thread_call_cancel(&timer->call_entry))
277 timer->active--;
278 timer->is_armed = FALSE;
279 }
280
281 simple_unlock(&timer->lock);
282 }
283 else {
284 ip_unlock(port);
285 result = KERN_INVALID_ARGUMENT;
286 }
287
288 if (result == KERN_SUCCESS)
289 if ( result_time != NULL &&
290 copyout((void *)&armed_time, (void *)result_time,
291 sizeof (armed_time)) != 0 )
292 result = KERN_FAILURE;
293
294 return (result);
295 }