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