xref: /libevent-2.1.12/bufferevent_pair.c (revision 56faf02b)
123085c92SNick Mathewson /*
2e49e2891SNick Mathewson  * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson
323085c92SNick Mathewson  *
423085c92SNick Mathewson  * Redistribution and use in source and binary forms, with or without
523085c92SNick Mathewson  * modification, are permitted provided that the following conditions
623085c92SNick Mathewson  * are met:
723085c92SNick Mathewson  * 1. Redistributions of source code must retain the above copyright
823085c92SNick Mathewson  *    notice, this list of conditions and the following disclaimer.
923085c92SNick Mathewson  * 2. Redistributions in binary form must reproduce the above copyright
1023085c92SNick Mathewson  *    notice, this list of conditions and the following disclaimer in the
1123085c92SNick Mathewson  *    documentation and/or other materials provided with the distribution.
1223085c92SNick Mathewson  * 3. The name of the author may not be used to endorse or promote products
1323085c92SNick Mathewson  *    derived from this software without specific prior written permission.
1423085c92SNick Mathewson  *
1523085c92SNick Mathewson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1623085c92SNick Mathewson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1723085c92SNick Mathewson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1823085c92SNick Mathewson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1923085c92SNick Mathewson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2023085c92SNick Mathewson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2123085c92SNick Mathewson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2223085c92SNick Mathewson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2323085c92SNick Mathewson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2423085c92SNick Mathewson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2523085c92SNick Mathewson  */
260915ca0aSKevin Bowling #include "event2/event-config.h"
270915ca0aSKevin Bowling #include "evconfig-private.h"
2823085c92SNick Mathewson 
2923085c92SNick Mathewson #include <sys/types.h>
3023085c92SNick Mathewson 
319f560bfaSNick Mathewson #ifdef _WIN32
32e865eb93SNick Mathewson #include <winsock2.h>
33e865eb93SNick Mathewson #endif
34e865eb93SNick Mathewson 
3523085c92SNick Mathewson #include "event2/util.h"
3623085c92SNick Mathewson #include "event2/buffer.h"
3723085c92SNick Mathewson #include "event2/bufferevent.h"
3823085c92SNick Mathewson #include "event2/bufferevent_struct.h"
3923085c92SNick Mathewson #include "event2/event.h"
4023085c92SNick Mathewson #include "defer-internal.h"
4123085c92SNick Mathewson #include "bufferevent-internal.h"
4223085c92SNick Mathewson #include "mm-internal.h"
430b47b125SNick Mathewson #include "util-internal.h"
4423085c92SNick Mathewson 
4523085c92SNick Mathewson struct bufferevent_pair {
461becc4c4SNick Mathewson 	struct bufferevent_private bev;
4723085c92SNick Mathewson 	struct bufferevent_pair *partner;
4892a359eeSAzat Khuzhin 	/* For ->destruct() lock checking */
4992a359eeSAzat Khuzhin 	struct bufferevent_pair *unlinked_partner;
5023085c92SNick Mathewson };
5123085c92SNick Mathewson 
5223085c92SNick Mathewson 
5323085c92SNick Mathewson /* Given a bufferevent that's really a bev part of a bufferevent_pair,
5423085c92SNick Mathewson  * return that bufferevent_filtered. Returns NULL otherwise.*/
5523085c92SNick Mathewson static inline struct bufferevent_pair *
upcast(struct bufferevent * bev)5623085c92SNick Mathewson upcast(struct bufferevent *bev)
5723085c92SNick Mathewson {
5823085c92SNick Mathewson 	struct bufferevent_pair *bev_p;
59*56faf02bSDominic Chen 	if (!BEV_IS_PAIR(bev))
6023085c92SNick Mathewson 		return NULL;
611becc4c4SNick Mathewson 	bev_p = EVUTIL_UPCAST(bev, struct bufferevent_pair, bev.bev);
62*56faf02bSDominic Chen 	EVUTIL_ASSERT(BEV_IS_PAIR(&bev_p->bev.bev));
6323085c92SNick Mathewson 	return bev_p;
6423085c92SNick Mathewson }
6523085c92SNick Mathewson 
661becc4c4SNick Mathewson #define downcast(bev_pair) (&(bev_pair)->bev.bev)
6723085c92SNick Mathewson 
68a62283a9SNick Mathewson static inline void
incref_and_lock(struct bufferevent * b)69a62283a9SNick Mathewson incref_and_lock(struct bufferevent *b)
70a62283a9SNick Mathewson {
71a62283a9SNick Mathewson 	struct bufferevent_pair *bevp;
72cb9da0bfSNick Mathewson 	bufferevent_incref_and_lock_(b);
73a62283a9SNick Mathewson 	bevp = upcast(b);
74a62283a9SNick Mathewson 	if (bevp->partner)
75cb9da0bfSNick Mathewson 		bufferevent_incref_and_lock_(downcast(bevp->partner));
76a62283a9SNick Mathewson }
77a62283a9SNick Mathewson 
78a62283a9SNick Mathewson static inline void
decref_and_unlock(struct bufferevent * b)79a62283a9SNick Mathewson decref_and_unlock(struct bufferevent *b)
80a62283a9SNick Mathewson {
81a62283a9SNick Mathewson 	struct bufferevent_pair *bevp = upcast(b);
82a62283a9SNick Mathewson 	if (bevp->partner)
83cb9da0bfSNick Mathewson 		bufferevent_decref_and_unlock_(downcast(bevp->partner));
84cb9da0bfSNick Mathewson 	bufferevent_decref_and_unlock_(b);
85a62283a9SNick Mathewson }
86a62283a9SNick Mathewson 
8723085c92SNick Mathewson /* XXX Handle close */
8823085c92SNick Mathewson 
8923085c92SNick Mathewson static void be_pair_outbuf_cb(struct evbuffer *,
9023085c92SNick Mathewson     const struct evbuffer_cb_info *, void *);
9123085c92SNick Mathewson 
9223085c92SNick Mathewson static struct bufferevent_pair *
bufferevent_pair_elt_new(struct event_base * base,int options)9323085c92SNick Mathewson bufferevent_pair_elt_new(struct event_base *base,
94b73ad7bcSNick Mathewson     int options)
9523085c92SNick Mathewson {
9623085c92SNick Mathewson 	struct bufferevent_pair *bufev;
9723085c92SNick Mathewson 	if (! (bufev = mm_calloc(1, sizeof(struct bufferevent_pair))))
9823085c92SNick Mathewson 		return NULL;
998ac3c4c2SNick Mathewson 	if (bufferevent_init_common_(&bufev->bev, base, &bufferevent_ops_pair,
10023085c92SNick Mathewson 		options)) {
10123085c92SNick Mathewson 		mm_free(bufev);
10223085c92SNick Mathewson 		return NULL;
10323085c92SNick Mathewson 	}
1041becc4c4SNick Mathewson 	if (!evbuffer_add_cb(bufev->bev.bev.output, be_pair_outbuf_cb, bufev)) {
10523085c92SNick Mathewson 		bufferevent_free(downcast(bufev));
10623085c92SNick Mathewson 		return NULL;
10723085c92SNick Mathewson 	}
10823085c92SNick Mathewson 
109cb9da0bfSNick Mathewson 	bufferevent_init_generic_timeout_cbs_(&bufev->bev.bev);
11034574db0SNick Mathewson 
11123085c92SNick Mathewson 	return bufev;
11223085c92SNick Mathewson }
11323085c92SNick Mathewson 
11423085c92SNick Mathewson int
bufferevent_pair_new(struct event_base * base,int options,struct bufferevent * pair[2])115b73ad7bcSNick Mathewson bufferevent_pair_new(struct event_base *base, int options,
11623085c92SNick Mathewson     struct bufferevent *pair[2])
11723085c92SNick Mathewson {
11823085c92SNick Mathewson 	struct bufferevent_pair *bufev1 = NULL, *bufev2 = NULL;
119b73ad7bcSNick Mathewson 	int tmp_options;
120a98a512bSNick Mathewson 
121a98a512bSNick Mathewson 	options |= BEV_OPT_DEFER_CALLBACKS;
122a98a512bSNick Mathewson 	tmp_options = options & ~BEV_OPT_THREADSAFE;
12323085c92SNick Mathewson 
12423085c92SNick Mathewson 	bufev1 = bufferevent_pair_elt_new(base, options);
12523085c92SNick Mathewson 	if (!bufev1)
12623085c92SNick Mathewson 		return -1;
127915193e7SNick Mathewson 	bufev2 = bufferevent_pair_elt_new(base, tmp_options);
12823085c92SNick Mathewson 	if (!bufev2) {
12923085c92SNick Mathewson 		bufferevent_free(downcast(bufev1));
13023085c92SNick Mathewson 		return -1;
13123085c92SNick Mathewson 	}
13223085c92SNick Mathewson 
133915193e7SNick Mathewson 	if (options & BEV_OPT_THREADSAFE) {
134915193e7SNick Mathewson 		/*XXXX check return */
1358ac3c4c2SNick Mathewson 		bufferevent_enable_locking_(downcast(bufev2), bufev1->bev.lock);
136915193e7SNick Mathewson 	}
137915193e7SNick Mathewson 
13823085c92SNick Mathewson 	bufev1->partner = bufev2;
13923085c92SNick Mathewson 	bufev2->partner = bufev1;
14023085c92SNick Mathewson 
1411becc4c4SNick Mathewson 	evbuffer_freeze(downcast(bufev1)->input, 0);
1421becc4c4SNick Mathewson 	evbuffer_freeze(downcast(bufev1)->output, 1);
1431becc4c4SNick Mathewson 	evbuffer_freeze(downcast(bufev2)->input, 0);
1441becc4c4SNick Mathewson 	evbuffer_freeze(downcast(bufev2)->output, 1);
1458dec59bbSNick Mathewson 
14623085c92SNick Mathewson 	pair[0] = downcast(bufev1);
14723085c92SNick Mathewson 	pair[1] = downcast(bufev2);
14823085c92SNick Mathewson 
14923085c92SNick Mathewson 	return 0;
15023085c92SNick Mathewson }
15123085c92SNick Mathewson 
15223085c92SNick Mathewson static void
be_pair_transfer(struct bufferevent * src,struct bufferevent * dst,int ignore_wm)15323085c92SNick Mathewson be_pair_transfer(struct bufferevent *src, struct bufferevent *dst,
15423085c92SNick Mathewson     int ignore_wm)
15523085c92SNick Mathewson {
15661ee18b8SOndřej Kuzník 	size_t dst_size;
15723085c92SNick Mathewson 	size_t n;
15823085c92SNick Mathewson 
1598dec59bbSNick Mathewson 	evbuffer_unfreeze(src->output, 1);
1608dec59bbSNick Mathewson 	evbuffer_unfreeze(dst->input, 0);
1618dec59bbSNick Mathewson 
16223085c92SNick Mathewson 	if (dst->wm_read.high) {
1638ee9f9c1SNicholas Marriott 		dst_size = evbuffer_get_length(dst->input);
16423085c92SNick Mathewson 		if (dst_size < dst->wm_read.high) {
16523085c92SNick Mathewson 			n = dst->wm_read.high - dst_size;
16623085c92SNick Mathewson 			evbuffer_remove_buffer(src->output, dst->input, n);
16723085c92SNick Mathewson 		} else {
16823085c92SNick Mathewson 			if (!ignore_wm)
1698dec59bbSNick Mathewson 				goto done;
170d3288293SNick Mathewson 			n = evbuffer_get_length(src->output);
17123085c92SNick Mathewson 			evbuffer_add_buffer(dst->input, src->output);
17223085c92SNick Mathewson 		}
17323085c92SNick Mathewson 	} else {
174d3288293SNick Mathewson 		n = evbuffer_get_length(src->output);
17523085c92SNick Mathewson 		evbuffer_add_buffer(dst->input, src->output);
17623085c92SNick Mathewson 	}
17723085c92SNick Mathewson 
178d3288293SNick Mathewson 	if (n) {
17934574db0SNick Mathewson 		BEV_RESET_GENERIC_READ_TIMEOUT(dst);
180d3288293SNick Mathewson 
181d3288293SNick Mathewson 		if (evbuffer_get_length(dst->output))
18234574db0SNick Mathewson 			BEV_RESET_GENERIC_WRITE_TIMEOUT(dst);
183d3288293SNick Mathewson 		else
184d3288293SNick Mathewson 			BEV_DEL_GENERIC_WRITE_TIMEOUT(dst);
185d3288293SNick Mathewson 	}
18634574db0SNick Mathewson 
18761ee18b8SOndřej Kuzník 	bufferevent_trigger_nolock_(dst, EV_READ, 0);
18861ee18b8SOndřej Kuzník 	bufferevent_trigger_nolock_(src, EV_WRITE, 0);
1898dec59bbSNick Mathewson done:
1908dec59bbSNick Mathewson 	evbuffer_freeze(src->output, 1);
1918dec59bbSNick Mathewson 	evbuffer_freeze(dst->input, 0);
19223085c92SNick Mathewson }
19323085c92SNick Mathewson 
19423085c92SNick Mathewson static inline int
be_pair_wants_to_talk(struct bufferevent_pair * src,struct bufferevent_pair * dst)1951becc4c4SNick Mathewson be_pair_wants_to_talk(struct bufferevent_pair *src,
1961becc4c4SNick Mathewson     struct bufferevent_pair *dst)
19723085c92SNick Mathewson {
1981becc4c4SNick Mathewson 	return (downcast(src)->enabled & EV_WRITE) &&
1991becc4c4SNick Mathewson 	    (downcast(dst)->enabled & EV_READ) &&
2001becc4c4SNick Mathewson 	    !dst->bev.read_suspended &&
2011becc4c4SNick Mathewson 	    evbuffer_get_length(downcast(src)->output);
20223085c92SNick Mathewson }
20323085c92SNick Mathewson 
20423085c92SNick Mathewson static void
be_pair_outbuf_cb(struct evbuffer * outbuf,const struct evbuffer_cb_info * info,void * arg)20523085c92SNick Mathewson be_pair_outbuf_cb(struct evbuffer *outbuf,
20623085c92SNick Mathewson     const struct evbuffer_cb_info *info, void *arg)
20723085c92SNick Mathewson {
20823085c92SNick Mathewson 	struct bufferevent_pair *bev_pair = arg;
20923085c92SNick Mathewson 	struct bufferevent_pair *partner = bev_pair->partner;
21023085c92SNick Mathewson 
211a62283a9SNick Mathewson 	incref_and_lock(downcast(bev_pair));
212a62283a9SNick Mathewson 
21323085c92SNick Mathewson 	if (info->n_added > info->n_deleted && partner) {
21423085c92SNick Mathewson 		/* We got more data.  If the other side's reading, then
21523085c92SNick Mathewson 		   hand it over. */
2161becc4c4SNick Mathewson 		if (be_pair_wants_to_talk(bev_pair, partner)) {
21723085c92SNick Mathewson 			be_pair_transfer(downcast(bev_pair), downcast(partner), 0);
21823085c92SNick Mathewson 		}
21923085c92SNick Mathewson 	}
220a62283a9SNick Mathewson 
221a62283a9SNick Mathewson 	decref_and_unlock(downcast(bev_pair));
22223085c92SNick Mathewson }
22323085c92SNick Mathewson 
22423085c92SNick Mathewson static int
be_pair_enable(struct bufferevent * bufev,short events)22523085c92SNick Mathewson be_pair_enable(struct bufferevent *bufev, short events)
22623085c92SNick Mathewson {
22723085c92SNick Mathewson 	struct bufferevent_pair *bev_p = upcast(bufev);
22823085c92SNick Mathewson 	struct bufferevent_pair *partner = bev_p->partner;
22923085c92SNick Mathewson 
230a62283a9SNick Mathewson 	incref_and_lock(bufev);
231a62283a9SNick Mathewson 
232d3288293SNick Mathewson 	if (events & EV_READ) {
233d3288293SNick Mathewson 		BEV_RESET_GENERIC_READ_TIMEOUT(bufev);
234d3288293SNick Mathewson 	}
235d3288293SNick Mathewson 	if ((events & EV_WRITE) && evbuffer_get_length(bufev->output))
236d3288293SNick Mathewson 		BEV_RESET_GENERIC_WRITE_TIMEOUT(bufev);
23734574db0SNick Mathewson 
23823085c92SNick Mathewson 	/* We're starting to read! Does the other side have anything to write?*/
23923085c92SNick Mathewson 	if ((events & EV_READ) && partner &&
2401becc4c4SNick Mathewson 	    be_pair_wants_to_talk(partner, bev_p)) {
24123085c92SNick Mathewson 		be_pair_transfer(downcast(partner), bufev, 0);
24223085c92SNick Mathewson 	}
24323085c92SNick Mathewson 	/* We're starting to write! Does the other side want to read? */
24423085c92SNick Mathewson 	if ((events & EV_WRITE) && partner &&
2451becc4c4SNick Mathewson 	    be_pair_wants_to_talk(bev_p, partner)) {
24623085c92SNick Mathewson 		be_pair_transfer(bufev, downcast(partner), 0);
24723085c92SNick Mathewson 	}
248a62283a9SNick Mathewson 	decref_and_unlock(bufev);
24923085c92SNick Mathewson 	return 0;
25023085c92SNick Mathewson }
25123085c92SNick Mathewson 
25223085c92SNick Mathewson static int
be_pair_disable(struct bufferevent * bev,short events)25323085c92SNick Mathewson be_pair_disable(struct bufferevent *bev, short events)
25423085c92SNick Mathewson {
255d3288293SNick Mathewson 	if (events & EV_READ) {
256d3288293SNick Mathewson 		BEV_DEL_GENERIC_READ_TIMEOUT(bev);
257d3288293SNick Mathewson 	}
258c0e425abSNick Mathewson 	if (events & EV_WRITE) {
259d3288293SNick Mathewson 		BEV_DEL_GENERIC_WRITE_TIMEOUT(bev);
260c0e425abSNick Mathewson 	}
261d3288293SNick Mathewson 	return 0;
26223085c92SNick Mathewson }
26323085c92SNick Mathewson 
26423085c92SNick Mathewson static void
be_pair_unlink(struct bufferevent * bev)26502fbf687SNick Mathewson be_pair_unlink(struct bufferevent *bev)
26623085c92SNick Mathewson {
26723085c92SNick Mathewson 	struct bufferevent_pair *bev_p = upcast(bev);
26823085c92SNick Mathewson 
26923085c92SNick Mathewson 	if (bev_p->partner) {
27092a359eeSAzat Khuzhin 		bev_p->unlinked_partner = bev_p->partner;
27123085c92SNick Mathewson 		bev_p->partner->partner = NULL;
27223085c92SNick Mathewson 		bev_p->partner = NULL;
27323085c92SNick Mathewson 	}
27423085c92SNick Mathewson }
27523085c92SNick Mathewson 
27692a359eeSAzat Khuzhin /* Free *shared* lock in the latest be (since we share it between two of them). */
27792a359eeSAzat Khuzhin static void
be_pair_destruct(struct bufferevent * bev)27892a359eeSAzat Khuzhin be_pair_destruct(struct bufferevent *bev)
27992a359eeSAzat Khuzhin {
28092a359eeSAzat Khuzhin 	struct bufferevent_pair *bev_p = upcast(bev);
28192a359eeSAzat Khuzhin 
28292a359eeSAzat Khuzhin 	/* Transfer ownership of the lock into partner, otherwise we will use
28392a359eeSAzat Khuzhin 	 * already free'd lock during freeing second bev, see next example:
28492a359eeSAzat Khuzhin 	 *
28592a359eeSAzat Khuzhin 	 * bev1->own_lock = 1
28692a359eeSAzat Khuzhin 	 * bev2->own_lock = 0
28792a359eeSAzat Khuzhin 	 * bev2->lock = bev1->lock
28892a359eeSAzat Khuzhin 	 *
28992a359eeSAzat Khuzhin 	 * bufferevent_free(bev1) # refcnt == 0 -> unlink
29092a359eeSAzat Khuzhin 	 * bufferevent_free(bev2) # refcnt == 0 -> unlink
29192a359eeSAzat Khuzhin 	 *
29292a359eeSAzat Khuzhin 	 * event_base_free() -> finilizers -> EVTHREAD_FREE_LOCK(bev1->lock)
29392a359eeSAzat Khuzhin 	 *                                 -> BEV_LOCK(bev2->lock) <-- already freed
29492a359eeSAzat Khuzhin 	 *
29592a359eeSAzat Khuzhin 	 * Where bev1 == pair[0], bev2 == pair[1].
29692a359eeSAzat Khuzhin 	 */
29792a359eeSAzat Khuzhin 	if (bev_p->unlinked_partner && bev_p->bev.own_lock) {
29892a359eeSAzat Khuzhin 		bev_p->unlinked_partner->bev.own_lock = 1;
29992a359eeSAzat Khuzhin 		bev_p->bev.own_lock = 0;
30092a359eeSAzat Khuzhin 	}
30192a359eeSAzat Khuzhin 	bev_p->unlinked_partner = NULL;
30292a359eeSAzat Khuzhin }
30392a359eeSAzat Khuzhin 
30423085c92SNick Mathewson static int
be_pair_flush(struct bufferevent * bev,short iotype,enum bufferevent_flush_mode mode)30523085c92SNick Mathewson be_pair_flush(struct bufferevent *bev, short iotype,
30623085c92SNick Mathewson     enum bufferevent_flush_mode mode)
30723085c92SNick Mathewson {
30823085c92SNick Mathewson 	struct bufferevent_pair *bev_p = upcast(bev);
30923085c92SNick Mathewson 	struct bufferevent *partner;
310f45d39d1SBill Vaughan 
31123085c92SNick Mathewson 	if (!bev_p->partner)
31223085c92SNick Mathewson 		return -1;
31323085c92SNick Mathewson 
31423085c92SNick Mathewson 	if (mode == BEV_NORMAL)
31523085c92SNick Mathewson 		return 0;
31623085c92SNick Mathewson 
317f45d39d1SBill Vaughan 	incref_and_lock(bev);
318f45d39d1SBill Vaughan 
319f45d39d1SBill Vaughan 	partner = downcast(bev_p->partner);
320f45d39d1SBill Vaughan 
32123085c92SNick Mathewson 	if ((iotype & EV_READ) != 0)
32223085c92SNick Mathewson 		be_pair_transfer(partner, bev, 1);
32323085c92SNick Mathewson 
32423085c92SNick Mathewson 	if ((iotype & EV_WRITE) != 0)
32523085c92SNick Mathewson 		be_pair_transfer(bev, partner, 1);
32623085c92SNick Mathewson 
32723085c92SNick Mathewson 	if (mode == BEV_FINISHED) {
32828518896SDavid Paschich 		short what = BEV_EVENT_EOF;
32928518896SDavid Paschich 		if (iotype & EV_READ)
33028518896SDavid Paschich 			what |= BEV_EVENT_WRITING;
33128518896SDavid Paschich 		if (iotype & EV_WRITE)
33228518896SDavid Paschich 			what |= BEV_EVENT_READING;
33328518896SDavid Paschich 		bufferevent_run_eventcb_(partner, what, 0);
33423085c92SNick Mathewson 	}
335a62283a9SNick Mathewson 	decref_and_unlock(bev);
33623085c92SNick Mathewson 	return 0;
33723085c92SNick Mathewson }
33823085c92SNick Mathewson 
33917a8e2d7SNick Mathewson struct bufferevent *
bufferevent_pair_get_partner(struct bufferevent * bev)34017a8e2d7SNick Mathewson bufferevent_pair_get_partner(struct bufferevent *bev)
34117a8e2d7SNick Mathewson {
34217a8e2d7SNick Mathewson 	struct bufferevent_pair *bev_p;
343f2428a28SNick Mathewson 	struct bufferevent *partner = NULL;
34417a8e2d7SNick Mathewson 	bev_p = upcast(bev);
34517a8e2d7SNick Mathewson 	if (! bev_p)
34617a8e2d7SNick Mathewson 		return NULL;
34717a8e2d7SNick Mathewson 
34817a8e2d7SNick Mathewson 	incref_and_lock(bev);
349f2428a28SNick Mathewson 	if (bev_p->partner)
35017a8e2d7SNick Mathewson 		partner = downcast(bev_p->partner);
35117a8e2d7SNick Mathewson 	decref_and_unlock(bev);
35217a8e2d7SNick Mathewson 	return partner;
35317a8e2d7SNick Mathewson }
35417a8e2d7SNick Mathewson 
35523085c92SNick Mathewson const struct bufferevent_ops bufferevent_ops_pair = {
35623085c92SNick Mathewson 	"pair_elt",
357657d1b6dSNick Mathewson 	evutil_offsetof(struct bufferevent_pair, bev.bev),
35823085c92SNick Mathewson 	be_pair_enable,
35923085c92SNick Mathewson 	be_pair_disable,
36002fbf687SNick Mathewson 	be_pair_unlink,
36192a359eeSAzat Khuzhin 	be_pair_destruct,
362cb9da0bfSNick Mathewson 	bufferevent_generic_adj_timeouts_,
36323085c92SNick Mathewson 	be_pair_flush,
36431d89f27SNick Mathewson 	NULL, /* ctrl */
36523085c92SNick Mathewson };
366