]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/mk_timer.c
da3c9258370878cffe449611090825e82974441e
[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_initialize(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 uint64_t time_of_posting;
143 mk_timer_t timer = p0;
144 ipc_port_t port;
145
146 clock_get_uptime(&time_of_posting);
147
148 simple_lock(&timer->lock);
149
150 if (timer->active > 1) {
151 timer->active--;
152 simple_unlock(&timer->lock);
153 return;
154 }
155
156 port = timer->port;
157 assert(port != IP_NULL);
158
159 while ( timer->is_armed &&
160 !thread_call_is_delayed(&timer->call_entry, NULL) ) {
161 mk_timer_expire_msg_t msg;
162
163 timer->is_armed = FALSE;
164
165 msg.time_of_arming = timer->time_of_arming;
166 msg.armed_time = timer->call_entry.deadline;
167 msg.time_of_posting = time_of_posting;
168
169 simple_unlock(&timer->lock);
170
171 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
172 msg.header.msgh_remote_port = port;
173 msg.header.msgh_local_port = MACH_PORT_NULL;
174 msg.header.msgh_reserved = msg.header.msgh_id = 0;
175
176 (void) mach_msg_send_from_kernel(&msg.header, sizeof (msg));
177
178 simple_lock(&timer->lock);
179 }
180
181 if (--timer->active == 0 && timer->is_dead) {
182 simple_unlock(&timer->lock);
183 zfree(mk_timer_zone, (vm_offset_t)timer);
184
185 ipc_port_release_send(port);
186 return;
187 }
188
189 simple_unlock(&timer->lock);
190 }
191
192 kern_return_t
193 mk_timer_destroy(
194 mach_port_name_t name)
195 {
196 ipc_space_t myspace = current_space();
197 ipc_port_t port;
198 kern_return_t result;
199
200 result = ipc_port_translate_receive(myspace, name, &port);
201 if (result != KERN_SUCCESS)
202 return (result);
203
204 if (ip_kotype(port) == IKOT_TIMER) {
205 ip_unlock(port);
206 result = mach_port_destroy(myspace, name);
207 }
208 else {
209 ip_unlock(port);
210 result = KERN_INVALID_ARGUMENT;
211 }
212
213 return (result);
214 }
215
216 kern_return_t
217 mk_timer_arm(
218 mach_port_name_t name,
219 uint64_t expire_time)
220 {
221 uint64_t time_of_arming;
222 mk_timer_t timer;
223 ipc_space_t myspace = current_space();
224 ipc_port_t port;
225 kern_return_t result;
226
227 clock_get_uptime(&time_of_arming);
228
229 result = ipc_port_translate_receive(myspace, name, &port);
230 if (result != KERN_SUCCESS)
231 return (result);
232
233 if (ip_kotype(port) == IKOT_TIMER) {
234 timer = (mk_timer_t)port->ip_kobject;
235 assert(timer != NULL);
236 simple_lock(&timer->lock);
237 assert(timer->port == port);
238 ip_unlock(port);
239
240 timer->time_of_arming = time_of_arming;
241 timer->is_armed = TRUE;
242
243 if (!thread_call_enter_delayed(&timer->call_entry, expire_time))
244 timer->active++;
245 simple_unlock(&timer->lock);
246 }
247 else {
248 ip_unlock(port);
249 result = KERN_INVALID_ARGUMENT;
250 }
251
252 return (result);
253 }
254
255 kern_return_t
256 mk_timer_cancel(
257 mach_port_name_t name,
258 uint64_t *result_time)
259 {
260 uint64_t armed_time = 0;
261 mk_timer_t timer;
262 ipc_space_t myspace = current_space();
263 ipc_port_t port;
264 kern_return_t result;
265
266 result = ipc_port_translate_receive(myspace, name, &port);
267 if (result != KERN_SUCCESS)
268 return (result);
269
270 if (ip_kotype(port) == IKOT_TIMER) {
271 timer = (mk_timer_t)port->ip_kobject;
272 assert(timer != NULL);
273 simple_lock(&timer->lock);
274 assert(timer->port == port);
275 ip_unlock(port);
276
277 if (timer->is_armed) {
278 armed_time = timer->call_entry.deadline;
279 if (thread_call_cancel(&timer->call_entry))
280 timer->active--;
281 timer->is_armed = FALSE;
282 }
283
284 simple_unlock(&timer->lock);
285 }
286 else {
287 ip_unlock(port);
288 result = KERN_INVALID_ARGUMENT;
289 }
290
291 if (result == KERN_SUCCESS)
292 if ( result_time != NULL &&
293 copyout((void *)&armed_time, (void *)result_time,
294 sizeof (armed_time)) != 0 )
295 result = KERN_FAILURE;
296
297 return (result);
298 }