X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fmeshlink.c;h=1118b8a5e6b31b5d24cda596b66117099fea661a;hb=26fdd4fc9d2a2cc12b0118c3061a65ab3f3ee6c4;hp=4569c9f58fb2cdae484b3b316c1eba044daa636a;hpb=1b0f134888b1d6c9acda938fb654cd4dfd295167;p=meshlink diff --git a/src/meshlink.c b/src/meshlink.c index 4569c9f5..1118b8a5 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -3455,16 +3455,46 @@ static bool channel_pre_accept(struct utcp *utcp, uint16_t port) { return mesh->channel_accept_cb; } -static void aio_signal(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_aio_buffer_t *aio) { - if(aio->data) { - if(aio->cb.buffer) { - aio->cb.buffer(mesh, channel, aio->data, aio->len, aio->priv); +/* Finish one AIO buffer, return true if the channel is still open. */ +static bool aio_finish_one(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_aio_buffer_t **head) { + meshlink_aio_buffer_t *aio = *head; + *head = aio->next; + + if(channel->c) { + channel->in_callback = true; + + if(aio->data) { + if(aio->cb.buffer) { + aio->cb.buffer(mesh, channel, aio->data, aio->len, aio->priv); + } + } else { + if(aio->cb.fd) { + aio->cb.fd(mesh, channel, aio->fd, aio->done, aio->priv); + } } - } else { - if(aio->cb.fd) { - aio->cb.fd(mesh, channel, aio->fd, aio->done, aio->priv); + + channel->in_callback = false; + + if(!channel->c) { + free(aio); + free(channel); + return false; + } + } + + free(aio); + return true; +} + +/* Finish all AIO buffers, return true if the channel is still open. */ +static bool aio_abort(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_aio_buffer_t **head) { + while(*head) { + if(!aio_finish_one(mesh, channel, head)) { + return false; } } + + return true; } static ssize_t channel_recv(struct utcp_connection *connection, const void *data, size_t len) { @@ -3486,6 +3516,15 @@ static ssize_t channel_recv(struct utcp_connection *connection, const void *data size_t left = len; while(channel->aio_receive) { + if(!len) { + /* This receive callback signalled an error, abort all outstanding AIO buffers. */ + if(!aio_abort(mesh, channel, &channel->aio_receive)) { + return len; + } + + break; + } + meshlink_aio_buffer_t *aio = channel->aio_receive; size_t todo = aio->len - aio->done; @@ -3498,23 +3537,31 @@ static ssize_t channel_recv(struct utcp_connection *connection, const void *data } else { ssize_t result = write(aio->fd, p, todo); - if(result > 0) { - todo = result; + if(result <= 0) { + /* Writing to fd failed, cancel just this AIO buffer. */ + logger(mesh, MESHLINK_ERROR, "Writing to AIO fd %d failed: %s", aio->fd, strerror(errno)); + + if(!aio_finish_one(mesh, channel, &channel->aio_receive)) { + return len; + } + + continue; } + + todo = result; } aio->done += todo; + p += todo; + left -= todo; if(aio->done == aio->len) { - channel->aio_receive = aio->next; - aio_signal(mesh, channel, aio); - free(aio); + if(!aio_finish_one(mesh, channel, &channel->aio_receive)) { + return len; + } } - p += todo; - left -= todo; - - if(!left && len) { + if(!left) { return len; } } @@ -3601,57 +3648,85 @@ static void channel_poll(struct utcp_connection *connection, size_t len) { node_t *n = channel->node; meshlink_handle_t *mesh = n->mesh; - meshlink_aio_buffer_t *aio = channel->aio_send; - if(aio) { - /* We at least one AIO buffer. Send as much as possible form the first buffer. */ - size_t left = aio->len - aio->done; + while(channel->aio_send) { + if(!len) { + /* This poll callback signalled an error, abort all outstanding AIO buffers. */ + if(!aio_abort(mesh, channel, &channel->aio_send)) { + return; + } + + break; + } + + /* We have at least one AIO buffer. Send as much as possible from the buffers. */ + meshlink_aio_buffer_t *aio = channel->aio_send; + size_t todo = aio->len - aio->done; ssize_t sent; - if(len > left) { - len = left; + if(todo > len) { + todo = len; } if(aio->data) { - sent = utcp_send(connection, (char *)aio->data + aio->done, len); + sent = utcp_send(connection, (char *)aio->data + aio->done, todo); } else { - char buf[65536]; - size_t todo = utcp_get_sndbuf_free(connection); + char buf[todo]; + ssize_t result = read(aio->fd, buf, todo); - if(todo > left) { - todo = left; - } + if(result > 0) { + todo = result; + sent = utcp_send(connection, buf, todo); + } else { + /* Reading from fd failed, cancel just this AIO buffer. */ + if(result != 0) { + logger(mesh, MESHLINK_ERROR, "Reading from AIO fd %d failed: %s", aio->fd, strerror(errno)); + } - if(todo > sizeof(buf)) { - todo = sizeof(buf); + if(!aio_finish_one(mesh, channel, &channel->aio_send)) { + return; + } + + continue; } + } - ssize_t result = read(aio->fd, buf, todo); + if(sent != (ssize_t)todo) { + /* We should never get a partial send at this point */ + assert(sent < 0); - if(result > 0) { - sent = utcp_send(connection, buf, result); - } else { - sent = result; + /* Sending failed, abort all outstanding AIO buffers and send a poll callback. */ + if(!aio_abort(mesh, channel, &channel->aio_send)) { + return; } + + len = 0; + break; } - if(sent >= 0) { - aio->done += sent; + aio->done += sent; + len -= sent; + + /* If we didn't finish this buffer, exit early. */ + if(aio->done < aio->len) { + return; } - /* If the buffer is now completely sent, call the callback and dispose of it. */ - if(aio->done >= aio->len) { - channel->aio_send = aio->next; - aio_signal(mesh, channel, aio); - free(aio); + /* Signal completion of this buffer, and go to the next one. */ + if(!aio_finish_one(mesh, channel, &channel->aio_send)) { + return; } - } else { - if(channel->poll_cb) { - channel->poll_cb(mesh, channel, len); - } else { - utcp_set_poll_cb(connection, NULL); + + if(!len) { + return; } } + + if(channel->poll_cb) { + channel->poll_cb(mesh, channel, len); + } else { + utcp_set_poll_cb(connection, NULL); + } } void meshlink_set_channel_poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_poll_cb_t cb) { @@ -3791,24 +3866,20 @@ void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *channel pthread_mutex_lock(&mesh->mutex); - utcp_close(channel->c); + if(channel->c) { + utcp_close(channel->c); + channel->c = NULL; - /* Clean up any outstanding AIO buffers. */ - for(meshlink_aio_buffer_t *aio = channel->aio_send, *next; aio; aio = next) { - next = aio->next; - aio_signal(mesh, channel, aio); - free(aio); + /* Clean up any outstanding AIO buffers. */ + aio_abort(mesh, channel, &channel->aio_send); + aio_abort(mesh, channel, &channel->aio_receive); } - for(meshlink_aio_buffer_t *aio = channel->aio_receive, *next; aio; aio = next) { - next = aio->next; - aio_signal(mesh, channel, aio); - free(aio); + if(!channel->in_callback) { + free(channel); } pthread_mutex_unlock(&mesh->mutex); - - free(channel); } ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) { @@ -3881,7 +3952,11 @@ bool meshlink_channel_aio_send(meshlink_handle_t *mesh, meshlink_channel_t *chan /* Ensure the poll callback is set, and call it right now to push data if possible */ utcp_set_poll_cb(channel->c, channel_poll); - channel_poll(channel->c, len); + size_t todo = MIN(len, utcp_get_rcvbuf_free(channel->c)); + + if(todo) { + channel_poll(channel->c, todo); + } pthread_mutex_unlock(&mesh->mutex); @@ -3918,7 +3993,11 @@ bool meshlink_channel_aio_fd_send(meshlink_handle_t *mesh, meshlink_channel_t *c /* Ensure the poll callback is set, and call it right now to push data if possible */ utcp_set_poll_cb(channel->c, channel_poll); - channel_poll(channel->c, len); + size_t left = utcp_get_rcvbuf_free(channel->c); + + if(left) { + channel_poll(channel->c, left); + } pthread_mutex_unlock(&mesh->mutex);