|
|
1.1 root 1: /*
2: * Mach Operating System
3: * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
4: * Copyright (c) 1993,1994 The University of Utah and
5: * the Computer Systems Laboratory (CSL).
6: * All rights reserved.
7: *
8: * Permission to use, copy, modify and distribute this software and its
9: * documentation is hereby granted, provided that both the copyright
10: * notice and this permission notice appear in all copies of the
11: * software, derivative works or modified versions, and any portions
12: * thereof, and that both notices appear in supporting documentation.
13: *
14: * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
15: * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
16: * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
17: * THIS SOFTWARE.
18: *
19: * Carnegie Mellon requests users of this software to return to
20: *
21: * Software Distribution Coordinator or [email protected]
22: * School of Computer Science
23: * Carnegie Mellon University
24: * Pittsburgh PA 15213-3890
25: *
26: * any improvements or extensions that they make and grant Carnegie Mellon
27: * the rights to redistribute these changes.
28: */
29: /*
30: * File: ipc/ipc_mqueue.c
31: * Author: Rich Draves
32: * Date: 1989
33: *
34: * Functions to manipulate IPC message queues.
35: */
36:
37: #include <norma_ipc.h>
38:
39: #include <mach/port.h>
40: #include <mach/message.h>
41: #include <kern/assert.h>
42: #include <kern/counters.h>
43: #include <kern/sched_prim.h>
44: #include <kern/ipc_sched.h>
45: #include <kern/ipc_kobject.h>
46: #include <ipc/ipc_mqueue.h>
47: #include <ipc/ipc_thread.h>
48: #include <ipc/ipc_kmsg.h>
49: #include <ipc/ipc_port.h>
50: #include <ipc/ipc_pset.h>
51: #include <ipc/ipc_space.h>
52: #include <ipc/ipc_marequest.h>
53:
54:
55:
56: #if NORMA_IPC
57: extern ipc_mqueue_t norma_ipc_handoff_mqueue;
58: extern ipc_kmsg_t norma_ipc_handoff_msg;
59: extern mach_msg_size_t norma_ipc_handoff_max_size;
60: extern mach_msg_size_t norma_ipc_handoff_msg_size;
61: extern ipc_kmsg_t norma_ipc_kmsg_accept();
62: #endif /* NORMA_IPC */
63:
64: /*
65: * Routine: ipc_mqueue_init
66: * Purpose:
67: * Initialize a newly-allocated message queue.
68: */
69:
70: void
71: ipc_mqueue_init(
72: ipc_mqueue_t mqueue)
73: {
74: imq_lock_init(mqueue);
75: ipc_kmsg_queue_init(&mqueue->imq_messages);
76: ipc_thread_queue_init(&mqueue->imq_threads);
77: }
78:
79: /*
80: * Routine: ipc_mqueue_move
81: * Purpose:
82: * Move messages from one queue (source) to another (dest).
83: * Only moves messages sent to the specified port.
84: * Conditions:
85: * Both queues must be locked.
86: * (This is sufficient to manipulate port->ip_seqno.)
87: */
88:
89: void
90: ipc_mqueue_move(
91: ipc_mqueue_t dest,
92: ipc_mqueue_t source,
93: ipc_port_t port)
94: {
95: ipc_kmsg_queue_t oldq, newq;
96: ipc_thread_queue_t blockedq;
97: ipc_kmsg_t kmsg, next;
98: ipc_thread_t th;
99:
100: oldq = &source->imq_messages;
101: newq = &dest->imq_messages;
102: blockedq = &dest->imq_threads;
103:
104: for (kmsg = ipc_kmsg_queue_first(oldq);
105: kmsg != IKM_NULL; kmsg = next) {
106: next = ipc_kmsg_queue_next(oldq, kmsg);
107:
108: /* only move messages sent to port */
109:
110: if (kmsg->ikm_header.msgh_remote_port != (mach_port_t) port)
111: continue;
112:
113: ipc_kmsg_rmqueue(oldq, kmsg);
114:
115: /* before adding kmsg to newq, check for a blocked receiver */
116:
117: while ((th = ipc_thread_dequeue(blockedq)) != ITH_NULL) {
118: assert(ipc_kmsg_queue_empty(newq));
119:
120: thread_go(th);
121:
122: /* check if the receiver can handle the message */
123:
124: if (kmsg->ikm_header.msgh_size <= th->ith_msize) {
125: th->ith_state = MACH_MSG_SUCCESS;
126: th->ith_kmsg = kmsg;
127: th->ith_seqno = port->ip_seqno++;
128:
129: goto next_kmsg;
130: }
131:
132: th->ith_state = MACH_RCV_TOO_LARGE;
133: th->ith_msize = kmsg->ikm_header.msgh_size;
134: }
135:
136: /* didn't find a receiver to handle the message */
137:
138: ipc_kmsg_enqueue(newq, kmsg);
139: next_kmsg:;
140: }
141: }
142:
143: /*
144: * Routine: ipc_mqueue_changed
145: * Purpose:
146: * Wake up receivers waiting in a message queue.
147: * Conditions:
148: * The message queue is locked.
149: */
150:
151: void
152: ipc_mqueue_changed(
153: ipc_mqueue_t mqueue,
154: mach_msg_return_t mr)
155: {
156: ipc_thread_t th;
157:
158: while ((th = ipc_thread_dequeue(&mqueue->imq_threads)) != ITH_NULL) {
159: th->ith_state = mr;
160: thread_go(th);
161: }
162: }
163:
164: /*
165: * Routine: ipc_mqueue_send
166: * Purpose:
167: * Send a message to a port. The message holds a reference
168: * for the destination port in the msgh_remote_port field.
169: *
170: * If unsuccessful, the caller still has possession of
171: * the message and must do something with it. If successful,
172: * the message is queued, given to a receiver, destroyed,
173: * or handled directly by the kernel via mach_msg.
174: * Conditions:
175: * Nothing locked.
176: * Returns:
177: * MACH_MSG_SUCCESS The message was accepted.
178: * MACH_SEND_TIMED_OUT Caller still has message.
179: * MACH_SEND_INTERRUPTED Caller still has message.
180: */
181:
182: mach_msg_return_t
183: ipc_mqueue_send(kmsg, option, time_out)
184: ipc_kmsg_t kmsg;
185: mach_msg_option_t option;
186: mach_msg_timeout_t time_out;
187: {
188: ipc_port_t port;
189:
190: port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
191: assert(IP_VALID(port));
192:
193: ip_lock(port);
194:
195: if (port->ip_receiver == ipc_space_kernel) {
196: ipc_kmsg_t reply;
197:
198: /*
199: * We can check ip_receiver == ipc_space_kernel
200: * before checking that the port is active because
201: * ipc_port_dealloc_kernel clears ip_receiver
202: * before destroying a kernel port.
203: */
204:
205: assert(ip_active(port));
206: ip_unlock(port);
207:
208: reply = ipc_kobject_server(kmsg);
209: if (reply != IKM_NULL)
210: ipc_mqueue_send_always(reply);
211:
212: return MACH_MSG_SUCCESS;
213: }
214:
215: #if NORMA_IPC
216: if (IP_NORMA_IS_PROXY(port)) {
217: mach_msg_return_t mr;
218:
219: mr = norma_ipc_send(kmsg);
220: ip_unlock(port);
221: return mr;
222: }
223: #endif /* NORMA_IPC */
224:
225: for (;;) {
226: ipc_thread_t self;
227:
228: /*
229: * Can't deliver to a dead port.
230: * However, we can pretend it got sent
231: * and was then immediately destroyed.
232: */
233:
234: if (!ip_active(port)) {
235: /*
236: * We can't let ipc_kmsg_destroy deallocate
237: * the port right, because we might end up
238: * in an infinite loop trying to deliver
239: * a send-once notification.
240: */
241:
242: ip_release(port);
243: ip_check_unlock(port);
244: kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
245: #if NORMA_IPC
246: /* XXX until ipc_kmsg_destroy is fixed... */
247: norma_ipc_finish_receiving(&kmsg);
248: #endif /* NORMA_IPC */
249: ipc_kmsg_destroy(kmsg);
250: return MACH_MSG_SUCCESS;
251: }
252:
253: /*
254: * Don't block if:
255: * 1) We're under the queue limit.
256: * 2) Caller used the MACH_SEND_ALWAYS internal option.
257: * 3) Message is sent to a send-once right.
258: */
259:
260: if ((port->ip_msgcount < port->ip_qlimit) ||
261: (option & MACH_SEND_ALWAYS) ||
262: (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
263: MACH_MSG_TYPE_PORT_SEND_ONCE))
264: break;
265:
266: /* must block waiting for queue to clear */
267:
268: self = current_thread();
269:
270: if (option & MACH_SEND_TIMEOUT) {
271: if (time_out == 0) {
272: ip_unlock(port);
273: return MACH_SEND_TIMED_OUT;
274: }
275:
276: thread_will_wait_with_timeout(self, time_out);
277: } else
278: thread_will_wait(self);
279:
280: ipc_thread_enqueue(&port->ip_blocked, self);
281: self->ith_state = MACH_SEND_IN_PROGRESS;
282:
283: ip_unlock(port);
284: counter(c_ipc_mqueue_send_block++);
285: thread_block((void (*)(void)) 0);
286: ip_lock(port);
287:
288: /* why did we wake up? */
289:
290: if (self->ith_state == MACH_MSG_SUCCESS)
291: continue;
292: assert(self->ith_state == MACH_SEND_IN_PROGRESS);
293:
294: /* take ourselves off blocked queue */
295:
296: ipc_thread_rmqueue(&port->ip_blocked, self);
297:
298: /*
299: * Thread wakeup-reason field tells us why
300: * the wait was interrupted.
301: */
302:
303: switch (self->ith_wait_result) {
304: case THREAD_INTERRUPTED:
305: /* send was interrupted - give up */
306:
307: ip_unlock(port);
308: return MACH_SEND_INTERRUPTED;
309:
310: case THREAD_TIMED_OUT:
311: /* timeout expired */
312:
313: assert(option & MACH_SEND_TIMEOUT);
314: time_out = 0;
315: break;
316:
317: case THREAD_RESTART:
318: default:
319: #if MACH_ASSERT
320: assert(!"ipc_mqueue_send");
321: #else
322: panic("ipc_mqueue_send");
323: #endif
324: }
325: }
326:
327: if (kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) {
328: ip_unlock(port);
329:
330: /* don't allow the creation of a circular loop */
331:
332: #if NORMA_IPC
333: /* XXX until ipc_kmsg_destroy is fixed... */
334: norma_ipc_finish_receiving(&kmsg);
335: #endif /* NORMA_IPC */
336: ipc_kmsg_destroy(kmsg);
337: return MACH_MSG_SUCCESS;
338: }
339:
340: {
341: ipc_mqueue_t mqueue;
342: ipc_pset_t pset;
343: ipc_thread_t receiver;
344: ipc_thread_queue_t receivers;
345:
346: port->ip_msgcount++;
347: assert(port->ip_msgcount > 0);
348:
349: pset = port->ip_pset;
350: if (pset == IPS_NULL)
351: mqueue = &port->ip_messages;
352: else
353: mqueue = &pset->ips_messages;
354:
355: imq_lock(mqueue);
356: receivers = &mqueue->imq_threads;
357:
358: /*
359: * Can unlock the port now that the msg queue is locked
360: * and we know the port is active. While the msg queue
361: * is locked, we have control of the kmsg, so the ref in
362: * it for the port is still good. If the msg queue is in
363: * a set (dead or alive), then we're OK because the port
364: * is still a member of the set and the set won't go away
365: * until the port is taken out, which tries to lock the
366: * set's msg queue to remove the port's msgs.
367: */
368:
369: ip_unlock(port);
370:
371: /* check for a receiver for the message */
372:
373: #if NORMA_IPC
374: if (mqueue == norma_ipc_handoff_mqueue) {
375: norma_ipc_handoff_msg = kmsg;
376: if (kmsg->ikm_header.msgh_size <= norma_ipc_handoff_max_size) {
377: imq_unlock(mqueue);
378: return MACH_MSG_SUCCESS;
379: }
380: norma_ipc_handoff_msg_size = kmsg->ikm_header.msgh_size;
381: }
382: #endif /* NORMA_IPC */
383: for (;;) {
384: receiver = ipc_thread_queue_first(receivers);
385: if (receiver == ITH_NULL) {
386: /* no receivers; queue kmsg */
387:
388: ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg);
389: imq_unlock(mqueue);
390: break;
391: }
392:
393: ipc_thread_rmqueue_first_macro(receivers, receiver);
394: assert(ipc_kmsg_queue_empty(&mqueue->imq_messages));
395:
396: if (kmsg->ikm_header.msgh_size <= receiver->ith_msize) {
397: /* got a successful receiver */
398:
399: receiver->ith_state = MACH_MSG_SUCCESS;
400: receiver->ith_kmsg = kmsg;
401: receiver->ith_seqno = port->ip_seqno++;
402: imq_unlock(mqueue);
403:
404: thread_go(receiver);
405: break;
406: }
407:
408: receiver->ith_state = MACH_RCV_TOO_LARGE;
409: receiver->ith_msize = kmsg->ikm_header.msgh_size;
410: thread_go(receiver);
411: }
412: }
413:
414: return MACH_MSG_SUCCESS;
415: }
416:
417: /*
418: * Routine: ipc_mqueue_copyin
419: * Purpose:
420: * Convert a name in a space to a message queue.
421: * Conditions:
422: * Nothing locked. If successful, the message queue
423: * is returned locked and caller gets a ref for the object.
424: * This ref ensures the continued existence of the queue.
425: * Returns:
426: * MACH_MSG_SUCCESS Found a message queue.
427: * MACH_RCV_INVALID_NAME The space is dead.
428: * MACH_RCV_INVALID_NAME The name doesn't denote a right.
429: * MACH_RCV_INVALID_NAME
430: * The denoted right is not receive or port set.
431: * MACH_RCV_IN_SET Receive right is a member of a set.
432: */
433:
434: mach_msg_return_t
435: ipc_mqueue_copyin(
436: ipc_space_t space,
437: mach_port_t name,
438: ipc_mqueue_t *mqueuep,
439: ipc_object_t *objectp)
440: {
441: ipc_entry_t entry;
442: ipc_entry_bits_t bits;
443: ipc_object_t object;
444: ipc_mqueue_t mqueue;
445:
446: is_read_lock(space);
447: if (!space->is_active) {
448: is_read_unlock(space);
449: return MACH_RCV_INVALID_NAME;
450: }
451:
452: entry = ipc_entry_lookup(space, name);
453: if (entry == IE_NULL) {
454: is_read_unlock(space);
455: return MACH_RCV_INVALID_NAME;
456: }
457:
458: bits = entry->ie_bits;
459: object = entry->ie_object;
460:
461: if (bits & MACH_PORT_TYPE_RECEIVE) {
462: ipc_port_t port;
463: ipc_pset_t pset;
464:
465: port = (ipc_port_t) object;
466: assert(port != IP_NULL);
467:
468: ip_lock(port);
469: assert(ip_active(port));
470: assert(port->ip_receiver_name == name);
471: assert(port->ip_receiver == space);
472: is_read_unlock(space);
473:
474: pset = port->ip_pset;
475: if (pset != IPS_NULL) {
476: ips_lock(pset);
477: if (ips_active(pset)) {
478: ips_unlock(pset);
479: ip_unlock(port);
480: return MACH_RCV_IN_SET;
481: }
482:
483: ipc_pset_remove(pset, port);
484: ips_check_unlock(pset);
485: assert(port->ip_pset == IPS_NULL);
486: }
487:
488: mqueue = &port->ip_messages;
489: } else if (bits & MACH_PORT_TYPE_PORT_SET) {
490: ipc_pset_t pset;
491:
492: pset = (ipc_pset_t) object;
493: assert(pset != IPS_NULL);
494:
495: ips_lock(pset);
496: assert(ips_active(pset));
497: assert(pset->ips_local_name == name);
498: is_read_unlock(space);
499:
500: mqueue = &pset->ips_messages;
501: } else {
502: is_read_unlock(space);
503: return MACH_RCV_INVALID_NAME;
504: }
505:
506: /*
507: * At this point, the object is locked and active,
508: * the space is unlocked, and mqueue is initialized.
509: */
510:
511: io_reference(object);
512: imq_lock(mqueue);
513: io_unlock(object);
514:
515: *objectp = object;
516: *mqueuep = mqueue;
517: return MACH_MSG_SUCCESS;
518: }
519:
520: /*
521: * Routine: ipc_mqueue_receive
522: * Purpose:
523: * Receive a message from a message queue.
524: *
525: * If continuation is non-zero, then we might discard
526: * our kernel stack when we block. We will continue
527: * after unblocking by executing continuation.
528: *
529: * If resume is true, then we are resuming a receive
530: * operation after a blocked receive discarded our stack.
531: * Conditions:
532: * The message queue is locked; it will be returned unlocked.
533: *
534: * Our caller must hold a reference for the port or port set
535: * to which this queue belongs, to keep the queue
536: * from being deallocated. Furthermore, the port or set
537: * must have been active when the queue was locked.
538: *
539: * The kmsg is returned with clean header fields
540: * and with the circular bit turned off.
541: * Returns:
542: * MACH_MSG_SUCCESS Message returned in kmsgp.
543: * MACH_RCV_TOO_LARGE Message size returned in kmsgp.
544: * MACH_RCV_TIMED_OUT No message obtained.
545: * MACH_RCV_INTERRUPTED No message obtained.
546: * MACH_RCV_PORT_DIED Port/set died; no message.
547: * MACH_RCV_PORT_CHANGED Port moved into set; no msg.
548: *
549: */
550:
551: mach_msg_return_t
552: ipc_mqueue_receive(
553: ipc_mqueue_t mqueue,
554: mach_msg_option_t option,
555: mach_msg_size_t max_size,
556: mach_msg_timeout_t time_out,
557: boolean_t resume,
558: void (*continuation)(void),
559: ipc_kmsg_t *kmsgp,
560: mach_port_seqno_t *seqnop)
561: {
562: ipc_port_t port;
563: ipc_kmsg_t kmsg;
564: mach_port_seqno_t seqno;
565:
566: {
567: ipc_kmsg_queue_t kmsgs = &mqueue->imq_messages;
568: ipc_thread_t self = current_thread();
569:
570: if (resume)
571: goto after_thread_block;
572:
573: for (;;) {
574: kmsg = ipc_kmsg_queue_first(kmsgs);
575: #if NORMA_IPC
576: /*
577: * It may be possible to make this work even when a timeout
578: * is specified.
579: *
580: * Netipc_replenish should be moved somewhere else.
581: */
582: if (kmsg == IKM_NULL && ! (option & MACH_RCV_TIMEOUT)) {
583: netipc_replenish(FALSE);
584: *kmsgp = IKM_NULL;
585: kmsg = norma_ipc_kmsg_accept(mqueue, max_size,
586: (mach_msg_size_t *)kmsgp);
587: if (kmsg != IKM_NULL) {
588: port = (ipc_port_t)
589: kmsg->ikm_header.msgh_remote_port;
590: seqno = port->ip_seqno++;
591: break;
592: }
593: if (*kmsgp) {
594: imq_unlock(mqueue);
595: return MACH_RCV_TOO_LARGE;
596: }
597: }
598: #endif /* NORMA_IPC */
599: if (kmsg != IKM_NULL) {
600: /* check space requirements */
601:
602: if (kmsg->ikm_header.msgh_size > max_size) {
603: * (mach_msg_size_t *) kmsgp =
604: kmsg->ikm_header.msgh_size;
605: imq_unlock(mqueue);
606: return MACH_RCV_TOO_LARGE;
607: }
608:
609: ipc_kmsg_rmqueue_first_macro(kmsgs, kmsg);
610: port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
611: seqno = port->ip_seqno++;
612: break;
613: }
614:
615: /* must block waiting for a message */
616:
617: if (option & MACH_RCV_TIMEOUT) {
618: if (time_out == 0) {
619: imq_unlock(mqueue);
620: return MACH_RCV_TIMED_OUT;
621: }
622:
623: thread_will_wait_with_timeout(self, time_out);
624: } else
625: thread_will_wait(self);
626:
627: ipc_thread_enqueue_macro(&mqueue->imq_threads, self);
628: self->ith_state = MACH_RCV_IN_PROGRESS;
629: self->ith_msize = max_size;
630:
631: imq_unlock(mqueue);
632: if (continuation != (void (*)(void)) 0) {
633: counter(c_ipc_mqueue_receive_block_user++);
634: } else {
635: counter(c_ipc_mqueue_receive_block_kernel++);
636: }
637: thread_block(continuation);
638: after_thread_block:
639: imq_lock(mqueue);
640:
641: /* why did we wake up? */
642:
643: if (self->ith_state == MACH_MSG_SUCCESS) {
644: /* pick up the message that was handed to us */
645:
646: kmsg = self->ith_kmsg;
647: seqno = self->ith_seqno;
648: port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
649: break;
650: }
651:
652: switch (self->ith_state) {
653: case MACH_RCV_TOO_LARGE:
654: /* pick up size of the too-large message */
655:
656: * (mach_msg_size_t *) kmsgp = self->ith_msize;
657: /* fall-through */
658:
659: case MACH_RCV_PORT_DIED:
660: case MACH_RCV_PORT_CHANGED:
661: /* something bad happened to the port/set */
662:
663: imq_unlock(mqueue);
664: return self->ith_state;
665:
666: case MACH_RCV_IN_PROGRESS:
667: /*
668: * Awakened for other than IPC completion.
669: * Remove ourselves from the waiting queue,
670: * then check the wakeup cause.
671: */
672:
673: ipc_thread_rmqueue(&mqueue->imq_threads, self);
674:
675: switch (self->ith_wait_result) {
676: case THREAD_INTERRUPTED:
677: /* receive was interrupted - give up */
678:
679: imq_unlock(mqueue);
680: return MACH_RCV_INTERRUPTED;
681:
682: case THREAD_TIMED_OUT:
683: /* timeout expired */
684:
685: assert(option & MACH_RCV_TIMEOUT);
686: time_out = 0;
687: break;
688:
689: case THREAD_RESTART:
690: default:
691: #if MACH_ASSERT
692: assert(!"ipc_mqueue_receive");
693: #else
694: panic("ipc_mqueue_receive");
695: #endif
696: }
697: break;
698:
699: default:
700: #if MACH_ASSERT
701: assert(!"ipc_mqueue_receive: strange ith_state");
702: #else
703: panic("ipc_mqueue_receive: strange ith_state");
704: #endif
705: }
706: }
707:
708: /* we have a kmsg; unlock the msg queue */
709:
710: imq_unlock(mqueue);
711: assert(kmsg->ikm_header.msgh_size <= max_size);
712: }
713:
714: {
715: ipc_marequest_t marequest;
716:
717: marequest = kmsg->ikm_marequest;
718: if (marequest != IMAR_NULL) {
719: ipc_marequest_destroy(marequest);
720: kmsg->ikm_marequest = IMAR_NULL;
721: }
722: assert((kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) == 0);
723:
724: assert(port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port);
725: ip_lock(port);
726:
727: if (ip_active(port)) {
728: ipc_thread_queue_t senders;
729: ipc_thread_t sender;
730:
731: assert(port->ip_msgcount > 0);
732: port->ip_msgcount--;
733:
734: senders = &port->ip_blocked;
735: sender = ipc_thread_queue_first(senders);
736:
737: if ((sender != ITH_NULL) &&
738: (port->ip_msgcount < port->ip_qlimit)) {
739: ipc_thread_rmqueue(senders, sender);
740: sender->ith_state = MACH_MSG_SUCCESS;
741: thread_go(sender);
742: }
743: }
744:
745: ip_unlock(port);
746: }
747:
748: #if NORMA_IPC
749: norma_ipc_finish_receiving(&kmsg);
750: #endif /* NORMA_IPC */
751: *kmsgp = kmsg;
752: *seqnop = seqno;
753: return MACH_MSG_SUCCESS;
754: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.