Add a "ctrl" mechanism to bufferevents for property access.

OpenSSL uses something like this to implement get/set access for
properties on its BIOs, so that it doesn't need to add a pair of
get/set functions to the vtable struct for every new abstract property
it provides an accessor for.

Doing this lets us make bufferevent_setfd abstract, and implement an
abstract bufferevent_getfd.

svn:r1284
This commit is contained in:
Nick Mathewson 2009-05-13 20:37:21 +00:00
parent 83f46e51d7
commit 31d89f274b
10 changed files with 138 additions and 5 deletions

View File

@ -22,6 +22,8 @@ Changes in 2.0.2-alpha:
o Rename the evbuffercb and everrorcb callbacks to bufferevent_data_cb and bufferevent_event_cb respectively. The old names are available in bufferevent_compat.h.
o Rename the EVBUFFER_* codes used by bufferevent event callbacks to BEV_EVENT_*, to avoid namespace collision with evbuffer flags. The old names are available in bufferevent_compat.h.
o Move the EVBUFFER_INPUT and EVBUFFER_OUTPUT macros to bufferevent_compat.h
o Add a bufferevent_getfd() function to mirror bufferevent_setfd()
o Make bufferevent_setfd() return an error code if the operation is not successful.
Changes in 2.0.1-alpha:
o free minheap on event_base_free(); from Christopher Layne

View File

@ -300,3 +300,9 @@ done:
return r;
}
evutil_socket_t
_evbuffer_overlapped_get_fd(struct evbuffer *buf)
{
struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
return buf_o ? buf_o->fd : -1;
}

View File

@ -60,6 +60,17 @@ struct bufferevent_private {
void *lock;
};
enum bufferevent_ctrl_op {
BEV_CTRL_SET_FD,
BEV_CTRL_GET_FD,
BEV_CTRL_GET_UNDERLYING,
};
union bufferevent_ctrl_data {
void *ptr;
evutil_socket_t fd;
};
/**
Implementation table for a bufferevent: holds function pointers and other
information to make the various bufferevent types work.
@ -101,6 +112,9 @@ struct bufferevent_ops {
/** Called to flush data. */
int (*flush)(struct bufferevent *, short, enum bufferevent_flush_mode);
/** Called to access miscellaneous fields. */
int (*ctrl)(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
};
extern const struct bufferevent_ops bufferevent_ops_socket;

View File

@ -531,3 +531,41 @@ bufferevent_enable_locking(struct bufferevent *bufev, void *lock)
#endif
}
int
bufferevent_setfd(struct bufferevent *bev, evutil_socket_t fd)
{
union bufferevent_ctrl_data d;
int res = -1;
d.fd = fd;
BEV_LOCK(bev);
if (bev->be_ops->ctrl)
res = bev->be_ops->ctrl(bev, BEV_CTRL_SET_FD, &d);
BEV_UNLOCK(bev);
return res;
}
evutil_socket_t
bufferevent_getfd(struct bufferevent *bev)
{
union bufferevent_ctrl_data d;
int res = -1;
d.fd = -1;
BEV_LOCK(bev);
if (bev->be_ops->ctrl)
res = bev->be_ops->ctrl(bev, BEV_CTRL_GET_FD, &d);
BEV_UNLOCK(bev);
return (res<0) ? -1 : d.fd;
}
struct bufferevent *
bufferevent_get_underlying(struct bufferevent *bev)
{
union bufferevent_ctrl_data d;
int res = -1;
d.ptr = NULL;
BEV_LOCK(bev);
if (bev->be_ops->ctrl)
res = bev->be_ops->ctrl(bev, BEV_CTRL_GET_UNDERLYING, &d);
BEV_UNLOCK(bev);
return (res<0) ? NULL : d.ptr;
}

View File

@ -65,6 +65,7 @@ static int be_async_disable(struct bufferevent *, short);
static void be_async_destruct(struct bufferevent *);
static void be_async_adj_timeouts(struct bufferevent *);
static int be_async_flush(struct bufferevent *, short, enum bufferevent_flush_mode);
static int be_async_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
const struct bufferevent_ops bufferevent_ops_async = {
"socket_async",
@ -74,9 +75,9 @@ const struct bufferevent_ops bufferevent_ops_async = {
be_async_destruct,
be_async_adj_timeouts,
be_async_flush,
be_async_ctrl,
};
struct bufferevent_async {
struct bufferevent_private bev;
unsigned read_in_progress : 1;
@ -285,3 +286,18 @@ err:
bufferevent_free(&bev_a->bev.bev);
return NULL;
}
static int
be_async_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
union bufferevent_ctrl_data *data)
{
switch (op) {
case BEV_CTRL_GET_FD:
data->fd = _evbuffer_overlapped_get_fd(bev->input);
return 0;
case BEV_CTRL_SET_FD:
case BEV_CTRL_GET_UNDERLYING:
default:
return -1;
}
}

