Annotation of OSKit-Mach/ipc/ipc_mqueue.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.