Annotation of OSKit-Mach/ipc/ipc_mqueue.c, revision 1.1.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.