Gave asynchronous operations approximate POSIX signal semantics. This

will enable us to cascade async operations, which is necessary in order to
properly support DNS.  (For example, an HTTP request may have to redirect
to a new location and will have to perform a new DNS lookup, so we can't
just rely on doing the name lookup at the time of parsing the initial
URL).

Anything other than HTTP is probably broken right now; I'll fix the others
up asap.
This commit is contained in:
Michael Brown
2007-01-15 08:49:10 +00:00
parent ec75b269d3
commit 4e20d73bb5
26 changed files with 656 additions and 248 deletions

View File

@@ -125,6 +125,7 @@
#define EBUSY 0xd4 /**< Device or resource busy */
/** Operation cancelled */
#define ECANCELED PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE
#define ECHILD ENOENT /**< No child processes */
#define ECONNABORTED 0xd5 /**< Software caused connection abort */
#define ECONNREFUSED 0xd6 /**< Connection refused */
#define ECONNRESET 0xd7 /**< Connection reset by peer */
@@ -157,10 +158,10 @@
#define ENOSYS 0xee /**< Function not implemented */
#define ENOTCONN 0xef /**< Transport endpoint is not connected */
#define ENOTSOCK 0xf0 /**< Socket operation on non-socket */
#define EOPNOTSUPP 0xf1 /**< Operation not supported */
#define ENOTSUP EOPNOTSUPP /**< Not supported */
#define ENOTSUP 0xf1 /**< Not supported */
#define ENOTTY 0xf2 /**< Inappropriate ioctl for device */
#define ENXIO ENODEV /**< No such device or address */
#define EOPNOTSUPP ENOTSUP /**< Operation not supported */
#define EOVERFLOW 0xf3 /**< Result too large */
#define EPERM EACCES /**< Operation not permitted */
#define EPROTO 0xf4 /**< Protocol error */

View File

