diff options
author | Vladimir Chtchetkine <vchtchetkine@google.com> | 2012-03-28 12:11:11 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-03-28 12:11:11 -0700 |
commit | 300dc988021a26ea93e3f87e15e70fdcb9cb9359 (patch) | |
tree | e0421ba301cc609ff8cf764d008c44d1e4c3f86c /android | |
parent | 9dcf03fda8001a74016a316b0aa223f6675dff37 (diff) | |
parent | 9d36fe7e6f0c5ece4de1b29ec78a15c37c158b1d (diff) | |
download | external_qemu-300dc988021a26ea93e3f87e15e70fdcb9cb9359.zip external_qemu-300dc988021a26ea93e3f87e15e70fdcb9cb9359.tar.gz external_qemu-300dc988021a26ea93e3f87e15e70fdcb9cb9359.tar.bz2 |
Merge "Implements an asynchronous socket connector with retries"
Diffstat (limited to 'android')
-rw-r--r-- | android/async-socket-connector.c | 322 | ||||
-rw-r--r-- | android/async-socket-connector.h | 158 | ||||
-rw-r--r-- | android/utils/debug.h | 1 |
3 files changed, 481 insertions, 0 deletions
diff --git a/android/async-socket-connector.c b/android/async-socket-connector.c new file mode 100644 index 0000000..daf6a37 --- /dev/null +++ b/android/async-socket-connector.c @@ -0,0 +1,322 @@ +/* + * 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. + */ + +/* + * Encapsulates exchange protocol between the emulator, and an Android device + * that is connected to the host via USB. The communication is established over + * a TCP port forwarding, enabled by ADB. + */ + +#include "qemu-common.h" +#include "android/async-utils.h" +#include "android/utils/debug.h" +#include "android/async-socket-connector.h" +#include "utils/panic.h" +#include "iolooper.h" + +#define E(...) derror(__VA_ARGS__) +#define W(...) dwarning(__VA_ARGS__) +#define D(...) VERBOSE_PRINT(asconnector,__VA_ARGS__) +#define D_ACTIVE VERBOSE_CHECK(asconnector) + +/******************************************************************************** + * Internals + *******************************************************************************/ + +struct AsyncSocketConnector { + /* TCP address for the connection. */ + SockAddress address; + /* I/O looper for asynchronous I/O. */ + Looper* looper; + /* I/O port for asynchronous connection. */ + LoopIo connector_io[1]; + /* Timer that is used to retry asynchronous connections. */ + LoopTimer connector_timer[1]; + /* Asynchronous connector to the socket. */ + AsyncConnector connector[1]; + /* Callback to invoke on connection / connection error. */ + asc_event_cb on_connected_cb; + /* An opaque parameter to pass to the connection callback. */ + void* on_connected_cb_opaque; + /* Retry timeout in milliseconds. */ + int retry_to; + /* Socket descriptor for the connection. */ + int fd; +}; + +/* Asynchronous I/O looper callback invoked by the connector. + * Param: + * opaque - AsyncSocketConnector instance. + * fd, events - Standard I/O callback parameters. + */ +static void _on_async_socket_connector_io(void* opaque, int fd, unsigned events); + +/* Gets socket's address string. */ +AINLINED const char* +_asc_socket_string(AsyncSocketConnector* connector) +{ + return sock_address_to_string(&connector->address); +} + +/* Destroys AsyncSocketConnector instance. + * Param: + * connector - Initialized AsyncSocketConnector instance. + */ +static void +_async_socket_connector_free(AsyncSocketConnector* connector) +{ + if (connector != NULL) { + if (connector->fd >= 0) { + socket_close(connector->fd); + } + + if (connector->looper != NULL) { + loopTimer_done(connector->connector_timer); + looper_free(connector->looper); + } + + sock_address_done(&connector->address); + + AFREE(connector); + } +} + +/* Opens connection socket. + * Param: + * connector - Initialized AsyncSocketConnector instance. + * Return: + * 0 on success, or -1 on failure. + */ +static int +_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)); + return -1; + } + + /* Prepare for async I/O on the connector. */ + socket_set_nonblock(connector->fd); + loopIo_init(connector->connector_io, connector->looper, connector->fd, + _on_async_socket_connector_io, connector); + + return 0; +} + +/* Closes connection socket. + * Param: + * connector - Initialized AsyncSocketConnector instance. + * Return: + * 0 on success, or -1 on failure. + */ +static void +_async_socket_connector_close_socket(AsyncSocketConnector* connector) +{ + if (connector->fd >= 0) { + socket_close(connector->fd); + connector->fd = -1; + } +} + +/* Asynchronous connector (AsyncConnector instance) has completed connection + * attempt. + * + * NOTE: Upon exit from this routine AsyncSocketConnector instance might be + * destroyed. So, once this routine is called, there must be no further + * references to AsyncSocketConnector instance passed to this routine. + * Param: + * connector - Initialized AsyncSocketConnector instance. + * status - Status of the connection attempt. + */ +static void +_on_async_socket_connector_connecting(AsyncSocketConnector* connector, + AsyncStatus status) +{ + ASCCbRes action; + int do_retry = 0; + + switch (status) { + case ASYNC_COMPLETE: + loopIo_done(connector->connector_io); + 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); + } + 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)); + /* 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); + } + break; + + case ASYNC_NEED_MORE: + return; + } + + if (do_retry) { + D("Retrying connection to socket %s", _asc_socket_string(connector)); + loopTimer_startRelative(connector->connector_timer, connector->retry_to); + } else { + _async_socket_connector_free(connector); + } +} + +static void +_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); +} + +/* Retry connection timer callback. + * Param: + * opaque - AsyncSocketConnector instance. + */ +static void +_on_async_socket_connector_retry(void* opaque) +{ + AsyncSocketConnector* const connector = (AsyncSocketConnector*)opaque; + + /* Invoke the callback to notify about a connection retry attempt. */ + const ASCCbRes action = + connector->on_connected_cb(connector->on_connected_cb_opaque, + connector, ASC_CONNECTION_RETRY); + + if (action == ASC_CB_RETRY) { + AsyncStatus status; + + /* Close handle opened for the previous (failed) attempt. */ + _async_socket_connector_close_socket(connector); + + /* 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; + } + + _on_async_socket_connector_connecting(connector, status); + } else { + _async_socket_connector_free(connector); + } +} + +/******************************************************************************** + * Async connector implementation + *******************************************************************************/ + +AsyncSocketConnector* +async_socket_connector_new(const SockAddress* address, + int retry_to, + asc_event_cb cb, + void* cb_opaque) +{ + AsyncSocketConnector* connector; + + if (cb == NULL) { + W("No callback for AsyncSocketConnector for %s", + sock_address_to_string(address)); + errno = EINVAL; + return NULL; + } + + ANEW0(connector); + + connector->fd = -1; + connector->retry_to = retry_to; + connector->on_connected_cb = cb; + connector->on_connected_cb_opaque = cb_opaque; + + /* Copy socket address. */ + if (sock_address_get_family(address) == SOCKET_UNIX) { + sock_address_init_unix(&connector->address, sock_address_get_path(address)); + } else { + connector->address = *address; + } + + /* Create a looper for asynchronous I/O. */ + connector->looper = looper_newCore(); + if (connector->looper != NULL) { + /* Create a timer that will be used for connection retries. */ + 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", + _asc_socket_string(connector)); + _async_socket_connector_free(connector); + return NULL; + } + + return connector; +} + +ASCConnectRes +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); + } 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 +async_socket_connector_pull_fd(AsyncSocketConnector* connector) +{ + const int fd = connector->fd; + if (fd >= 0) { + connector->fd = -1; + } + return fd; +} diff --git a/android/async-socket-connector.h b/android/async-socket-connector.h new file mode 100644 index 0000000..998d0c8 --- /dev/null +++ b/android/async-socket-connector.h @@ -0,0 +1,158 @@ +/* + * 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_SOCKET_CONNECTOR_H_ +#define ANDROID_ASYNC_SOCKET_CONNECTOR_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. + * 2. The client then proceeds with calling async_socket_connector_connect that + * would initiate connection attempts. + * + * The main job on the client side falls on the client's callback routine that + * serves the connection events. Once connection has been initiated, the connector + * will invoke that callback to report current connection status. + * + * In general, there are three connection events passed to the callback: + * 1. Success. + * 2. Failure. + * 3. Retry. + * + * Typically, when client's callback is called for 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. + * + * 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 + * from the callback, the connector will stop connection attempts, and will + * self-destruct. If ASC_CB_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. + * + * 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. + */ + +/* 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, + * errno contains connection error. + * Return: + * One of ASCCbRes values. + */ +typedef ASCCbRes (*asc_event_cb)(void* opaque, + AsyncSocketConnector* connector, + ASCEvent event); + +/* Creates and initializes AsyncSocketConnector instance. + * Param: + * address - Initialized socket address to connect to. + * retry_to - Retry timeout 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. + */ +extern AsyncSocketConnector* async_socket_connector_new(const SockAddress* address, + int retry_to, + asc_event_cb cb, + void* cb_opaque); + +/* Initiates asynchronous connection. + * 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. + */ +extern ASCConnectRes 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. + * Param: + * connector - Initialized AsyncSocketConnector instance. + * Return: + * File descriptor for the connected socket. + */ +extern int async_socket_connector_pull_fd(AsyncSocketConnector* connector); + +#endif /* ANDROID_ASYNC_SOCKET_CONNECTOR_H_ */ diff --git a/android/utils/debug.h b/android/utils/debug.h index e97ec43..d7de9da 100644 --- a/android/utils/debug.h +++ b/android/utils/debug.h @@ -44,6 +44,7 @@ _VERBOSE_TAG(adbserver, "ADB server") \ _VERBOSE_TAG(adbclient, "ADB QEMU client") \ _VERBOSE_TAG(adb, "ADB debugger") \ + _VERBOSE_TAG(asconnector, "Asynchronous socket connector") \ #define _VERBOSE_TAG(x,y) VERBOSE_##x, typedef enum { |