diff options
-rw-r--r-- | android/async-io-common.h | 61 | ||||
-rw-r--r-- | android/async-socket-connector.c | 121 | ||||
-rw-r--r-- | android/async-socket-connector.h | 107 | ||||
-rw-r--r-- | android/async-socket.c | 402 | ||||
-rw-r--r-- | android/async-socket.h | 273 |
5 files changed, 527 insertions, 437 deletions
diff --git a/android/async-io-common.h b/android/async-io-common.h new file mode 100644 index 0000000..02714a7 --- /dev/null +++ b/android/async-io-common.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_ASYNC_IO_COMMON_H_ +#define ANDROID_ASYNC_IO_COMMON_H_ + +/* + * Contains declarations common for asynchronous socket I/O + */ + +/* Enumerates asynchronous I/O states. + * Values from this enum are passed to callbacks associated with an I/O, + * indicating at what state the I/O is. */ +typedef enum AsyncIOState { + /* Asynchronous I/O has been queued. (0) */ + ASIO_STATE_QUEUED, + /* Asynchronous I/O has started. This state indicates that I/O has been + * performed for the first time. (1) */ + ASIO_STATE_STARTED, + /* Asynchronous I/O is continuing. This state indicates that I/O has been + * invoked for the second (or more) time. (2) */ + ASIO_STATE_CONTINUES, + /* Asynchronous I/O is about to be retried. (3) */ + ASIO_STATE_RETRYING, + /* Asynchronous I/O has been successfuly completed. (4) */ + ASIO_STATE_SUCCEEDED, + /* Asynchronous I/O has failed. (5) */ + ASIO_STATE_FAILED, + /* Asynchronous I/O has timed out. (6) */ + ASIO_STATE_TIMED_OUT, + /* Asynchronous I/O has been cancelled (due to disconnect, for + * instance). (7) */ + ASIO_STATE_CANCELLED, +} AsyncIOState; + +/* Enumerates actions to perform with an I/O on state transition. + * Values from this enum are returned from async I/O callbacks, indicating what + * action should be performed with the I/O by I/O handler. */ +typedef enum AsyncIOAction { + /* I/O is done. Perform default action depending on I/O type. */ + ASIO_ACTION_DONE, + /* Abort the I/O. */ + ASIO_ACTION_ABORT, + /* Retry the I/O. */ + ASIO_ACTION_RETRY, +} AsyncIOAction; + +#endif /* ANDROID_ASYNC_IO_COMMON_H_ */ diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c index daf6a37..41a13e0 100644 --- a/android/async-socket-connector.c +++ b/android/async-socket-connector.c @@ -47,7 +47,7 @@ struct AsyncSocketConnector { LoopTimer connector_timer[1]; /* Asynchronous connector to the socket. */ AsyncConnector connector[1]; - /* Callback to invoke on connection / connection error. */ + /* Callback to invoke on connection events. */ asc_event_cb on_connected_cb; /* An opaque parameter to pass to the connection callback. */ void* on_connected_cb_opaque; @@ -106,8 +106,8 @@ _async_socket_connector_open_socket(AsyncSocketConnector* connector) /* Open socket. */ connector->fd = socket_create_inet(SOCKET_STREAM); if (connector->fd < 0) { - D("Unable to create connector socket for %s. Error: %s", - _asc_socket_string(connector), strerror(errno)); + D("Unable to create AsyncSocketConnector socket for '%s'. Error: %d -> %s", + _asc_socket_string(connector), errno, strerror(errno)); return -1; } @@ -148,45 +148,38 @@ static void _on_async_socket_connector_connecting(AsyncSocketConnector* connector, AsyncStatus status) { - ASCCbRes action; - int do_retry = 0; + AsyncIOAction action = ASIO_ACTION_DONE; switch (status) { case ASYNC_COMPLETE: loopIo_done(connector->connector_io); - D("Socket %s is connected", _asc_socket_string(connector)); + D("Socket '%s' is connected", _asc_socket_string(connector)); /* Invoke "on connected" callback */ action = connector->on_connected_cb(connector->on_connected_cb_opaque, - connector, ASC_CONNECTION_SUCCEEDED); - if (action == ASC_CB_RETRY) { - do_retry = 1; - } else if (action == ASC_CB_ABORT) { - _async_socket_connector_close_socket(connector); - } + connector, ASIO_STATE_SUCCEEDED); break; case ASYNC_ERROR: loopIo_done(connector->connector_io); - D("Error %d while connecting to socket %s: %s", - errno, _asc_socket_string(connector), strerror(errno)); + D("Error while connecting to socket '%s': %d -> %s", + _asc_socket_string(connector), errno, strerror(errno)); /* Invoke "on connected" callback */ action = connector->on_connected_cb(connector->on_connected_cb_opaque, - connector, ASC_CONNECTION_FAILED); - if (action == ASC_CB_RETRY) { - do_retry = 1; - } else if (action == ASC_CB_ABORT) { - _async_socket_connector_close_socket(connector); - } + connector, ASIO_STATE_FAILED); break; case ASYNC_NEED_MORE: return; } - if (do_retry) { - D("Retrying connection to socket %s", _asc_socket_string(connector)); + if (action == ASIO_ACTION_RETRY) { + D("Retrying connection to socket '%s'", _asc_socket_string(connector)); loopTimer_startRelative(connector->connector_timer, connector->retry_to); } else { + if (action == ASIO_ACTION_ABORT) { + D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", + __FUNCTION__, _asc_socket_string(connector)); + } _async_socket_connector_free(connector); } } @@ -196,9 +189,19 @@ _on_async_socket_connector_io(void* opaque, int fd, unsigned events) { AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; - /* Complete socket connection. */ - const AsyncStatus status = asyncConnector_run(connector->connector); - _on_async_socket_connector_connecting(connector, status); + /* Notify the client that another connection attempt is about to start. */ + const AsyncIOAction action = + connector->on_connected_cb(connector->on_connected_cb_opaque, + connector, ASIO_STATE_CONTINUES); + if (action != ASIO_ACTION_ABORT) { + /* Complete socket connection. */ + const AsyncStatus status = asyncConnector_run(connector->connector); + _on_async_socket_connector_connecting(connector, status); + } else { + D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", + __FUNCTION__, _asc_socket_string(connector)); + _async_socket_connector_free(connector); + } } /* Retry connection timer callback. @@ -208,31 +211,33 @@ _on_async_socket_connector_io(void* opaque, int fd, unsigned events) static void _on_async_socket_connector_retry(void* opaque) { + AsyncStatus status; AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; /* Invoke the callback to notify about a connection retry attempt. */ - const ASCCbRes action = + AsyncIOAction action = connector->on_connected_cb(connector->on_connected_cb_opaque, - connector, ASC_CONNECTION_RETRY); - - if (action == ASC_CB_RETRY) { - AsyncStatus status; + connector, ASIO_STATE_RETRYING); - /* Close handle opened for the previous (failed) attempt. */ - _async_socket_connector_close_socket(connector); + if (action == ASIO_ACTION_ABORT) { + D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", + __FUNCTION__, _asc_socket_string(connector)); + _async_socket_connector_free(connector); + return; + } - /* Retry connection attempt. */ - if (_async_socket_connector_open_socket(connector) == 0) { - status = asyncConnector_init(connector->connector, &connector->address, - connector->connector_io); - } else { - status = ASYNC_ERROR; - } + /* Close handle opened for the previous (failed) attempt. */ + _async_socket_connector_close_socket(connector); - _on_async_socket_connector_connecting(connector, status); + /* Retry connection attempt. */ + if (_async_socket_connector_open_socket(connector) == 0) { + status = asyncConnector_init(connector->connector, &connector->address, + connector->connector_io); } else { - _async_socket_connector_free(connector); + status = ASYNC_ERROR; } + + _on_async_socket_connector_connecting(connector, status); } /******************************************************************************** @@ -248,7 +253,7 @@ async_socket_connector_new(const SockAddress* address, AsyncSocketConnector* connector; if (cb == NULL) { - W("No callback for AsyncSocketConnector for %s", + W("No callback for AsyncSocketConnector for socket '%s'", sock_address_to_string(address)); errno = EINVAL; return NULL; @@ -275,8 +280,9 @@ async_socket_connector_new(const SockAddress* address, loopTimer_init(connector->connector_timer, connector->looper, _on_async_socket_connector_retry, connector); } else { - E("Unable to create I/O looper for asynchronous connector to socket %s", + E("Unable to create I/O looper for AsyncSocketConnector for socket '%s'", _asc_socket_string(connector)); + cb(cb_opaque, connector, ASIO_STATE_FAILED); _async_socket_connector_free(connector); return NULL; } @@ -284,31 +290,30 @@ async_socket_connector_new(const SockAddress* address, return connector; } -ASCConnectRes +void async_socket_connector_connect(AsyncSocketConnector* connector) { AsyncStatus status; if (_async_socket_connector_open_socket(connector) == 0) { - status = asyncConnector_init(connector->connector, &connector->address, - connector->connector_io); + const AsyncIOAction action = + connector->on_connected_cb(connector->on_connected_cb_opaque, + connector, ASIO_STATE_STARTED); + if (action == ASIO_ACTION_ABORT) { + D("%s: AsyncSocketConnector client for socket '%s' has aborted connection", + __FUNCTION__, _asc_socket_string(connector)); + _async_socket_connector_free(connector); + return; + } else { + status = asyncConnector_init(connector->connector, + &connector->address, + connector->connector_io); + } } else { status = ASYNC_ERROR; } _on_async_socket_connector_connecting(connector, status); - - switch (status) { - case ASYNC_COMPLETE: - return ASC_CONNECT_SUCCEEDED; - - case ASYNC_ERROR: - return ASC_CONNECT_FAILED; - - case ASYNC_NEED_MORE: - default: - return ASC_CONNECT_IN_PROGRESS; - } } int diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h index 998d0c8..4c05059 100644 --- a/android/async-socket-connector.h +++ b/android/async-socket-connector.h @@ -17,15 +17,17 @@ #ifndef ANDROID_ASYNC_SOCKET_CONNECTOR_H_ #define ANDROID_ASYNC_SOCKET_CONNECTOR_H_ +#include "android/async-io-common.h" + /* * Contains declaration of an API that allows asynchronous connection to a * socket with retries. * * The typical usage of this API is as such: * - * 1. The client creates an async connector instance by calling async_socket_connector_new - * routine, supplying there address of the socket to connect, and a callback - * to invoke on connection events. + * 1. The client creates an asynchronous connector instance by calling + * async_socket_connector_new routine, supplying there address of the socket + * to connect, and a callback to invoke on connection events. * 2. The client then proceeds with calling async_socket_connector_connect that * would initiate connection attempts. * @@ -38,94 +40,58 @@ * 2. Failure. * 3. Retry. * - * Typically, when client's callback is called for successful connection, the + * Typically, when client's callback is called for a successful connection, the * client will pull connected socket's FD from the connector, and then this FD - * will be used by the client for I/O on the connected socket. If socket's FD - * is pulled by the client, it must return ASC_CB_KEEP from the callback. + * will be used by the client for I/O on the connected socket. * - * When client's callback is invoked with an error (ASC_CONNECTION_FAILED event), - * the client has an opportunity to review the error (available in 'errno'), and - * either abort the connection by returning ASC_CB_ABORT, or schedule a retry - * by returning ASC_CB_RETRY from the callback. If client returns ASC_CB_ABORT + * When client's callback is invoked with an error (ASIO_STATE_FAILED event), the + * client has an opportunity to review the error (available in 'errno'), and + * either abort the connection by returning ASIO_ACTION_ABORT, or schedule a retry + * by returning ASIO_ACTION_RETRY from the callback. If client returns ASIO_ACTION_ABORT * from the callback, the connector will stop connection attempts, and will - * self-destruct. If ASC_CB_RETRY is returned from the callback, the connector + * self-destruct. If ASIO_ACTION_RETRY is returned from the callback, the connector * will retry connection attempt after timeout that was set by the caller in the * call to async_socket_connector_new routine. * - * When client's callback is invoked with ASC_CONNECTION_RETRY, the client has an - * opportunity to cancel further connection attempts by returning ASC_CB_ABORT, - * or it can allow another connection attempt by returning ASC_CB_RETRY. + * When client's callback is invoked with ASIO_STATE_RETRYING (indicating that + * connector is about to retry a connection attempt), the client has an opportunity + * to cancel further connection attempts by returning ASIO_ACTION_ABORT, or it + * can allow another connection attempt by returning ASIO_ACTION_RETRY. * * The client has no control over the lifespan of initialized connector instance. * It always self-destructs after client's cllback returns with a status other - * than ASC_CB_RETRY. + * than ASIO_ACTION_RETRY. */ /* Declares async socket connector descriptor. */ typedef struct AsyncSocketConnector AsyncSocketConnector; -/* Enumerates connection events. - * Values from this enum are passed to the callback that connector's client uses - * to monitor connection status / progress. - */ -typedef enum ASCEvent { - /* Connection with the socket has been successfuly established. */ - ASC_CONNECTION_SUCCEEDED, - /* A failure has occured while establising connection, with errno containing - * the actual error. */ - ASC_CONNECTION_FAILED, - /* Async socket connector is about to retry the connection. */ - ASC_CONNECTION_RETRY, -} ASCEvent; - -/* Enumerates return values from the callback to the connector's client. - */ -typedef enum ASCCbRes { - /* Keep established connection. */ - ASC_CB_KEEP, - /* Abort connection attempts. */ - ASC_CB_ABORT, - /* Retry connection attempt. */ - ASC_CB_RETRY, -} ASCCbRes; - -/* Enumerates values returned from the connector routine. - */ -typedef enum ASCConnectRes { - /* Connection has succeeded in the connector routine. */ - ASC_CONNECT_SUCCEEDED, - /* Connection has failed in the connector routine. */ - ASC_CONNECT_FAILED, - /* Connection is in progress, and will be completed asynchronously. */ - ASC_CONNECT_IN_PROGRESS, -} ASCConnectRes; - /* Declares callback that connector's client uses to monitor connection * status / progress. * Param: * opaque - An opaque pointer associated with the client. - * connector - Connector instance for thecallback. - * event - Event that has occured. If event is set to ASC_CONNECTION_FAILED, + * connector - AsyncSocketConnector instance. + * event - Event that has occurred. If event is set to ASIO_STATE_FAILED, * errno contains connection error. * Return: - * One of ASCCbRes values. + * One of AsyncIOAction values. */ -typedef ASCCbRes (*asc_event_cb)(void* opaque, - AsyncSocketConnector* connector, - ASCEvent event); +typedef AsyncIOAction (*asc_event_cb)(void* opaque, + AsyncSocketConnector* connector, + AsyncIOState event); /* Creates and initializes AsyncSocketConnector instance. * Param: * address - Initialized socket address to connect to. - * retry_to - Retry timeout in milliseconds. + * retry_to - Timeout to retry a failed connection attempt in milliseconds. * cb, cb_opaque - Callback to invoke on connection events. This callback is * required, and must not be NULL. * Return: * Initialized AsyncSocketConnector instance. Note that AsyncSocketConnector * instance returned from this routine will be destroyed by the connector itself, - * when its work on connecting to the socket is completed. Typically, the - * connector wil destroy the descriptor after client's callback routine returns - * with the status other than ASC_CB_RETRY. + * when its work on connecting to the socket is completed. Typically, connector + * will destroy its descriptor after client's callback routine returns with a + * status other than ASIO_ACTION_RETRY. */ extern AsyncSocketConnector* async_socket_connector_new(const SockAddress* address, int retry_to, @@ -133,21 +99,20 @@ extern AsyncSocketConnector* async_socket_connector_new(const SockAddress* addre void* cb_opaque); /* Initiates asynchronous connection. + * Note that connection result will be reported via callback set with the call to + * async_socket_connector_new routine. * Param: - * connector - Initialized AsyncSocketConnector instance. - * Return: - * Status indicating state of the connection: completed, failed, or in progress. - * Note that the connector will always invoke a callback passed to the - * async_socket_connector_new routine prior to exiting from this routine with - * statuses other ASC_CONNECT_IN_PROGRESS. + * connector - Initialized AsyncSocketConnector instance. Note that this + * connector descriptor might be destroyed asynchronously, before this + * routine returns. */ -extern ASCConnectRes async_socket_connector_connect(AsyncSocketConnector* connector); +extern void async_socket_connector_connect(AsyncSocketConnector* connector); /* Pulls socket's file descriptor from the connector. * This routine should be called from the connection callback on successful - * connection status. This will provide the connector's client with operational - * socket FD, and at the same time this will tell the connector not to close - * the FD when connector descriptor gets destroyed. + * connection status. This will provide the connector's client with an operational + * socket FD, and at the same time this will tell the connector not to close the + * FD when connector descriptor gets destroyed. * Param: * connector - Initialized AsyncSocketConnector instance. * Return: diff --git a/android/async-socket.c b/android/async-socket.c index 13e15ca..4be69f6 100644 --- a/android/async-socket.c +++ b/android/async-socket.c @@ -37,9 +37,6 @@ * Asynchronous Socket internal API declarations *******************************************************************************/ -/* Asynchronous socket I/O (reader, or writer) descriptor. */ -typedef struct AsyncSocketIO AsyncSocketIO; - /* Gets socket's address string. */ static const char* _async_socket_string(AsyncSocket* as); @@ -48,10 +45,11 @@ static Looper* _async_socket_get_looper(AsyncSocket* as); /* Handler for the I/O time out. * Param: - * as - Asynchronous socket for the reader. + * as - Asynchronous socket for the I/O. * asio - Desciptor for the timed out I/O. */ -static void _async_socket_io_timed_out(AsyncSocket* as, AsyncSocketIO* asio); +static AsyncIOAction _async_socket_io_timed_out(AsyncSocket* as, + AsyncSocketIO* asio); /******************************************************************************** * Asynchronous Socket Reader / Writer @@ -72,10 +70,12 @@ struct AsyncSocketIO { uint32_t to_transfer; /* Bytes thransferred through the socket in this I/O. */ uint32_t transferred; - /* I/O callbacks for this I/O. */ - const ASIOCb* io_cb; + /* I/O callback for this I/O. */ + on_as_io_cb on_io; /* I/O type selector: 1 - read, 0 - write. */ int is_io_read; + /* State of the I/O. */ + AsyncIOState state; }; /* @@ -105,7 +105,7 @@ static void _on_async_socket_io_timed_out(void* opaque); * as - Asynchronous socket for the I/O. * is_io_read - I/O type selector: 1 - read, 0 - write. * buffer, len - Reader / writer buffer address. - * io_cb - Callbacks for this reader / writer. + * io_cb - Callback for this reader / writer. * io_opaque - An opaque pointer associated with the I/O. * deadline - Deadline to complete the I/O. * Return: @@ -116,7 +116,7 @@ _async_socket_rw_new(AsyncSocket* as, int is_io_read, void* buffer, uint32_t len, - const ASIOCb* io_cb, + on_as_io_cb io_cb, void* io_opaque, Duration deadline) { @@ -137,8 +137,9 @@ _async_socket_rw_new(AsyncSocket* as, asio->buffer = (uint8_t*)buffer; asio->to_transfer = len; asio->transferred = 0; - asio->io_cb = io_cb; + asio->on_io = io_cb; asio->io_opaque = io_opaque; + asio->state = ASIO_STATE_QUEUED; loopTimer_init(asio->timer, _async_socket_get_looper(as), _on_async_socket_io_timed_out, asio); @@ -168,7 +169,7 @@ _async_socket_io_destroy(AsyncSocketIO* asio) * Param: * as - Asynchronous socket for the reader. * buffer, len - Reader's buffer. - * io_cb - Lists reader's callbacks. + * io_cb - Reader's callback. * reader_opaque - An opaque pointer associated with the reader. * deadline - Deadline to complete the operation. * Return: @@ -178,7 +179,7 @@ static AsyncSocketIO* _async_socket_reader_new(AsyncSocket* as, void* buffer, uint32_t len, - const ASIOCb* io_cb, + on_as_io_cb io_cb, void* reader_opaque, Duration deadline) { @@ -191,7 +192,7 @@ _async_socket_reader_new(AsyncSocket* as, * Param: * as - Asynchronous socket for the writer. * buffer, len - Writer's buffer. - * io_cb - Lists writer's callbacks. + * io_cb - Writer's callback. * writer_opaque - An opaque pointer associated with the writer. * deadline - Deadline to complete the operation. * Return: @@ -201,7 +202,7 @@ static AsyncSocketIO* _async_socket_writer_new(AsyncSocket* as, const void* buffer, uint32_t len, - const ASIOCb* io_cb, + on_as_io_cb io_cb, void* writer_opaque, Duration deadline) { @@ -216,8 +217,76 @@ static void _on_async_socket_io_timed_out(void* opaque) { AsyncSocketIO* const asio = (AsyncSocketIO*)opaque; - _async_socket_io_timed_out(asio->as, asio); - _async_socket_io_destroy(asio); + const AsyncIOAction action = _async_socket_io_timed_out(asio->as, asio); + if (action != ASIO_ACTION_RETRY) { + _async_socket_io_destroy(asio); + } +} + +/******************************************************************************** + * Public Asynchronous Socket I/O API + *******************************************************************************/ + +AsyncSocket* +async_socket_io_get_socket(const AsyncSocketIO* asio) +{ + return asio->as; +} + +void +async_socket_io_cancel_time_out(AsyncSocketIO* asio) +{ + loopTimer_stop(asio->timer); +} + +void* +async_socket_io_get_io_opaque(const AsyncSocketIO* asio) +{ + return asio->io_opaque; +} + +void* +async_socket_io_get_client_opaque(const AsyncSocketIO* asio) +{ + return async_socket_get_client_opaque(asio->as); +} + +void* +async_socket_io_get_buffer_info(const AsyncSocketIO* asio, + uint32_t* transferred, + uint32_t* to_transfer) +{ + if (transferred != NULL) { + *transferred = asio->transferred; + } + if (to_transfer != NULL) { + *to_transfer = asio->to_transfer; + } + return asio->buffer; +} + +void* +async_socket_io_get_buffer(const AsyncSocketIO* asio) +{ + return asio->buffer; +} + +uint32_t +async_socket_io_get_transferred(const AsyncSocketIO* asio) +{ + return asio->transferred; +} + +uint32_t +async_socket_io_get_to_transfer(const AsyncSocketIO* asio) +{ + return asio->to_transfer; +} + +int +async_socket_io_is_read(const AsyncSocketIO* asio) +{ + return asio->is_io_read; } /******************************************************************************** @@ -227,8 +296,8 @@ _on_async_socket_io_timed_out(void* opaque) struct AsyncSocket { /* TCP address for the socket. */ SockAddress address; - /* Client callbacks for this socket. */ - const ASClientCb* client_cb; + /* Connection callback for this socket. */ + on_as_connection_cb on_connection; /* An opaque pointer associated with this socket by the client. */ void* client_opaque; /* I/O looper for asynchronous I/O on the socket. */ @@ -353,7 +422,7 @@ _async_socket_remove_io(AsyncSocket* as, * as - Initialized AsyncSocket instance. * list_head, list_tail - Pointers to the list head and tail. * Return: - * Next I/O at the head of the list, or NULL if I/O list become empty. + * Next I/O at the head of the list, or NULL if I/O list became empty. */ static AsyncSocketIO* _async_socket_advance_io(AsyncSocket* as, @@ -375,7 +444,7 @@ _async_socket_advance_io(AsyncSocket* as, * Param: * as - Initialized AsyncSocket instance. * Return: - * Next reader at the head of the list, or NULL if reader list become empty. + * Next reader at the head of the list, or NULL if reader list became empty. */ static AsyncSocketIO* _async_socket_advance_reader(AsyncSocket* as) @@ -387,7 +456,7 @@ _async_socket_advance_reader(AsyncSocket* as) * Param: * as - Initialized AsyncSocket instance. * Return: - * Next writer at the head of the list, or NULL if writer list become empty. + * Next writer at the head of the list, or NULL if writer list became empty. */ static AsyncSocketIO* _async_socket_advance_writer(AsyncSocket* as) @@ -399,75 +468,58 @@ _async_socket_advance_writer(AsyncSocket* as) * Param: * as - Initialized AsyncSocket instance. * asio - I/O to complete. + * Return: + * One of AsyncIOAction values. */ -static void +static AsyncIOAction _async_socket_complete_io(AsyncSocket* as, AsyncSocketIO* asio) { /* Stop the timer. */ loopTimer_stop(asio->timer); - /* Report I/O completion. First report via I/O callback, and only if it is - * not set, report via client callback. */ - if (asio->io_cb && asio->io_cb->on_completed) { - asio->io_cb->on_completed(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, asio->transferred); - } else if (as->client_cb->io_cb && as->client_cb->io_cb->on_completed) { - as->client_cb->io_cb->on_completed(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, - asio->transferred); - } + return asio->on_io(asio->io_opaque, asio, ASIO_STATE_SUCCEEDED); } /* Timeouts an I/O. * Param: * as - Initialized AsyncSocket instance. * asio - An I/O that has timed out. + * Return: + * One of AsyncIOAction values. */ -static void +static AsyncIOAction _async_socket_io_timed_out(AsyncSocket* as, AsyncSocketIO* asio) { - /* Remove the I/O from a list of active I/O. */ - if (asio->is_io_read) { - _async_socket_remove_io(as, &as->readers_head, &as->readers_tail, asio); - } else { - _async_socket_remove_io(as, &as->writers_head, &as->writers_tail, asio); + /* Report to the client. */ + const AsyncIOAction action = asio->on_io(asio->io_opaque, asio, + ASIO_STATE_TIMED_OUT); + + /* Remove the I/O from a list of active I/O for actions other than retry. */ + if (action != ASIO_ACTION_RETRY) { + if (asio->is_io_read) { + _async_socket_remove_io(as, &as->readers_head, &as->readers_tail, asio); + } else { + _async_socket_remove_io(as, &as->writers_head, &as->writers_tail, asio); + } } - /* Report I/O time out. First report it via I/O callbacks, and only if it is - * not set, report it via client callbacks. */ - if (asio->io_cb && asio->io_cb->on_timed_out) { - asio->io_cb->on_timed_out(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, - asio->transferred, asio->to_transfer); - } else if (as->client_cb->io_cb && as->client_cb->io_cb->on_timed_out) { - as->client_cb->io_cb->on_timed_out(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, - asio->transferred, asio->to_transfer); - } + return action; } /* Cancels an I/O. * Param: * as - Initialized AsyncSocket instance. * asio - An I/O to cancel. + * Return: + * One of AsyncIOAction values. */ -static void +static AsyncIOAction _async_socket_cancel_io(AsyncSocket* as, AsyncSocketIO* asio) { /* Stop the timer. */ loopTimer_stop(asio->timer); - /* Report I/O cancellation. First report it via I/O callbacks, and only if it - * is not set, report it via client callbacks. */ - if (asio->io_cb && asio->io_cb->on_cancelled) { - asio->io_cb->on_cancelled(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, - asio->transferred, asio->to_transfer); - } else if (as->client_cb->io_cb && as->client_cb->io_cb->on_cancelled) { - as->client_cb->io_cb->on_cancelled(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, - asio->transferred, asio->to_transfer); - } + return asio->on_io(asio->io_opaque, asio, ASIO_STATE_CANCELLED); } /* Reports an I/O failure. @@ -475,25 +527,17 @@ _async_socket_cancel_io(AsyncSocket* as, AsyncSocketIO* asio) * as - Initialized AsyncSocket instance. * asio - An I/O that has failed. Can be NULL for general failures. * failure - Failure (errno) that has occurred. + * Return: + * One of AsyncIOAction values. */ -static void +static AsyncIOAction _async_socket_io_failure(AsyncSocket* as, AsyncSocketIO* asio, int failure) { /* Stop the timer. */ loopTimer_stop(asio->timer); - /* Report I/O failure. First report it via I/O callbacks, and only if it - * is not set, report it via client callbacks. */ - if (asio && asio->io_cb && asio->io_cb->on_io_failure) { - asio->io_cb->on_io_failure(as->client_opaque, as, asio->is_io_read, - asio->io_opaque, asio->buffer, - asio->transferred, asio->to_transfer, failure); - } else if (as->client_cb->io_cb && as->client_cb->io_cb->on_io_failure) { - as->client_cb->io_cb->on_io_failure(as->client_opaque, as, - asio->is_io_read, asio->io_opaque, - asio->buffer, asio->transferred, - asio->to_transfer, failure); - } + errno = failure; + return asio->on_io(asio->io_opaque, asio, ASIO_STATE_FAILED); } /* Cancels all the active socket readers. @@ -505,6 +549,8 @@ _async_socket_cancel_readers(AsyncSocket* as) { while (as->readers_head != NULL) { AsyncSocketIO* const to_cancel = _async_socket_pull_first_reader(as); + /* We ignore action returned from the cancellation callback, since we're + * in a disconnected state here. */ _async_socket_cancel_io(as, to_cancel); _async_socket_io_destroy(to_cancel); } @@ -519,6 +565,8 @@ _async_socket_cancel_writers(AsyncSocket* as) { while (as->writers_head != NULL) { AsyncSocketIO* const to_cancel = _async_socket_pull_first_writer(as); + /* We ignore action returned from the cancellation callback, since we're + * in a disconnected state here. */ _async_socket_cancel_io(as, to_cancel); _async_socket_io_destroy(to_cancel); } @@ -607,7 +655,7 @@ _on_async_socket_disconnected(AsyncSocket* as) { /* Save error to restore it for the client's callback. */ const int save_errno = errno; - ASConnectAction action = ASCA_ABORT; + AsyncIOAction action = ASIO_ACTION_ABORT; D("Async socket '%s' is disconnected. Error %d -> %s", _async_socket_string(as), errno, strerror(errno)); @@ -620,14 +668,11 @@ _on_async_socket_disconnected(AsyncSocket* as) /* Restore errno, and invoke client's callback. */ errno = save_errno; - action = as->client_cb->on_connection(as->client_opaque, as, - ASCS_DISCONNECTED); + action = as->on_connection(as->client_opaque, as, ASIO_STATE_FAILED); - if (action == ASCA_RETRY) { + if (action == ASIO_ACTION_RETRY) { /* Client requested reconnection. */ - if (as->reconnect_to) { - _async_socket_reconnect(as, as->reconnect_to); - } + _async_socket_reconnect(as, as->reconnect_to); } } @@ -636,14 +681,14 @@ _on_async_socket_disconnected(AsyncSocket* as) * as - Initialized AsyncSocket instance. * asio - Descriptor for the failed I/O. Can be NULL for general failures. */ -static void +static AsyncIOAction _on_async_socket_failure(AsyncSocket* as, AsyncSocketIO* asio) { - D("Async socket '%s' I/O failure %d: %s", + D("Async socket '%s' I/O failure: %d -> %s", _async_socket_string(as), errno, strerror(errno)); /* Report the failure. */ - _async_socket_io_failure(as, asio, errno); + return _async_socket_io_failure(as, asio, errno); } /* A callback that is invoked when there is data available to read. @@ -656,6 +701,8 @@ _on_async_socket_failure(AsyncSocket* as, AsyncSocketIO* asio) static int _on_async_socket_recv(AsyncSocket* as) { + AsyncIOAction action; + /* Get current reader. */ AsyncSocketIO* const asr = as->readers_head; if (asr == NULL) { @@ -665,6 +712,29 @@ _on_async_socket_recv(AsyncSocket* as) return 0; } + /* Bump I/O state, and inform the client that I/O is in progress. */ + if (asr->state == ASIO_STATE_QUEUED) { + asr->state = ASIO_STATE_STARTED; + } else { + asr->state = ASIO_STATE_CONTINUES; + } + action = asr->on_io(asr->io_opaque, asr, asr->state); + if (action == ASIO_ACTION_ABORT) { + D("Read is aborted by the client of async socket '%s'", + _async_socket_string(as)); + /* Move on to the next reader. */ + _async_socket_advance_reader(as); + _async_socket_io_destroy(asr); + /* Lets see if there are still active readers, and enable, or disable + * read I/O callback accordingly. */ + if (as->readers_head != NULL) { + loopIo_wantRead(as->io); + } else { + loopIo_dontWantRead(as->io); + } + return 0; + } + /* Read next chunk of data. */ int res = socket_recv(as->fd, asr->buffer + asr->transferred, asr->to_transfer - asr->transferred); @@ -688,7 +758,21 @@ _on_async_socket_recv(AsyncSocket* as) } /* An I/O error. */ - _on_async_socket_failure(as, asr); + action = _on_async_socket_failure(as, asr); + if (action == ASIO_ACTION_ABORT) { + D("Read is aborted on failure by the client of async socket '%s'", + _async_socket_string(as)); + /* Move on to the next reader. */ + _async_socket_advance_reader(as); + _async_socket_io_destroy(asr); + /* Lets see if there are still active readers, and enable, or disable + * read I/O callback accordingly. */ + if (as->readers_head != NULL) { + loopIo_wantRead(as->io); + } else { + loopIo_dontWantRead(as->io); + } + } return -1; } @@ -724,6 +808,8 @@ _on_async_socket_recv(AsyncSocket* as) static int _on_async_socket_send(AsyncSocket* as) { + AsyncIOAction action; + /* Get current writer. */ AsyncSocketIO* const asw = as->writers_head; if (asw == NULL) { @@ -733,6 +819,29 @@ _on_async_socket_send(AsyncSocket* as) return 0; } + /* Bump I/O state, and inform the client that I/O is in progress. */ + if (asw->state == ASIO_STATE_QUEUED) { + asw->state = ASIO_STATE_STARTED; + } else { + asw->state = ASIO_STATE_CONTINUES; + } + action = asw->on_io(asw->io_opaque, asw, asw->state); + if (action == ASIO_ACTION_ABORT) { + D("Write is aborted by the client of async socket '%s'", + _async_socket_string(as)); + /* Move on to the next reader. */ + _async_socket_advance_reader(as); + _async_socket_io_destroy(asw); + /* Lets see if there are still active writers, and enable, or disable + * write I/O callback accordingly. */ + if (as->writers_head != NULL) { + loopIo_wantWrite(as->io); + } else { + loopIo_dontWantWrite(as->io); + } + return 0; + } + /* Write next chunk of data. */ int res = socket_send(as->fd, asw->buffer + asw->transferred, asw->to_transfer - asw->transferred); @@ -756,7 +865,22 @@ _on_async_socket_send(AsyncSocket* as) } /* An I/O error. */ - _on_async_socket_failure(as, asw); + /* An I/O error. */ + action = _on_async_socket_failure(as, asw); + if (action == ASIO_ACTION_ABORT) { + D("Write is aborted on failure by the client of async socket '%s'", + _async_socket_string(as)); + /* Move on to the next reader. */ + _async_socket_advance_reader(as); + _async_socket_io_destroy(asw); + /* Lets see if there are still active writers, and enable, or disable + * write I/O callback accordingly. */ + if (as->writers_head != NULL) { + loopIo_wantWrite(as->io); + } else { + loopIo_dontWantWrite(as->io); + } + } return -1; } @@ -813,51 +937,33 @@ _on_async_socket_io(void* opaque, int fd, unsigned events) * connector - Connector that is used to connect this socket. * event - Connection event. * Return: - * One of ASCCbRes values. + * One of AsyncIOAction values. */ -static ASCCbRes +static AsyncIOAction _on_connector_events(void* opaque, AsyncSocketConnector* connector, - ASCEvent event) + AsyncIOState event) { - ASConnectAction action; - ASConnectStatus adsc_status; + AsyncIOAction action; AsyncSocket* const as = (AsyncSocket*)opaque; - /* Convert connector event into async socket connection event. */ - switch (event) { - case ASC_CONNECTION_SUCCEEDED: - /* Accept the connection. */ - adsc_status = ASCS_CONNECTED; - as->fd = async_socket_connector_pull_fd(connector); - loopIo_init(as->io, as->looper, as->fd, _on_async_socket_io, as); - break; - - case ASC_CONNECTION_RETRY: - adsc_status = ASCS_RETRY; - break; - - case ASC_CONNECTION_FAILED: - default: - adsc_status = ASCS_FAILURE; - break; + if (event == ASIO_STATE_SUCCEEDED) { + /* Accept the connection. */ + as->fd = async_socket_connector_pull_fd(connector); + loopIo_init(as->io, as->looper, as->fd, _on_async_socket_io, as); } /* Invoke client's callback. */ - action = as->client_cb->on_connection(as->client_opaque, as, adsc_status); - if (event == ASC_CONNECTION_SUCCEEDED && action != ASCA_KEEP) { + action = as->on_connection(as->client_opaque, as, event); + if (event == ASIO_STATE_SUCCEEDED && action != ASIO_ACTION_DONE) { /* For whatever reason the client didn't want to keep this connection. * Close it. */ + D("Connection is discarded by a client of async socket '%s'", + _async_socket_string(as)); _async_socket_close_socket(as); } - if (action == ASCA_RETRY) { - return ASC_CB_RETRY; - } else if (action == ASCA_ABORT) { - return ASC_CB_ABORT; - } else { - return ASC_CB_KEEP; - } + return action; } /* Timer callback invoked to reconnect the lost connection. @@ -879,12 +985,12 @@ _on_async_socket_reconnect(void* opaque) AsyncSocket* async_socket_new(int port, int reconnect_to, - const ASClientCb* client_cb, + on_as_connection_cb client_cb, void* client_opaque) { AsyncSocket* as; - if (client_cb == NULL || client_cb->on_connection == NULL) { + if (client_cb == NULL) { E("Invalid client_cb parameter"); return NULL; } @@ -893,7 +999,7 @@ async_socket_new(int port, as->fd = -1; as->client_opaque = client_opaque; - as->client_cb = client_cb; + as->on_connection = client_cb; as->readers_head = as->readers_tail = NULL; as->reconnect_to = reconnect_to; sock_address_init_inet(&as->address, SOCK_ADDRESS_INET_LOOPBACK, port); @@ -901,6 +1007,7 @@ async_socket_new(int port, if (as->looper == NULL) { E("Unable to create I/O looper for async socket '%s'", _async_socket_string(as)); + client_cb(client_opaque, as, ASIO_STATE_FAILED); _async_socket_destroy(as); return NULL; } @@ -909,15 +1016,16 @@ async_socket_new(int port, return as; } -int +void async_socket_connect(AsyncSocket* as, int retry_to) { AsyncSocketConnector* const connector = async_socket_connector_new(&as->address, retry_to, _on_connector_events, as); - if (connector == NULL) { - return -1; + if (connector != NULL) { + async_socket_connector_connect(connector); + } else { + as->on_connection(as->client_opaque, as, ASIO_STATE_FAILED); } - return (async_socket_connector_connect(connector) == ASC_CONNECT_FAILED) ? -1 : 0; } void @@ -930,18 +1038,18 @@ async_socket_disconnect(AsyncSocket* as) } } -int +void async_socket_reconnect(AsyncSocket* as, int retry_to) { _async_socket_cancel_all_io(as); _async_socket_close_socket(as); - return async_socket_connect(as, retry_to); + async_socket_connect(as, retry_to); } -int +void async_socket_read_abs(AsyncSocket* as, void* buffer, uint32_t len, - const ASIOCb* reader_cb, + on_as_io_cb reader_cb, void* reader_opaque, Duration deadline) { @@ -955,25 +1063,24 @@ async_socket_read_abs(AsyncSocket* as, as->readers_tail = asr; } loopIo_wantRead(as->io); - return 0; } -int +void async_socket_read_rel(AsyncSocket* as, void* buffer, uint32_t len, - const ASIOCb* reader_cb, + on_as_io_cb reader_cb, void* reader_opaque, int to) { const Duration dl = (to >= 0) ? looper_now(_async_socket_get_looper(as)) + to : DURATION_INFINITE; - return async_socket_read_abs(as, buffer, len, reader_cb, reader_opaque, dl); + async_socket_read_abs(as, buffer, len, reader_cb, reader_opaque, dl); } -int +void async_socket_write_abs(AsyncSocket* as, const void* buffer, uint32_t len, - const ASIOCb* writer_cb, + on_as_io_cb writer_cb, void* writer_opaque, Duration deadline) { @@ -987,16 +1094,29 @@ async_socket_write_abs(AsyncSocket* as, as->writers_tail = asw; } loopIo_wantWrite(as->io); - return 0; } -int async_socket_write_rel(AsyncSocket* as, - const void* buffer, uint32_t len, - const ASIOCb* writer_cb, - void* writer_opaque, - int to) +void +async_socket_write_rel(AsyncSocket* as, + const void* buffer, uint32_t len, + on_as_io_cb writer_cb, + void* writer_opaque, + int to) { const Duration dl = (to >= 0) ? looper_now(_async_socket_get_looper(as)) + to : DURATION_INFINITE; - return async_socket_write_abs(as, buffer, len, writer_cb, writer_opaque, dl); + async_socket_write_abs(as, buffer, len, writer_cb, writer_opaque, dl); +} + +void* +async_socket_get_client_opaque(const AsyncSocket* as) +{ + return as->client_opaque; +} + +Duration +async_socket_deadline(AsyncSocket* as, int rel) +{ + return (rel >= 0) ? looper_now(_async_socket_get_looper(as)) + rel : + DURATION_INFINITE; } diff --git a/android/async-socket.h b/android/async-socket.h index 5b5d125..1402126 100644 --- a/android/async-socket.h +++ b/android/async-socket.h @@ -17,12 +17,14 @@ #ifndef ANDROID_ASYNC_SOCKET_H_ #define ANDROID_ASYNC_SOCKET_H_ +#include "android/async-io-common.h" + /* - * Contains declaration of an API that encapsulates communication via + * Contains declaration of an API that encapsulates communication via an * asynchronous socket. * - * This is pretty basic API that allows to asynchronously connect to a socket, - * and perform asynchronous read from / write to the connected socket. + * This is pretty basic API that allows asynchronous connection to a socket, + * and asynchronous read from / write to the connected socket. * * Since all the operations (including connection) are asynchronous, all the * operation results are reported back to the client of this API via set of @@ -32,32 +34,8 @@ /* Declares asynchronous socket descriptor. */ typedef struct AsyncSocket AsyncSocket; -/* Enumerates asynchronous socket connection statuses. - * Values enumerated here are passed to the client's callback that was set to - * monitor socket connection. - */ -typedef enum ASConnectStatus { - /* Socket has been connected. */ - ASCS_CONNECTED, - /* Socket has been disconnected. */ - ASCS_DISCONNECTED, - /* An error has occured while connecting to the socket. */ - ASCS_FAILURE, - /* An attempt to retry connection is about to begin. */ - ASCS_RETRY, -} ASConnectStatus; - -/* Enumerates values returned from the client's callback that was set to - * monitor socket connection. - */ -typedef enum ASConnectAction { - /* Keep the connection. */ - ASCA_KEEP, - /* Retry connection attempt. */ - ASCA_RETRY, - /* Abort the connection. */ - ASCA_ABORT, -} ASConnectAction; +/* Asynchronous socket I/O (reader, or writer) descriptor. */ +typedef struct AsyncSocketIO AsyncSocketIO; /* Defines client's callback set to monitor socket connection. * Param: @@ -65,216 +43,177 @@ typedef enum ASConnectAction { * as - Initialized AsyncSocket instance. * status - Socket connection status. * Return: - * One of the ASConnectAction values. + * One of the AsyncIOAction values. */ -typedef ASConnectAction (*on_as_connection_cb)(void* client_opaque, - AsyncSocket* as, - ASConnectStatus status); +typedef AsyncIOAction (*on_as_connection_cb)(void* client_opaque, + AsyncSocket* as, + AsyncIOState status); -/* Defines client's callback set to monitor socket I/O failures. +/* Defines client's callback set to monitor I/O progress. * Param: - * client_opaque - An opaque pointer associated with the client. - * as - Initialized AsyncSocket instance. - * is_io_read - I/O type selector: 1 - read, 0 - write. - * io_opaque - An opaque pointer associated with the I/O that has failed. - * buffer - Address of the I/O buffer. - * transferred - Number of bytes that were transferred before I/O has failed. - * to_transfer - Number of bytes initially requested to transfer with the - * failed I/O. - * failure - Error that occured (errno value) + * io_opaque - An opaque pointer associated with the I/O. + * asio - Async I/O in progress. + * status - Status of the I/O. + * Return: + * One of the AsyncIOAction values. */ -typedef void (*on_as_io_failure_cb)(void* client_opaque, - AsyncSocket* as, - int is_io_read, - void* io_opaque, - void* buffer, - uint32_t transferred, - uint32_t to_transfer, - int failure); +typedef AsyncIOAction (*on_as_io_cb)(void* io_opaque, + AsyncSocketIO* asio, + AsyncIOState status); -/* Defines client's callback invoked when I/O has been completed. - * Param: - * client_opaque - An opaque pointer associated with the client. - * as - Initialized AsyncSocket instance. - * is_io_read - I/O type selector: 1 - read, 0 - write. - * io_opaque - An opaque pointer associated with the I/O that has been - * completed. - * buffer - Address of the I/O buffer. - * transferred - Number of bytes that were transferred. - */ -typedef void (*on_as_io_completed_cb)(void* client_opaque, - AsyncSocket* as, - int is_io_read, - void* io_opaque, - void* buffer, - uint32_t transferred); +/******************************************************************************** + * AsyncSocketIO API + *******************************************************************************/ -/* Defines client's callback invoked when an I/O gets cancelled (due to a - * disconnection, for instance). - * Param: - * client_opaque - An opaque pointer associated with the client. - * as - Initialized AsyncSocket instance. - * is_io_read - I/O type selector: 1 - read, 0 - write. - * io_opaque - An opaque pointer associated with the I/O that has been - * cancelled. - * buffer - Address of the I/O buffer. - * transferred - Number of bytes that were transferred before I/O has been - * cancelled. - * to_transfer - Number of bytes initially requested to transfer with the - * cancelled I/O. - */ -typedef void (*on_as_io_cancelled_cb)(void* client_opaque, - AsyncSocket* as, - int is_io_read, - void* io_opaque, - void* buffer, - uint32_t transferred, - uint32_t to_transfer); +/* Gets AsyncSocket instance for an I/O */ +extern AsyncSocket* async_socket_io_get_socket(const AsyncSocketIO* asio); + +/* Cancels time out set for an I/O */ +extern void async_socket_io_cancel_time_out(AsyncSocketIO* asio); + +/* Gets an opaque pointer associated with an I/O */ +extern void* async_socket_io_get_io_opaque(const AsyncSocketIO* asio); -/* Defines client's callback invoked when an I/O gets timed out. +/* Gets an opaque pointer associated with the client that has requested I/O */ +extern void* async_socket_io_get_client_opaque(const AsyncSocketIO* asio); + +/* Gets I/O buffer information. * Param: - * client_opaque - An opaque pointer associated with the client. - * as - Initialized AsyncSocket instance. - * is_io_read - I/O type selector: 1 - read, 0 - write. - * io_opaque - An opaque pointer associated with the I/O that has timed out. - * buffer - Address of the I/O buffer. - * transferred - Number of bytes that were transferred before I/O has timed out. - * to_transfer - Number of bytes initially requested to transfer with the timed - * out I/O. + * asio - I/O descriptor. + * transferred - Optional pointer to receive number of bytes transferred with + * this I/O. Can be NULL. + * to_transfer - Optional pointer to receive number of bytes requested to + * transfer with this I/O. Can be NULL. + * Return: + * I/O buffer. */ -typedef void (*on_as_io_timed_out_cb)(void* client_opaque, - AsyncSocket* as, - int is_io_read, - void* io_opaque, - void* buffer, - uint32_t transferred, - uint32_t to_transfer); +extern void* async_socket_io_get_buffer_info(const AsyncSocketIO* asio, + uint32_t* transferred, + uint32_t* to_transfer); -/* Lists asynchronous socket I/O callbacks. */ -typedef struct ASIOCb { - on_as_io_completed_cb on_completed; - on_as_io_cancelled_cb on_cancelled; - on_as_io_timed_out_cb on_timed_out; - on_as_io_failure_cb on_io_failure; -} ASIOCb; +/* Gets I/O buffer. */ +extern void* async_socket_io_get_buffer(const AsyncSocketIO* asio); +/* Gets number of bytes transferred with this I/O. */ +extern uint32_t async_socket_io_get_transferred(const AsyncSocketIO* asio); -/* Lists asynchronous socket client callbacks. */ -typedef struct ASClientCb { - /* Connection callback (client must have one) */ - on_as_connection_cb on_connection; - /* Optional client-level I/O callbacks. */ - const ASIOCb* io_cb; -} ASClientCb; +/* Gets number of bytes requested to transfer with this I/O. */ +extern uint32_t async_socket_io_get_to_transfer(const AsyncSocketIO* asio); + +/* Gets I/O type: read (returns 1), or write (returns 0). */ +extern int async_socket_io_is_read(const AsyncSocketIO* asio); + +/******************************************************************************** + * AsyncSocket API + *******************************************************************************/ /* Creates an asynchronous socket descriptor. * Param: * port - TCP port to connect the socket to. - * reconnect_to - Timeout before retrying to reconnect after disconnection. - * 0 means "don't try to reconnect". - * client_callbacks - Lists socket client callbacks. + * reconnect_to - Timeout before trying to reconnect after disconnection. + * connect_cb - Client callback to monitor connection state (must not be NULL). * client_opaque - An opaque pointer to associate with the socket client. * Return: * Initialized AsyncSocket instance on success, or NULL on failure. */ extern AsyncSocket* async_socket_new(int port, int reconnect_to, - const ASClientCb* client_callbacks, + on_as_connection_cb connect_cb, void* client_opaque); /* Asynchronously connects to an asynchronous socket. + * Note that connection result will be reported via callback passed to the + * async_socket_new routine. * Param: * as - Initialized AsyncSocket instance. * retry_to - Number of milliseconds to wait before retrying a failed - * connection. - * Return: - * 0 on success, or -1 on failure. If this routine returns a failure, I/O - * failure callback has not been invoked. + * connection attempt. */ -extern int async_socket_connect(AsyncSocket* as, int retry_to); +extern void async_socket_connect(AsyncSocket* as, int retry_to); /* Disconnects from an asynchronous socket. + * NOTE: AsyncSocket instance referenced in this call will be destroyed in this + * routine. * Param: * as - Initialized and connected AsyncSocket instance. */ extern void async_socket_disconnect(AsyncSocket* as); /* Asynchronously reconnects to an asynchronous socket. + * Note that connection result will be reported via callback passed to the + * async_socket_new routine. * Param: * as - Initialized AsyncSocket instance. - * retry_to - Number of milliseconds to wait before retrying to reconnect. - * Return: - * 0 on success, or -1 on failure. If this routine returns a failure, I/O - * failure callback has not been invoked. + * retry_to - Number of milliseconds to wait before trying to reconnect. */ -extern int async_socket_reconnect(AsyncSocket* as, int retry_to); +extern void async_socket_reconnect(AsyncSocket* as, int retry_to); /* Asynchronously reads data from an asynchronous socket with a deadline. * Param: * as - Initialized and connected AsyncSocket instance. * buffer, len - Buffer where to read data. - * reader_cb - Lists reader's callbacks. + * reader_cb - Callback to monitor I/O progress (must not be NULL). * reader_opaque - An opaque pointer associated with the reader. * deadline - Deadline to complete the read. - * Return: - * 0 on success, or -1 on failure. If this routine returns a failure, I/O - * failure callback has not been invoked. */ -extern int async_socket_read_abs(AsyncSocket* as, - void* buffer, uint32_t len, - const ASIOCb* reader_cb, - void* reader_opaque, - Duration deadline); +extern void async_socket_read_abs(AsyncSocket* as, + void* buffer, uint32_t len, + on_as_io_cb reader_cb, + void* reader_opaque, + Duration deadline); /* Asynchronously reads data from an asynchronous socket with a relative timeout. * Param: * as - Initialized and connected AsyncSocket instance. * buffer, len - Buffer where to read data. - * reader_cb - Lists reader's callbacks. + * reader_cb - Callback to monitor I/O progress (must not be NULL). * reader_opaque - An opaque pointer associated with the reader. * to - Milliseconds to complete the read. to < 0 indicates "no timeout" - * Return: - * 0 on success, or -1 on failure. If this routine returns a failure, I/O - * failure callback has not been invoked. */ -extern int async_socket_read_rel(AsyncSocket* as, - void* buffer, uint32_t len, - const ASIOCb* reader_cb, - void* reader_opaque, - int to); +extern void async_socket_read_rel(AsyncSocket* as, + void* buffer, uint32_t len, + on_as_io_cb reader_cb, + void* reader_opaque, + int to); /* Asynchronously writes data to an asynchronous socket with a deadline. * Param: * as - Initialized and connected AsyncSocket instance. * buffer, len - Buffer with writing data. - * writer_cb - Lists writer's callbacks. + * writer_cb - Callback to monitor I/O progress (must not be NULL). * writer_opaque - An opaque pointer associated with the writer. * deadline - Deadline to complete the write. - * Return: - * 0 on success, or -1 on failure. If this routine returns a failure, I/O - * failure callback has not been invoked. */ -extern int async_socket_write_abs(AsyncSocket* as, - const void* buffer, uint32_t len, - const ASIOCb* writer_cb, - void* writer_opaque, - Duration deadline); +extern void async_socket_write_abs(AsyncSocket* as, + const void* buffer, uint32_t len, + on_as_io_cb writer_cb, + void* writer_opaque, + Duration deadline); /* Asynchronously writes data to an asynchronous socket with a relative timeout. * Param: * as - Initialized and connected AsyncSocket instance. * buffer, len - Buffer with writing data. - * writer_cb - Lists writer's callbacks. + * writer_cb - Callback to monitor I/O progress (must not be NULL) * writer_opaque - An opaque pointer associated with the writer. * to - Milliseconds to complete the write. to < 0 indicates "no timeout" + */ +extern void async_socket_write_rel(AsyncSocket* as, + const void* buffer, uint32_t len, + on_as_io_cb writer_cb, + void* writer_opaque, + int to); + +/* Get a deadline for the given time interval relative to "now". + * Param: + * as - Initialized AsyncSocket instance. + * rel - Time interval. If < 0 an infinite duration will be returned. * Return: - * 0 on success, or -1 on failure. If this routine returns a failure, I/O - * failure callback has not been invoked. + * A deadline for the given time interval relative to "now". */ -extern int async_socket_write_rel(AsyncSocket* as, - const void* buffer, uint32_t len, - const ASIOCb* writer_cb, - void* writer_opaque, - int to); +extern Duration async_socket_deadline(AsyncSocket* as, int rel); + +/* Gets an opaque pointer associated with the socket's client */ +extern void* async_socket_get_client_opaque(const AsyncSocket* as); #endif /* ANDROID_ASYNC_SOCKET_H_ */ |