]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/mk_timer.c
a9548d2d3882d846854148b12b9484e467203fcb
[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 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
25 *
26 * HISTORY
27 *
28 * 29 June 2000 (debo)
29 * Created.
30 */
31
32 #include <mach/mach_types.h>
33 #include <mach/mach_traps.h>
34 #include <mach/mach_port_server.h>
35
36 #include <mach/mk_timer.h>
37
38 #include <ipc/ipc_space.h>
39
40 #include <kern/mk_timer.h>
41 #include <kern/thread_call.h>
42
43 static zone_t mk_timer_zone;
44
45 static mach_port_qos_t mk_timer_qos = {
46 FALSE, TRUE, 0, sizeof (mk_timer_expire_msg_t)
47 };
48
49 static void mk_timer_expire(
50 void *p0,
51 void *p1);
52
53 mach_port_name_t
54 mk_timer_create_trap(
55 __unused struct mk_timer_create_trap_args *args)
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, timer);
74
75 return (MACH_PORT_NULL);
76 }
77
78 simple_lock_init(&timer->lock, 0);
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, 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 __unused 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, 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_trap(
188 struct mk_timer_destroy_trap_args *args)
189 {
190 mach_port_name_t name = args->name;
191 ipc_space_t myspace = current_space();
192 ipc_port_t port;
193 kern_return_t result;
194
195 result = ipc_port_translate_receive(myspace, name, &port);
196 if (result != KERN_SUCCESS)
197 return (result);
198
199 if (ip_kotype(port) == IKOT_TIMER) {
200 ip_unlock(port);
201 result = mach_port_destroy(myspace, name);
202 }
203 else {
204 ip_unlock(port);
205 result = KERN_INVALID_ARGUMENT;
206 }
207
208 return (result);
209 }
210
211 kern_return_t
212 mk_timer_arm_trap(
213 struct mk_timer_arm_trap_args *args)
214 {
215 mach_port_name_t name = args->name;
216 uint64_t expire_time = args->expire_time;
217 mk_timer_t timer;
218 ipc_space_t myspace = current_space();
219 ipc_port_t port;
220 kern_return_t result;
221
222 result = ipc_port_translate_receive(myspace, name, &port);
223 if (result != KERN_SUCCESS)
224 return (result);
225
226 if (ip_kotype(port) == IKOT_TIMER) {
227 timer = (mk_timer_t)port->ip_kobject;
228 assert(timer != NULL);
229 simple_lock(&timer->lock);
230 assert(timer->port == port);
231 ip_unlock(port);
232
233 if (!timer->is_dead) {
234 timer->is_armed = TRUE;
235
236 if (!thread_call_enter_delayed(&timer->call_entry, expire_time))
237 timer->active++;
238 }
239
240 simple_unlock(&timer->lock);
241 }
242 else {
243 ip_unlock(port);
244 result = KERN_INVALID_ARGUMENT;
245 }
246
247 return (result);
248 }
249
250 kern_return_t
251 mk_timer_cancel_trap(
252 struct mk_timer_cancel_trap_args *args)
253 {
254 mach_port_name_t name = args->name;
255 mach_vm_address_t result_time_addr = args->result_time;
256 uint64_t armed_time = 0;
257 mk_timer_t timer;
258 ipc_space_t myspace = current_space();
259 ipc_port_t port;
260 kern_return_t result;
261
262 result = ipc_port_translate_receive(myspace, name, &port);
263 if (result != KERN_SUCCESS)
264 return (result);
265
266 if (ip_kotype(port) == IKOT_TIMER) {
267 timer = (mk_timer_t)port->ip_kobject;
268 assert(timer != NULL);
269 simple_lock(&timer->lock);
270 assert(timer->port == port);
271 ip_unlock(port);
272
273 if (timer->is_armed) {
274 armed_time = timer->call_entry.deadline;
275 if (thread_call_cancel(&timer->call_entry))
276 timer->active--;
277 timer->is_armed = FALSE;
278 }
279
280 simple_unlock(&timer->lock);
281 }
282 else {
283 ip_unlock(port);
284 result = KERN_INVALID_ARGUMENT;
285 }
286
287 if (result == KERN_SUCCESS)
288 if ( result_time_addr != 0 &&
289 copyout((void *)&armed_time, result_time_addr,
290 sizeof (armed_time)) != 0 )
291 result = KERN_FAILURE;
292
293 return (result);
294 }