示例#1
0
LWMsgStatus
lwmsg_assoc_recv_message_transact(
    LWMsgAssoc* assoc,
    LWMsgAssocDispatchFunction dispatch,
    void* data
    )
{
    LWMsgStatus status = LWMSG_STATUS_SUCCESS;
    LWMsgMessage recv_message = LWMSG_MESSAGE_INITIALIZER;
    LWMsgMessage send_message = LWMSG_MESSAGE_INITIALIZER;

    BAIL_ON_ERROR(status = assoc->aclass->recv_msg(assoc, &recv_message));
    BAIL_ON_ERROR(status = dispatch(assoc, &recv_message, &send_message, data));
    BAIL_ON_ERROR(status = assoc->aclass->send_msg(assoc, &send_message));

error:
    
    if (recv_message.tag != -1 && recv_message.data)
    {
        lwmsg_assoc_destroy_message(assoc, &recv_message);
    }

    if (send_message.tag != -1 && send_message.data)
    {
        lwmsg_assoc_destroy_message(assoc, &send_message);
    }

    return status;
}
示例#2
0
static
LWMsgStatus
lwmsg_peer_task_finish_transceive(
    LWMsgPeer* peer,
    PeerAssocTask* task
    )
{
    LWMsgStatus status = LWMSG_STATUS_PENDING;
    LWMsgMessage* message = NULL;

    while ((!task->recv_blocked && task->incoming) ||
           (!task->send_blocked && task->outgoing))
    {
        status = lwmsg_assoc_finish(task->assoc, &message);
        switch (status)
        {
        case LWMSG_STATUS_SUCCESS:
            if (message == &task->incoming_message)
            {
                task->incoming = LWMSG_FALSE;
                task->recv_partial = LWMSG_FALSE;
                BAIL_ON_ERROR(status = lwmsg_peer_task_dispatch_incoming_message(task));
            }
            else if (message == &task->outgoing_message)
            {
                lwmsg_peer_log_message(task, &task->outgoing_message, LWMSG_FALSE);
                task->outgoing = LWMSG_FALSE;
                if (task->destroy_outgoing)
                {
                    lwmsg_assoc_destroy_message(task->assoc, &task->outgoing_message);
                    task->destroy_outgoing = LWMSG_FALSE;
                }
            }
            break;
        case LWMSG_STATUS_PENDING:
            lwmsg_peer_task_update_blocked(task);
            break;
        default:
            BAIL_ON_ERROR(status);
        }
    }

error:

    return status;
}
示例#3
0
static
void
lwmsg_peer_task_delete(
    PeerAssocTask* task
    )
{
    LWMsgHashIter iter = {0};
    PeerCall* call = NULL;
    LWMsgRing* ring = NULL;
    LWMsgRing* next = NULL;

    lwmsg_hash_iter_begin(&task->incoming_calls, &iter);
    while ((call = lwmsg_hash_iter_next(&task->incoming_calls, &iter)))
    {
        lwmsg_hash_remove_entry(&task->incoming_calls, call);
        lwmsg_peer_call_delete(call);
    }
    lwmsg_hash_iter_end(&task->incoming_calls, &iter);

    lwmsg_hash_destroy(&task->incoming_calls);

    lwmsg_hash_iter_begin(&task->outgoing_calls, &iter);
    while ((call = lwmsg_hash_iter_next(&task->outgoing_calls, &iter)))
    {
        lwmsg_hash_remove_entry(&task->outgoing_calls, call);
        lwmsg_peer_call_delete(call);
    }
    lwmsg_hash_iter_end(&task->outgoing_calls, &iter);

    lwmsg_hash_destroy(&task->outgoing_calls);

    for (ring = task->active_incoming_calls.next;
         ring != &task->active_incoming_calls;
         ring = next)
    {
        next = ring->next;
        call = LWMSG_OBJECT_FROM_MEMBER(ring, PeerCall, queue_ring);
        lwmsg_peer_call_delete(call);
    }

    for (ring = task->active_outgoing_calls.next;
         ring != &task->active_outgoing_calls;
         ring = next)
    {
        next = ring->next;
        call = LWMSG_OBJECT_FROM_MEMBER(ring, PeerCall, queue_ring);
        lwmsg_peer_call_delete(call);
    }

    if (task->assoc)
    {
        lwmsg_peer_release_client_slot(task->session->peer);
        lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
        if (task->destroy_outgoing)
        {
            lwmsg_assoc_destroy_message(task->assoc, &task->outgoing_message);
        }
        lwmsg_assoc_delete(task->assoc);
    }

    if (task->event_task)
    {
        lwmsg_task_release(task->event_task);
    }

    if (task->session)
    {
        lwmsg_peer_session_release(task->session);
    }

    free(task);
}
示例#4
0
static
LWMsgStatus
lwmsg_peer_task_run_shutdown(
    LWMsgPeer* peer,
    PeerAssocTask* task,
    LWMsgTaskTrigger trigger,
    LWMsgTaskTrigger* next_trigger,
    LWMsgTime* next_timeout
    )
{
    LWMsgStatus status = LWMSG_STATUS_SUCCESS;
    int fd = CONNECTION_PRIVATE(task->assoc)->fd;

    /* Before we close the assoc, we need to free any data it allocated */

    /* First, make sure any calls are canceled, completed, and freed */
    BAIL_ON_ERROR(status = lwmsg_peer_task_rundown(peer, task, trigger, next_trigger, next_timeout));

    /* Destroy any incoming message we never got around to dispatching */
    lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);

    /* Destroy any outgoing message we never got around to sending */
    if (task->destroy_outgoing)
    {
        lwmsg_assoc_destroy_message(task->assoc, &task->outgoing_message);
    }

    /* We are going to close the fd, so unset it now */
    if (fd >= 0)
    {
        lwmsg_task_unset_trigger_fd(task->event_task, fd);
    }

    switch (task->type)
    {
    case PEER_TASK_BEGIN_CLOSE:
        status = lwmsg_assoc_close(task->assoc);
        break;
    case PEER_TASK_BEGIN_RESET:
        status = lwmsg_assoc_reset(task->assoc);
        break;
    default:
        BAIL_ON_ERROR(status = LWMSG_STATUS_INTERNAL);
    }

    switch (status)
    {
    case LWMSG_STATUS_SUCCESS:
        task->type = PEER_TASK_DROP;
        break;
    case LWMSG_STATUS_PENDING:
        /* Oops, we still need to wait for fd events, so set it on the task again */
        if (fd >= 0)
        {
            BAIL_ON_ERROR(status = lwmsg_task_set_trigger_fd(task->event_task, fd));
        }
        task->type = PEER_TASK_FINISH_CLOSE;
        lwmsg_peer_task_set_timeout(peer, task, &peer->timeout.establish, trigger, next_trigger, next_timeout);
        break;
    default:
        BAIL_ON_ERROR(status);
        break;
    }

