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) {
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;
} else {
ssize_t result = write(aio->fd, p, todo);
- if(result > 0) {
- todo = result;
+ if(result <= 0) {
+ if(result < 0 && errno == EINTR) {
+ continue;
+ }
+
+ /* 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;
}
}
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 {
+ if(result < 0 && errno == EINTR) {
+ continue;
+ }
+
+ /* 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(!aio_finish_one(mesh, channel, &channel->aio_send)) {
+ return;
+ }
- if(todo > sizeof(buf)) {
- todo = sizeof(buf);
+ 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) {
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) {
/* 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);
/* 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);