View File

@ -70,6 +70,7 @@ static void be_filter_writecb(struct bufferevent *, void *);
static void be_filter_errorcb(struct bufferevent *, short, void *);
static int be_filter_flush(struct bufferevent *bufev,
short iotype, enum bufferevent_flush_mode mode);
static int be_filter_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
static void bufferevent_filtered_outbuf_cb(struct evbuffer *buf,
const struct evbuffer_cb_info *info, void *arg);
@ -104,6 +105,7 @@ const struct bufferevent_ops bufferevent_ops_filter = {
be_filter_destruct,
be_filter_adj_timeouts,
be_filter_flush,
be_filter_ctrl,
};
/* Given a bufferevent that's really the bev filter of a bufferevent_filtered,
@ -441,3 +443,20 @@ be_filter_flush(struct bufferevent *bufev,
return processed_any;
}
static int
be_filter_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
union bufferevent_ctrl_data *data)
{
struct bufferevent_filtered *bevf;
switch(op) {
case BEV_CTRL_GET_UNDERLYING:
bevf = upcast(bev);
data->ptr = bevf->underlying;
return 0;
case BEV_CTRL_GET_FD:
case BEV_CTRL_SET_FD:
default:
return -1;
}
}

View File

@ -273,4 +273,5 @@ const struct bufferevent_ops bufferevent_ops_pair = {
be_pair_destruct,
be_pair_adj_timeouts,
be_pair_flush,
NULL, /* ctrl */
};

View File

@ -73,6 +73,9 @@ static int be_socket_disable(struct bufferevent *, short);
static void be_socket_destruct(struct bufferevent *);
static void be_socket_adj_timeouts(struct bufferevent *);
static int be_socket_flush(struct bufferevent *, short, enum bufferevent_flush_mode);
static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
static void be_socket_setfd(struct bufferevent *, evutil_socket_t);
const struct bufferevent_ops bufferevent_ops_socket = {
"socket",
@ -82,6 +85,7 @@ const struct bufferevent_ops bufferevent_ops_socket = {
be_socket_destruct,
be_socket_adj_timeouts,
be_socket_flush,
be_socket_ctrl,
};
static int
@ -287,7 +291,7 @@ bufferevent_socket_connect(struct bufferevent *bev,
EVUTIL_CLOSESOCKET(fd);
return -1;
}
bufferevent_setfd(bev, fd);
be_socket_setfd(bev, fd);
}
if (connect(fd, sa, socklen)<0) {
@ -400,8 +404,8 @@ be_socket_flush(struct bufferevent *bev, short iotype,
}
void
bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd)
static void
be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)
{
BEV_LOCK(bufev);
assert(bufev->be_ops == &bufferevent_ops_socket);
@ -458,3 +462,20 @@ done:
BEV_UNLOCK(bufev);
return res;
}
static int
be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
union bufferevent_ctrl_data *data)
{
switch (op) {
case BEV_CTRL_SET_FD:
be_socket_setfd(bev, data->fd);
return 0;
case BEV_CTRL_GET_FD:
data->fd = event_get_fd(&bev->ev_read);
return 0;
case BEV_CTRL_GET_UNDERLYING:
default:
return -1;
}
}

View File

@ -207,11 +207,24 @@ void bufferevent_setcb(struct bufferevent *bufev,
/**
Changes the file descriptor on which the bufferevent operates.
Not supported for all bufferevent types.
@param bufev the bufferevent object for which to change the file descriptor
@param fd the file descriptor to operate on
*/
void bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd);
/**
Returns the file descriptor associated with a bufferevent, or -1 if
no file descriptor is associated with the bufferevent.
*/
evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);
/**
Returns the underlying bufferevent associated with a bufferevent (if
the bufferevent is a wrapper), or NULL if there is no underlying bufferevent.
*/
struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev);
/**
Write data to a bufferevent buffer.

View File

@ -97,6 +97,9 @@ void event_overlapped_init(struct event_overlapped *, iocp_callback cb);
*/
struct evbuffer *evbuffer_overlapped_new(evutil_socket_t fd);
/** XXXX Document (nickm) */
evutil_socket_t _evbuffer_overlapped_get_fd(struct evbuffer *buf);
/** Start reading data onto the end of an overlapped evbuffer.
An evbuffer can only have one read pending at a time. While the read