done:

    return status;

error:

    goto done;
}
示例#5
0
static
LWMsgStatus
lwmsg_peer_task_rundown(
    LWMsgPeer* peer,
    PeerAssocTask* task,
    LWMsgTaskTrigger trigger,
    LWMsgTaskTrigger* next_trigger,
    LWMsgTime* next_timeout
    )
{
    LWMsgStatus status = LWMSG_STATUS_SUCCESS;
    LWMsgHashIter iter = {0};
    PeerCall* call = NULL;
    LWMsgMessage cancel = LWMSG_MESSAGE_INITIALIZER;
    LWMsgMessage message = LWMSG_MESSAGE_INITIALIZER;

    pthread_mutex_lock(&task->session->lock);

    lwmsg_hash_iter_begin(&task->incoming_calls, &iter);
    while ((call = lwmsg_hash_iter_next(&task->incoming_calls, &iter)))
    {
        if (call->state & PEER_CALL_COMPLETED)
        {
            /* Destroy in call parameters */
            message.tag = call->params.incoming.in.tag;
            message.data = call->params.incoming.in.data;
            lwmsg_assoc_destroy_message(task->assoc, &message);

            /* Destroy out call parameters */
            message.tag = call->params.incoming.out.tag;
            message.data = call->params.incoming.out.data;
            lwmsg_assoc_destroy_message(task->assoc, &message);

            lwmsg_hash_remove_entry(&task->incoming_calls, call);
            lwmsg_peer_call_delete(call);
        }
        else if (!(call->state & PEER_CALL_CANCELLED))
        {
            lwmsg_peer_call_cancel_incoming(call);
        }
    }
    lwmsg_hash_iter_end(&task->incoming_calls, &iter);

    lwmsg_hash_iter_begin(&task->outgoing_calls, &iter);
    while ((call = lwmsg_hash_iter_next(&task->outgoing_calls, &iter)))
    {
        if (task->status)
        {
            cancel.status = task->status;
        }
        else
        {
            cancel.status = LWMSG_STATUS_CANCELLED;
        }
        lwmsg_peer_call_complete_outgoing(call, &cancel);
    }
    lwmsg_hash_iter_end(&task->outgoing_calls, &iter);

    if (lwmsg_hash_get_count(&task->incoming_calls) != 0 ||
        lwmsg_hash_get_count(&task->outgoing_calls) != 0)
    {
        *next_trigger = LWMSG_TASK_TRIGGER_EXPLICIT;
        BAIL_ON_ERROR(status = LWMSG_STATUS_PENDING);
    }

error:

    pthread_mutex_unlock(&task->session->lock);

    return status;
}
示例#6
0
static
LWMsgStatus
lwmsg_peer_task_dispatch_calls(
    PeerAssocTask* task
    )
{
    LWMsgStatus status = LWMSG_STATUS_PENDING;
    PeerCall* call = NULL;
    LWMsgCookie cookie = 0;
    LWMsgMessage message = LWMSG_MESSAGE_INITIALIZER;
    LWMsgRing* ring = NULL;
    LWMsgRing* next = NULL;
    LWMsgSessionString sessid = {0};

    for (ring = task->active_outgoing_calls.next;
         ring != &task->active_outgoing_calls;
         ring = next)
    {
        next = ring->next;
        lwmsg_ring_remove(ring);

        call = LWMSG_OBJECT_FROM_MEMBER(ring, PeerCall, queue_ring);

        /* Undispatched outgoing call -- send request*/
        if (!(call->state & PEER_CALL_DISPATCHED))
        {
            call->state |= PEER_CALL_DISPATCHED;

            lwmsg_message_init(&task->outgoing_message);
            task->outgoing_message.tag = call->params.outgoing.in->tag;
            task->outgoing_message.data = call->params.outgoing.in->data;
            task->outgoing_message.cookie = call->cookie;
            task->outgoing_message.status = call->status;

            lwmsg_peer_log_message(task, &task->outgoing_message, LWMSG_TRUE);
            status = lwmsg_assoc_send_message(task->assoc, &task->outgoing_message);
            switch (status)
            {
            case LWMSG_STATUS_SUCCESS:
                break;
            case LWMSG_STATUS_PENDING:
                task->outgoing = LWMSG_TRUE;
                BAIL_ON_ERROR(status);
                break;
            case LWMSG_STATUS_MALFORMED:
            case LWMSG_STATUS_INVALID_HANDLE:
                /* The association should still be usable, so just fail the call */
                lwmsg_message_init(&message);
                message.status = status;
                message.flags = LWMSG_MESSAGE_FLAG_REPLY | LWMSG_MESSAGE_FLAG_SYNTHETIC;
                message.cookie = call->cookie;
                lwmsg_peer_call_complete_outgoing(call, &message);
                status = LWMSG_STATUS_SUCCESS;
                break;
            default:
                BAIL_ON_ERROR(status);
            }
        }
        /* Cancelled outgoing call -- send cancel request */
        else if ((call->state & PEER_CALL_DISPATCHED) &&
                 (call->state & PEER_CALL_CANCELLED) &&
                 call->status != LWMSG_STATUS_CANCELLED)
        {
            call->status = LWMSG_STATUS_CANCELLED;

            lwmsg_message_init(&task->outgoing_message);
            task->outgoing_message.flags = LWMSG_MESSAGE_FLAG_CONTROL;
            task->outgoing_message.status = LWMSG_STATUS_CANCELLED;
            task->outgoing_message.cookie = call->cookie;

            lwmsg_peer_log_message(task, &task->outgoing_message, LWMSG_TRUE);
            status = lwmsg_assoc_send_message(task->assoc, &task->outgoing_message);
            switch (status)
            {
            case LWMSG_STATUS_SUCCESS:
                break;
            case LWMSG_STATUS_PENDING:
                task->outgoing = LWMSG_TRUE;
                BAIL_ON_ERROR(status);
                break;
            default:
                BAIL_ON_ERROR(status);
            }
        }
    }

    for (ring = task->active_incoming_calls.next;
         ring != &task->active_incoming_calls;
         ring = next)
    {
        next = ring->next;
        lwmsg_ring_remove(ring);

        call = LWMSG_OBJECT_FROM_MEMBER(ring, PeerCall, queue_ring);

        /* Completed incoming call -- send reply */
        if ((call->state & PEER_CALL_COMPLETED) &&
            (call->state & PEER_CALL_DISPATCHED))
        {
            /* Trace call completion */
            if (call->task->session->peer->trace_end)
            {
                call->task->session->peer->trace_end(
                    LWMSG_CALL(call),
                    &call->params.incoming.out,
                    call->status,
                    call->task->session->peer->trace_data);
            }

            lwmsg_message_init(&task->outgoing_message);
            task->outgoing_message.flags = LWMSG_MESSAGE_FLAG_REPLY;
            task->outgoing_message.tag = call->params.incoming.out.tag;
            task->outgoing_message.data = call->params.incoming.out.data;
            task->outgoing_message.cookie = call->cookie;
            task->outgoing_message.status = call->status;

            cookie = call->cookie;

            /* It's now safe to destroy the incoming call parameters */
            message.tag = call->params.incoming.in.tag;
            message.data = call->params.incoming.in.data;
            lwmsg_assoc_destroy_message(task->assoc, &message);

            /* Delete the call structure */
            lwmsg_hash_remove_entry(&task->incoming_calls, call);
            lwmsg_peer_call_delete(call);

            do
            {
                status = lwmsg_assoc_send_message(task->assoc, &task->outgoing_message);
                switch (status)
                {
                case LWMSG_STATUS_SUCCESS:
                    lwmsg_peer_log_message(task, &task->outgoing_message, LWMSG_FALSE);
                    lwmsg_assoc_destroy_message(task->assoc, &task->outgoing_message);
                    break;
                case LWMSG_STATUS_PENDING:
                    task->outgoing = LWMSG_TRUE;
                    task->destroy_outgoing = LWMSG_TRUE;
                    BAIL_ON_ERROR(status);
                    break;
                case LWMSG_STATUS_MALFORMED:
                case LWMSG_STATUS_INVALID_HANDLE:
                case LWMSG_STATUS_OVERFLOW:
                case LWMSG_STATUS_UNDERFLOW:
                    /* Our reply could not be sent because the dispatch function gave us
                       bogus response parameters.  Send a synthetic reply instead so the
                       caller at least knows the call is complete */
                    lwmsg_peer_session_string_for_session((LWMsgSession*) task->session, sessid);
                    LWMSG_LOG_WARNING(task->session->peer->context,
                                      "(session:%s >> %u) Response payload could not be sent: %s",
                                      sessid,
                                      cookie,
                                      lwmsg_status_name(status));
                    lwmsg_assoc_destroy_message(task->assoc, &task->outgoing_message);
                    lwmsg_message_init(&task->outgoing_message);
                    task->outgoing_message.flags = LWMSG_MESSAGE_FLAG_REPLY | LWMSG_MESSAGE_FLAG_SYNTHETIC;
                    task->outgoing_message.status = status;
                    task->outgoing_message.cookie = cookie;
                    status = LWMSG_STATUS_AGAIN;
                    break;
                default:
                    BAIL_ON_ERROR(status);
                }
            } while (status == LWMSG_STATUS_AGAIN);
        }
    }

error:

    return status;
}
示例#7
0
static
LWMsgStatus
lwmsg_peer_task_dispatch_incoming_message(
    PeerAssocTask* task
    )
{
    LWMsgStatus status = LWMSG_STATUS_SUCCESS;
    LWMsgDispatchSpec* spec = NULL;
    LWMsgTag tag = task->incoming_message.tag;
    LWMsgPeer* peer = task->session->peer;
    PeerCall* call = NULL;

    /* If the incoming message is a reply to one of our calls,
       find it and complete it */
    if (task->incoming_message.flags & LWMSG_MESSAGE_FLAG_REPLY)
    {
        lwmsg_peer_task_complete_call(task, task->incoming_message.cookie);
        goto error;
    }
    /* If the incoming message is a control message... */
    else if (task->incoming_message.flags & LWMSG_MESSAGE_FLAG_CONTROL)
    {
        switch (task->incoming_message.status)
        {
        case LWMSG_STATUS_CANCELLED:
            /* The caller cancelled a previous call, so find it and cancel it here as well */
            lwmsg_peer_task_cancel_call(task, task->incoming_message.cookie);
            lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
            goto error;
        default:
            /* Silently drop unknown control messages */
            lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
            goto error;
        }
    }

    lwmsg_peer_log_message(task, &task->incoming_message, LWMSG_FALSE);

    /* Check if this cookie is already in use */
    if (lwmsg_hash_find_key(&task->incoming_calls, &task->incoming_message.cookie))
    {
        /* This association is toast */
        status = lwmsg_peer_task_handle_assoc_error(
            peer,
            task,
            LWMSG_STATUS_MALFORMED);
        lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
        goto error;
    }

    /* Create a call structure to track call */
    BAIL_ON_ERROR(status = lwmsg_peer_call_new(task, &call));
    call->base.is_outgoing = LWMSG_FALSE;
    call->params.incoming.in.tag = LWMSG_TAG_INVALID;
    call->params.incoming.out.tag = LWMSG_TAG_INVALID;

    /* The call structure holds a reference to the session */
    task->refs++;

    /* Set cookie and insert into incoming call table */
    call->cookie = task->incoming_message.cookie;
    lwmsg_hash_insert_entry(&task->incoming_calls, call);

    /* If the incoming message is synthetic for some bizarre reason,
       just echo back the status code to the caller */
    if (task->incoming_message.flags & LWMSG_MESSAGE_FLAG_SYNTHETIC)
    {
        status = lwmsg_peer_task_handle_call_error(
            task,
            call,
            task->incoming_message.status);
        lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
        goto error;
    }

    /* Make sure the tag is within the bounds of the dispatch vector */
    if (tag < 0 || tag >= peer->dispatch.vector_length)
    {
        status = lwmsg_peer_task_handle_call_error(
            task,
            call,
            LWMSG_STATUS_MALFORMED);
        lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
        goto error;
    }

    /* Make sure the call is supported */
    spec = peer->dispatch.vector[tag];
    if (spec == NULL)
    {
        status = lwmsg_peer_task_handle_call_error(
            task,
            call,
            LWMSG_STATUS_UNSUPPORTED);
        lwmsg_assoc_destroy_message(task->assoc, &task->incoming_message);
        goto error;
    }

    /* Dispatch the call */
    status = lwmsg_peer_call_dispatch_incoming(
        call,
        spec,
        peer->dispatch_data,
        &task->incoming_message);
    switch (status)
    {
    case LWMSG_STATUS_SUCCESS:
        break;
    case LWMSG_STATUS_PENDING:
        status = LWMSG_STATUS_SUCCESS;
        break;
    default:
        status = lwmsg_peer_task_handle_call_error(task, call, status);
        BAIL_ON_ERROR(status);
        break;
    }

error:

    return status;
}