@@ -107,7 +107,7 @@ struct aoe_session {
/** Byte offset within command's data buffer */
unsigned int command_offset;
/** Asynchronous operation for this command */
struct async_operation aop;
struct async async;
/** Retransmission timer */
struct retry_timer timer;
@@ -121,8 +121,9 @@ struct aoe_session {
extern void aoe_open ( struct aoe_session *aoe );
extern void aoe_close ( struct aoe_session *aoe );
extern struct async_operation * aoe_issue ( struct aoe_session *aoe,
struct ata_command *command );
extern int aoe_issue ( struct aoe_session *aoe,
struct ata_command *command,
struct async *parent );
/** An AoE device */
struct aoe_device {

View File

@@ -7,56 +7,164 @@
*
*/
#include <errno.h>
#include <assert.h>
#include <gpxe/list.h>
/** An asynchronous operation */
struct async_operation {
/** Operation status
struct async;
/** An asynchronous operation ID
*
* Only positive identifiers are valid; negative values are used to
* indicate errors.
*/
typedef long aid_t;
/** Signals that can be delivered to asynchronous operations */
enum signal {
/** A child asynchronous operation has completed
*
* This is an error code as defined in errno.h, plus an offset
* of EINPROGRESS. This means that a status value of 0
* corresponds to a return status code of -EINPROGRESS,
* i.e. that the default state of an asynchronous operation is
* "not yet completed".
* The parent should call async_wait() to reap the completed
* child. async_wait() will return the exit status and
* operation identifier of the child.
*
* The handler for this signal can be set to @c NULL; if it
* is, then the children will accumulate as zombies until
* async_wait() is called.
*
* The handler for this signal can also be set to @c SIG_IGN;
* if it is, then the children will automatically be reaped.
* Note that if you use @c SIG_IGN then you will not be able
* to retrieve the return status of the children; the call to
* async_wait() will simply return -ECHILD.
*/
int status;
SIGCHLD = 0,
/** Cancel asynchronous operation
*
* This signal should trigger the asynchronous operation to
* cancel itself (including killing all its own children, if
* any), and then call async_done(). The asynchronous
* operation is allowed to not complete immediately.
*
* The handler for this signal can be set to @c NULL; if it
* is, then attempts to cancel the asynchronous operation will
* fail and the operation will complete normally. Anything
* waiting for the operation to cancel will block.
*/
SIGKILL,
/** Update progress of asynchronous operation
*
* This signal should cause the asynchronous operation to
* immediately update the @c completed and @c total fields.
*
* The handler for this signal can be set to @c NULL; if it
* is, then the asynchronous operation is expected to keep its
* @c completed and @c total fields up to date at all times.
*/
SIGUPDATE,
SIGMAX
};
/**
* Set asynchronous operation status
* A signal handler
*
* @v aop Asynchronous operation
* @v rc Return status code
* @v async Asynchronous operation
* @v signal Signal received
*/
static inline __attribute__ (( always_inline )) void
async_set_status ( struct async_operation *aop, int rc ) {
aop->status = ( rc + EINPROGRESS );
}
typedef void ( * signal_handler_t ) ( struct async *async,
enum signal signal );
/** Asynchronous operation operations */
struct async_operations {
/** Reap asynchronous operation
*
* @v async Asynchronous operation
*
* Release all resources associated with the asynchronous
* operation. This will be called only after the asynchronous
* operation itself calls async_done(), so the only remaining
* resources will probably be the memory used by the struct
* async itself.
*
* This method can be set to @c NULL; if it is, then no
* resources will be freed. This may be suitable for
* asynchronous operations that consume no dynamically
* allocated memory.
*/
void ( * reap ) ( struct async *async );
/** Handle signals */
signal_handler_t signal[SIGMAX];
};
/** An asynchronous operation */
struct async {
/** Other asynchronous operations with the same parent */
struct list_head siblings;
/** Child asynchronous operations */
struct list_head children;
/** Parent asynchronous operation
*
* This field is optional; if left to NULL then the owner must
* never call async_done().
*/
struct async *parent;
/** Asynchronous operation ID */
aid_t aid;
/** Final return status code */
int rc;
/** Amount of operation completed so far
*
* The units for this quantity are arbitrary. @c completed
* divded by @total should give something which approximately
* represents the progress through the operation. For a
* download operation, using byte counts would make sense.
*
* This progress indicator should also incorporate the status
* of any child asynchronous operations.
*/
unsigned long completed;
/** Total operation size
*
* See @c completed. A zero value means "total size unknown"
* and is explcitly permitted; users should take this into
* account before calculating @c completed/total.
*/
unsigned long total;
struct async_operations *aop;
};
extern struct async_operations default_async_operations;
extern struct async_operations orphan_async_operations;
extern aid_t async_init ( struct async *async, struct async_operations *aop,
struct async *parent );
extern void async_ignore_signal ( struct async *async, enum signal signal );
extern void async_signal ( struct async *async, enum signal signal );
extern void async_signal_children ( struct async *async, enum signal signal );
extern void async_done ( struct async *async, int rc );
extern aid_t async_wait ( struct async *async, int *rc, int block );
/** Default signal handler */
#define SIG_DFL NULL
/** Ignore signal */
#define SIG_IGN async_ignore_signal
/**
* Get asynchronous operation status
* Initialise orphan asynchronous operation
*
* @v aop Asynchronous operation
* @ret rc Return status code
*/
static inline __attribute__ (( always_inline )) int
async_status ( struct async_operation *aop ) {
return ( aop->status - EINPROGRESS );
}
/**
* Flag asynchronous operation as complete
* @v async Asynchronous operation
* @ret aid Asynchronous operation ID
*
* @v aop Asynchronous operation
* @v rc Return status code
* An orphan asynchronous operation can act as a context for child
* operations. However, you must not call async_done() on such an
* operation, since this would attempt to send a signal to its
* (non-existent) parent. Instead, simply free the structure (after
* calling async_wait() to ensure that any child operations have
* completed).
*/
static inline __attribute__ (( always_inline )) void
async_done ( struct async_operation *aop, int rc ) {
assert ( rc != -EINPROGRESS );
async_set_status ( aop, rc );
static inline aid_t async_init_orphan ( struct async *async ) {
return async_init ( async, &orphan_async_operations, NULL );
}
extern int async_wait ( struct async_operation *aop );
#endif /* _GPXE_ASYNC_H */

View File

@@ -466,7 +466,7 @@ struct dhcp_session {
*/
int state;
/** Asynchronous operation for this DHCP session */
struct async_operation aop;
struct async async;
/** Retransmission timer */
struct retry_timer timer;
};
@@ -504,6 +504,6 @@ extern int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
struct dhcp_packet *dhcppkt );
extern int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
struct dhcp_option_block *options );
extern struct async_operation * start_dhcp ( struct dhcp_session *dhcp );
extern int start_dhcp ( struct dhcp_session *dhcp, struct async *parent );
#endif /* _GPXE_DHCP_H */

View File

@@ -64,7 +64,7 @@ struct ftp_request {
struct tcp_application tcp_data;
/** Asynchronous operation for this FTP operation */
struct async_operation aop;
struct async async;
};
struct async_operation * ftp_get ( struct ftp_request *ftp );

View File

@@ -44,7 +44,7 @@ struct hello_request {
struct tcp_application tcp;
/** Asynchronous operation */
struct async_operation aop;
struct async async;
};
extern struct async_operation * say_hello ( struct hello_request *hello );

View File

@@ -11,6 +11,7 @@
#include <gpxe/tcp.h>
#include <gpxe/async.h>
#include <gpxe/linebuf.h>
#include <gpxe/uri.h>
/** HTTP default port */
#define HTTP_PORT 80
@@ -28,33 +29,29 @@ enum http_rx_state {
*
*/
struct http_request {
/** Server address */
struct sockaddr_tcpip server;
/** Server host name */
const char *hostname;
/** Filename */
const char *filename;
/** URI being fetched */
struct uri *uri;
/** Data buffer to fill */
struct buffer *buffer;
/** Asynchronous operation */
struct async async;
/** HTTP response code */
unsigned int response;
/** HTTP Content-Length */
size_t content_length;
/** TCP application for this request */
struct tcp_application tcp;
/** Number of bytes already sent */
size_t tx_offset;
/** RX state */
enum http_rx_state rx_state;
/** Line buffer for received header lines */
struct line_buffer linebuf;
/** TCP application for this request */
struct tcp_application tcp;
/** Asynchronous operation */
struct async_operation aop;
};
extern struct async_operation * http_get ( struct http_request *http );
extern int http_get ( struct uri *uri, struct buffer *buffer,
struct async *parent );
#endif /* _GPXE_HTTP_H */

View File

@@ -591,7 +591,7 @@ struct iscsi_session {
*/
struct scsi_command *command;
/** Asynchronous operation for the current iSCSI operation */
struct async_operation aop;
struct async async;
/** Instant return code
*
* Set to a non-zero value if all requests should return
@@ -637,8 +637,9 @@ struct iscsi_session {
/** Maximum number of retries at connecting */
#define ISCSI_MAX_RETRIES 2
extern struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
struct scsi_command *command );
extern int iscsi_issue ( struct iscsi_session *iscsi,
struct scsi_command *command,
struct async *parent );
extern void iscsi_shutdown ( struct iscsi_session *iscsi );
/** An iSCSI device */

View File

@@ -135,7 +135,7 @@ struct tftp_session {
int state;
/** Asynchronous operation for this session */
struct async_operation aop;
struct async async;
/** Retransmission timer */
struct retry_timer timer;
};

View File

@@ -11,6 +11,6 @@
#include <stdint.h>
#include <gpxe/uaccess.h>
extern int fetch ( const char *filename, userptr_t *data, size_t *len );
extern int fetch ( const char *uri_string, userptr_t *data, size_t *len );
#endif /* _USR_FETCH_H */