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; }
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; }
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); }
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; }
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; }
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; }
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; }