Homework 8 Solutions

22C:116, Spring 1997

Ioana M. Lungeanu
Problem 1 part A:

    The body of the RPC Client Stub:

        create(replyEntry, replyQueue);
        send(requestBuf, requestLen, replyEntry, (1,1,1), remote);
        receive(replyQueue, replyBuf, replyLen, replyCount, NULL);

    Comments: The rights mask is (1,1,1) because:
        - remote link must be reusable for the next request.
        - reply link is transferable as a lnk parameter in send.
        - reply entry is duplicatable since it follows a receive.

    The RPC Server Stub:

        create(remoteEntry, client);
        broadcast the remoteEntry to the world
        while(1) {
            receive(client, requestBuf, requestLen, requestCount, replyEntry);
            service(requestBuf, requestCount, replyBuf, replyLen);
            send(replyBuf, replyLen, Null, (0,0,0), replyEntry);
        }

Problem 1 part B:

    First, the client sends the procedure id, the number in input
    arguments m and the number of output arguments n. In the same
    message, it sends the reply link.

    Then, the server creates a new link for incomming parameters.
    This new link is necessary because without it, the server could
    not distinguish messages delivering parameters for one service
    from messages on behalf of other clients.  It sends to the client
    this link, and the client sends the succession of arguments.  If
    the message queues are not really FIFO, each argument can be give
    a serial number, or the server can prompt for successive arguments.

    Then, the server calls the remote procedure and then sends return
    arguments to the client.  Again, if the message queues are not
    FIFO, each reply message must have a serial number or the client
    must prompt for each return argument.

    The following solution uses prompting to sequence the arguments:

    The body of the RPC Client Stub:

        create(replyEntry, replyQueue);
        send({procedureId, m, n}, len, replyEntry, (1,1,1), remote);
        for(i=0; i<m; i++) {
            receive(replyQueue, Null, 0, Null, paramIn);
            send(argumentIn[i], len, replyEntry, (0,0,0), paramIn);
        }
        receive(replyQueue, Null, 0, Null, paramOut);
        for(i=0; i<n; i++) {
            send(Null, 0, replyEntry, (1,1,1), paramOut);
            receive(replyQueue, argumentOut[i], len, count, Null);
        }

    The RPC Server Stub:

        create(remoteEntry, client);
	advertise remoteEntry to the world
        while(1) {
            receive(client, {procedureId, m, n}, len, count, replyEntry);
            create(paramIn, paramInQueue);
            for (i=0; i<m; i++) {
                send(Null, 0, param, (1,1,1), replyEntry);
                receive(paramQueue, argumentIn[i], len, count, Null);
            }
            service(procedureId, m, n, argumentIn, argumentOut);
            create(paramOut, paramOutQueue);
            send(Null, 0, paramOut, (1,1,1), replyEntry);
            for (i=0; i<n; i++) {
                receive(paramOutQueue, Null, 0, Null, Null);
                send(argumentOut[i], len, Null, (0,0,0), replyEntry);
            }
        }

Problem 2:

    The problem is how to make a non-blocking receive in the client,
    because the client must wait for one of two messages: either
    from the RPC server, either from the timr-server. This is solved
    by using the same message queue for both links. Then, receive is
    done on this shared queue. If the message contains no data, then
    it is the timeout, so the request must be retransmitted.  The
    server keeps a flag for each request id, telling if the request
    was serviced or not. When a request comes, it checks to see if it
    is the original request or just a copy.

    The body of the RPC Client Stub:

        create(replyEntry, replyQueue);
        create(timeoutEntry, replyQueue);
        do {
            send(requestBuf, requestLen, replyEntry, (1,1,1), remote);
            send(delay, len, timeoutEntry, (1,1,1), timer);
            receive(replyQueue, replyBuf, replyLen, count, NULL);
        } while(count == 0);

    The RPC Server Stub:

        create(remoteEntry, client);
        while(1) {
            receive(client, requestBuf, requestLen, requestCount, replyEntry);
            if (!serviced[requestBuf.requestId])
                service(requestBuf, requestCount, replyBuf, replyLen);
            send(replyBuf, replyLen, Null, (0,0,0), replyEntry);
        }

Problem 3:

    1   One server for each procedure:

    This scheme is very fast because of the possibility to run different
    procedures in parallel. However, if the procedures have side efects,
    because they alter shared data, then race conditions occur, which are
    unwnated. So, these scheme is very good if procedures don't share
    data.

    2   One server for all procedures:

    This scheme is slower than the first because, only one procedure may
    be executed at a time. But this eliminates the race conditions in the
    case of shared data between procedures. There is no difference from the
    shared data point of view between the scheme 2 and 3. Scheme 2 (with
    a single queue) may be a little faster that scheme 3 (with multiple
    queues), because the multi-receive primitive has to poll each queue,
    and this takes time. However, scheme 3 may be easier to use, because
    the procedure is specified by the queue number.

    In effect, this forces the procedures to be run with implicit mutual
    exclusion unless a multithreaded server is used.

    3   Procedures represent methods of an object:

    This is a clear example of data sharing. Scheme 2 and 3 are recommended,
    because they eliminate race conditions.  For objects with a single
    server per object, mutual exclusion between methods becomes natural;
    this is usually desirable and matches the semantics of Hoare's monitors.
    If large numbers of small objects must be supported, this requires a
    proliferation of servers!

    Also, every time an object is created, a new RPC server would have to
    be created if one server represents one object.

    4   Procedures represent methods of a class of objects:

    If one server supports all members of a class, object creation involves
    a message to that server, and that server holds all objects of that class
    inside itself.  This is probably inappropriate for large objects, but if
    large numbers of small objects are involved, it imposes a minimum
    overhead per object.

Problem 4:

    When a client calls a remote procedure in the server, for each
    parameter b passed by name or by refence it sends to the server
    two thunks: b_set() and b_get().

    For each assignment to b in the remote procedure, the server
    calls b_set() to be executed in the client, and for each evaluation
    of b in the remote procedure, the server calls b_get() to be
    executed in the client.

    In other words, the client becomes an RPC server for the server,
    providing for each parameter 2 procedures.