12b15cb3dSCy Schubert /*
22b15cb3dSCy Schubert * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson
32b15cb3dSCy Schubert *
42b15cb3dSCy Schubert * Redistribution and use in source and binary forms, with or without
52b15cb3dSCy Schubert * modification, are permitted provided that the following conditions
62b15cb3dSCy Schubert * are met:
72b15cb3dSCy Schubert * 1. Redistributions of source code must retain the above copyright
82b15cb3dSCy Schubert * notice, this list of conditions and the following disclaimer.
92b15cb3dSCy Schubert * 2. Redistributions in binary form must reproduce the above copyright
102b15cb3dSCy Schubert * notice, this list of conditions and the following disclaimer in the
112b15cb3dSCy Schubert * documentation and/or other materials provided with the distribution.
122b15cb3dSCy Schubert * 3. The name of the author may not be used to endorse or promote products
132b15cb3dSCy Schubert * derived from this software without specific prior written permission.
142b15cb3dSCy Schubert *
152b15cb3dSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
162b15cb3dSCy Schubert * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
172b15cb3dSCy Schubert * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
182b15cb3dSCy Schubert * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
192b15cb3dSCy Schubert * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
202b15cb3dSCy Schubert * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
212b15cb3dSCy Schubert * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
222b15cb3dSCy Schubert * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
232b15cb3dSCy Schubert * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
242b15cb3dSCy Schubert * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
252b15cb3dSCy Schubert */
262b15cb3dSCy Schubert #include "event2/event-config.h"
272b15cb3dSCy Schubert #include "evconfig-private.h"
282b15cb3dSCy Schubert
292b15cb3dSCy Schubert #include <sys/types.h>
302b15cb3dSCy Schubert
312b15cb3dSCy Schubert #ifdef _WIN32
322b15cb3dSCy Schubert #include <winsock2.h>
332b15cb3dSCy Schubert #endif
342b15cb3dSCy Schubert
352b15cb3dSCy Schubert #include "event2/util.h"
362b15cb3dSCy Schubert #include "event2/buffer.h"
372b15cb3dSCy Schubert #include "event2/bufferevent.h"
382b15cb3dSCy Schubert #include "event2/bufferevent_struct.h"
392b15cb3dSCy Schubert #include "event2/event.h"
402b15cb3dSCy Schubert #include "defer-internal.h"
412b15cb3dSCy Schubert #include "bufferevent-internal.h"
422b15cb3dSCy Schubert #include "mm-internal.h"
432b15cb3dSCy Schubert #include "util-internal.h"
442b15cb3dSCy Schubert
452b15cb3dSCy Schubert struct bufferevent_pair {
462b15cb3dSCy Schubert struct bufferevent_private bev;
472b15cb3dSCy Schubert struct bufferevent_pair *partner;
48a25439b6SCy Schubert /* For ->destruct() lock checking */
49a25439b6SCy Schubert struct bufferevent_pair *unlinked_partner;
502b15cb3dSCy Schubert };
512b15cb3dSCy Schubert
522b15cb3dSCy Schubert
532b15cb3dSCy Schubert /* Given a bufferevent that's really a bev part of a bufferevent_pair,
542b15cb3dSCy Schubert * return that bufferevent_filtered. Returns NULL otherwise.*/
552b15cb3dSCy Schubert static inline struct bufferevent_pair *
upcast(struct bufferevent * bev)562b15cb3dSCy Schubert upcast(struct bufferevent *bev)
572b15cb3dSCy Schubert {
582b15cb3dSCy Schubert struct bufferevent_pair *bev_p;
59*a466cc55SCy Schubert if (!BEV_IS_PAIR(bev))
602b15cb3dSCy Schubert return NULL;
612b15cb3dSCy Schubert bev_p = EVUTIL_UPCAST(bev, struct bufferevent_pair, bev.bev);
62*a466cc55SCy Schubert EVUTIL_ASSERT(BEV_IS_PAIR(&bev_p->bev.bev));
632b15cb3dSCy Schubert return bev_p;
642b15cb3dSCy Schubert }
652b15cb3dSCy Schubert
662b15cb3dSCy Schubert #define downcast(bev_pair) (&(bev_pair)->bev.bev)
672b15cb3dSCy Schubert
682b15cb3dSCy Schubert static inline void
incref_and_lock(struct bufferevent * b)692b15cb3dSCy Schubert incref_and_lock(struct bufferevent *b)
702b15cb3dSCy Schubert {
712b15cb3dSCy Schubert struct bufferevent_pair *bevp;
722b15cb3dSCy Schubert bufferevent_incref_and_lock_(b);
732b15cb3dSCy Schubert bevp = upcast(b);
742b15cb3dSCy Schubert if (bevp->partner)
752b15cb3dSCy Schubert bufferevent_incref_and_lock_(downcast(bevp->partner));
762b15cb3dSCy Schubert }
772b15cb3dSCy Schubert
782b15cb3dSCy Schubert static inline void
decref_and_unlock(struct bufferevent * b)792b15cb3dSCy Schubert decref_and_unlock(struct bufferevent *b)
802b15cb3dSCy Schubert {
812b15cb3dSCy Schubert struct bufferevent_pair *bevp = upcast(b);
822b15cb3dSCy Schubert if (bevp->partner)
832b15cb3dSCy Schubert bufferevent_decref_and_unlock_(downcast(bevp->partner));
842b15cb3dSCy Schubert bufferevent_decref_and_unlock_(b);
852b15cb3dSCy Schubert }
862b15cb3dSCy Schubert
872b15cb3dSCy Schubert /* XXX Handle close */
882b15cb3dSCy Schubert
892b15cb3dSCy Schubert static void be_pair_outbuf_cb(struct evbuffer *,
902b15cb3dSCy Schubert const struct evbuffer_cb_info *, void *);
912b15cb3dSCy Schubert
922b15cb3dSCy Schubert static struct bufferevent_pair *
bufferevent_pair_elt_new(struct event_base * base,int options)932b15cb3dSCy Schubert bufferevent_pair_elt_new(struct event_base *base,
942b15cb3dSCy Schubert int options)
952b15cb3dSCy Schubert {
962b15cb3dSCy Schubert struct bufferevent_pair *bufev;
972b15cb3dSCy Schubert if (! (bufev = mm_calloc(1, sizeof(struct bufferevent_pair))))
982b15cb3dSCy Schubert return NULL;
992b15cb3dSCy Schubert if (bufferevent_init_common_(&bufev->bev, base, &bufferevent_ops_pair,
1002b15cb3dSCy Schubert options)) {
1012b15cb3dSCy Schubert mm_free(bufev);
1022b15cb3dSCy Schubert return NULL;
1032b15cb3dSCy Schubert }
1042b15cb3dSCy Schubert if (!evbuffer_add_cb(bufev->bev.bev.output, be_pair_outbuf_cb, bufev)) {
1052b15cb3dSCy Schubert bufferevent_free(downcast(bufev));
1062b15cb3dSCy Schubert return NULL;
1072b15cb3dSCy Schubert }
1082b15cb3dSCy Schubert
1092b15cb3dSCy Schubert bufferevent_init_generic_timeout_cbs_(&bufev->bev.bev);
1102b15cb3dSCy Schubert
1112b15cb3dSCy Schubert return bufev;
1122b15cb3dSCy Schubert }
1132b15cb3dSCy Schubert
1142b15cb3dSCy Schubert int
bufferevent_pair_new(struct event_base * base,int options,struct bufferevent * pair[2])1152b15cb3dSCy Schubert bufferevent_pair_new(struct event_base *base, int options,
1162b15cb3dSCy Schubert struct bufferevent *pair[2])
1172b15cb3dSCy Schubert {
1182b15cb3dSCy Schubert struct bufferevent_pair *bufev1 = NULL, *bufev2 = NULL;
1192b15cb3dSCy Schubert int tmp_options;
1202b15cb3dSCy Schubert
1212b15cb3dSCy Schubert options |= BEV_OPT_DEFER_CALLBACKS;
1222b15cb3dSCy Schubert tmp_options = options & ~BEV_OPT_THREADSAFE;
1232b15cb3dSCy Schubert
1242b15cb3dSCy Schubert bufev1 = bufferevent_pair_elt_new(base, options);
1252b15cb3dSCy Schubert if (!bufev1)
1262b15cb3dSCy Schubert return -1;
1272b15cb3dSCy Schubert bufev2 = bufferevent_pair_elt_new(base, tmp_options);
1282b15cb3dSCy Schubert if (!bufev2) {
1292b15cb3dSCy Schubert bufferevent_free(downcast(bufev1));
1302b15cb3dSCy Schubert return -1;
1312b15cb3dSCy Schubert }
1322b15cb3dSCy Schubert
1332b15cb3dSCy Schubert if (options & BEV_OPT_THREADSAFE) {
1342b15cb3dSCy Schubert /*XXXX check return */
1352b15cb3dSCy Schubert bufferevent_enable_locking_(downcast(bufev2), bufev1->bev.lock);
1362b15cb3dSCy Schubert }
1372b15cb3dSCy Schubert
1382b15cb3dSCy Schubert bufev1->partner = bufev2;
1392b15cb3dSCy Schubert bufev2->partner = bufev1;
1402b15cb3dSCy Schubert
1412b15cb3dSCy Schubert evbuffer_freeze(downcast(bufev1)->input, 0);
1422b15cb3dSCy Schubert evbuffer_freeze(downcast(bufev1)->output, 1);
1432b15cb3dSCy Schubert evbuffer_freeze(downcast(bufev2)->input, 0);
1442b15cb3dSCy Schubert evbuffer_freeze(downcast(bufev2)->output, 1);
1452b15cb3dSCy Schubert
1462b15cb3dSCy Schubert pair[0] = downcast(bufev1);
1472b15cb3dSCy Schubert pair[1] = downcast(bufev2);
1482b15cb3dSCy Schubert
1492b15cb3dSCy Schubert return 0;
1502b15cb3dSCy Schubert }
1512b15cb3dSCy Schubert
1522b15cb3dSCy Schubert static void
be_pair_transfer(struct bufferevent * src,struct bufferevent * dst,int ignore_wm)1532b15cb3dSCy Schubert be_pair_transfer(struct bufferevent *src, struct bufferevent *dst,
1542b15cb3dSCy Schubert int ignore_wm)
1552b15cb3dSCy Schubert {
1562b15cb3dSCy Schubert size_t dst_size;
1572b15cb3dSCy Schubert size_t n;
1582b15cb3dSCy Schubert
1592b15cb3dSCy Schubert evbuffer_unfreeze(src->output, 1);
1602b15cb3dSCy Schubert evbuffer_unfreeze(dst->input, 0);
1612b15cb3dSCy Schubert
1622b15cb3dSCy Schubert if (dst->wm_read.high) {
1632b15cb3dSCy Schubert dst_size = evbuffer_get_length(dst->input);
1642b15cb3dSCy Schubert if (dst_size < dst->wm_read.high) {
1652b15cb3dSCy Schubert n = dst->wm_read.high - dst_size;
1662b15cb3dSCy Schubert evbuffer_remove_buffer(src->output, dst->input, n);
1672b15cb3dSCy Schubert } else {
1682b15cb3dSCy Schubert if (!ignore_wm)
1692b15cb3dSCy Schubert goto done;
1702b15cb3dSCy Schubert n = evbuffer_get_length(src->output);
1712b15cb3dSCy Schubert evbuffer_add_buffer(dst->input, src->output);
1722b15cb3dSCy Schubert }
1732b15cb3dSCy Schubert } else {
1742b15cb3dSCy Schubert n = evbuffer_get_length(src->output);
1752b15cb3dSCy Schubert evbuffer_add_buffer(dst->input, src->output);
1762b15cb3dSCy Schubert }
1772b15cb3dSCy Schubert
1782b15cb3dSCy Schubert if (n) {
1792b15cb3dSCy Schubert BEV_RESET_GENERIC_READ_TIMEOUT(dst);
1802b15cb3dSCy Schubert
1812b15cb3dSCy Schubert if (evbuffer_get_length(dst->output))
1822b15cb3dSCy Schubert BEV_RESET_GENERIC_WRITE_TIMEOUT(dst);
1832b15cb3dSCy Schubert else
1842b15cb3dSCy Schubert BEV_DEL_GENERIC_WRITE_TIMEOUT(dst);
1852b15cb3dSCy Schubert }
1862b15cb3dSCy Schubert
1872b15cb3dSCy Schubert bufferevent_trigger_nolock_(dst, EV_READ, 0);
1882b15cb3dSCy Schubert bufferevent_trigger_nolock_(src, EV_WRITE, 0);
1892b15cb3dSCy Schubert done:
1902b15cb3dSCy Schubert evbuffer_freeze(src->output, 1);
1912b15cb3dSCy Schubert evbuffer_freeze(dst->input, 0);
1922b15cb3dSCy Schubert }
1932b15cb3dSCy Schubert
1942b15cb3dSCy Schubert static inline int
be_pair_wants_to_talk(struct bufferevent_pair * src,struct bufferevent_pair * dst)1952b15cb3dSCy Schubert be_pair_wants_to_talk(struct bufferevent_pair *src,
1962b15cb3dSCy Schubert struct bufferevent_pair *dst)
1972b15cb3dSCy Schubert {
1982b15cb3dSCy Schubert return (downcast(src)->enabled & EV_WRITE) &&
1992b15cb3dSCy Schubert (downcast(dst)->enabled & EV_READ) &&
2002b15cb3dSCy Schubert !dst->bev.read_suspended &&
2012b15cb3dSCy Schubert evbuffer_get_length(downcast(src)->output);
2022b15cb3dSCy Schubert }
2032b15cb3dSCy Schubert
2042b15cb3dSCy Schubert static void
be_pair_outbuf_cb(struct evbuffer * outbuf,const struct evbuffer_cb_info * info,void * arg)2052b15cb3dSCy Schubert be_pair_outbuf_cb(struct evbuffer *outbuf,
2062b15cb3dSCy Schubert const struct evbuffer_cb_info *info, void *arg)
2072b15cb3dSCy Schubert {
2082b15cb3dSCy Schubert struct bufferevent_pair *bev_pair = arg;
2092b15cb3dSCy Schubert struct bufferevent_pair *partner = bev_pair->partner;
2102b15cb3dSCy Schubert
2112b15cb3dSCy Schubert incref_and_lock(downcast(bev_pair));
2122b15cb3dSCy Schubert
2132b15cb3dSCy Schubert if (info->n_added > info->n_deleted && partner) {
2142b15cb3dSCy Schubert /* We got more data. If the other side's reading, then
2152b15cb3dSCy Schubert hand it over. */
2162b15cb3dSCy Schubert if (be_pair_wants_to_talk(bev_pair, partner)) {
2172b15cb3dSCy Schubert be_pair_transfer(downcast(bev_pair), downcast(partner), 0);
2182b15cb3dSCy Schubert }
2192b15cb3dSCy Schubert }
2202b15cb3dSCy Schubert
2212b15cb3dSCy Schubert decref_and_unlock(downcast(bev_pair));
2222b15cb3dSCy Schubert }
2232b15cb3dSCy Schubert
2242b15cb3dSCy Schubert static int
be_pair_enable(struct bufferevent * bufev,short events)2252b15cb3dSCy Schubert be_pair_enable(struct bufferevent *bufev, short events)
2262b15cb3dSCy Schubert {
2272b15cb3dSCy Schubert struct bufferevent_pair *bev_p = upcast(bufev);
2282b15cb3dSCy Schubert struct bufferevent_pair *partner = bev_p->partner;
2292b15cb3dSCy Schubert
2302b15cb3dSCy Schubert incref_and_lock(bufev);
2312b15cb3dSCy Schubert
2322b15cb3dSCy Schubert if (events & EV_READ) {
2332b15cb3dSCy Schubert BEV_RESET_GENERIC_READ_TIMEOUT(bufev);
2342b15cb3dSCy Schubert }
2352b15cb3dSCy Schubert if ((events & EV_WRITE) && evbuffer_get_length(bufev->output))
2362b15cb3dSCy Schubert BEV_RESET_GENERIC_WRITE_TIMEOUT(bufev);
2372b15cb3dSCy Schubert
2382b15cb3dSCy Schubert /* We're starting to read! Does the other side have anything to write?*/
2392b15cb3dSCy Schubert if ((events & EV_READ) && partner &&
2402b15cb3dSCy Schubert be_pair_wants_to_talk(partner, bev_p)) {
2412b15cb3dSCy Schubert be_pair_transfer(downcast(partner), bufev, 0);
2422b15cb3dSCy Schubert }
2432b15cb3dSCy Schubert /* We're starting to write! Does the other side want to read? */
2442b15cb3dSCy Schubert if ((events & EV_WRITE) && partner &&
2452b15cb3dSCy Schubert be_pair_wants_to_talk(bev_p, partner)) {
2462b15cb3dSCy Schubert be_pair_transfer(bufev, downcast(partner), 0);
2472b15cb3dSCy Schubert }
2482b15cb3dSCy Schubert decref_and_unlock(bufev);
2492b15cb3dSCy Schubert return 0;
2502b15cb3dSCy Schubert }
2512b15cb3dSCy Schubert
2522b15cb3dSCy Schubert static int
be_pair_disable(struct bufferevent * bev,short events)2532b15cb3dSCy Schubert be_pair_disable(struct bufferevent *bev, short events)
2542b15cb3dSCy Schubert {
2552b15cb3dSCy Schubert if (events & EV_READ) {
2562b15cb3dSCy Schubert BEV_DEL_GENERIC_READ_TIMEOUT(bev);
2572b15cb3dSCy Schubert }
2582b15cb3dSCy Schubert if (events & EV_WRITE) {
2592b15cb3dSCy Schubert BEV_DEL_GENERIC_WRITE_TIMEOUT(bev);
2602b15cb3dSCy Schubert }
2612b15cb3dSCy Schubert return 0;
2622b15cb3dSCy Schubert }
2632b15cb3dSCy Schubert
2642b15cb3dSCy Schubert static void
be_pair_unlink(struct bufferevent * bev)2652b15cb3dSCy Schubert be_pair_unlink(struct bufferevent *bev)
2662b15cb3dSCy Schubert {
2672b15cb3dSCy Schubert struct bufferevent_pair *bev_p = upcast(bev);
2682b15cb3dSCy Schubert
2692b15cb3dSCy Schubert if (bev_p->partner) {
270a25439b6SCy Schubert bev_p->unlinked_partner = bev_p->partner;
2712b15cb3dSCy Schubert bev_p->partner->partner = NULL;
2722b15cb3dSCy Schubert bev_p->partner = NULL;
2732b15cb3dSCy Schubert }
2742b15cb3dSCy Schubert }
2752b15cb3dSCy Schubert
276a25439b6SCy Schubert /* Free *shared* lock in the latest be (since we share it between two of them). */
277a25439b6SCy Schubert static void
be_pair_destruct(struct bufferevent * bev)278a25439b6SCy Schubert be_pair_destruct(struct bufferevent *bev)
279a25439b6SCy Schubert {
280a25439b6SCy Schubert struct bufferevent_pair *bev_p = upcast(bev);
281a25439b6SCy Schubert
282a25439b6SCy Schubert /* Transfer ownership of the lock into partner, otherwise we will use
283a25439b6SCy Schubert * already free'd lock during freeing second bev, see next example:
284a25439b6SCy Schubert *
285a25439b6SCy Schubert * bev1->own_lock = 1
286a25439b6SCy Schubert * bev2->own_lock = 0
287a25439b6SCy Schubert * bev2->lock = bev1->lock
288a25439b6SCy Schubert *
289a25439b6SCy Schubert * bufferevent_free(bev1) # refcnt == 0 -> unlink
290a25439b6SCy Schubert * bufferevent_free(bev2) # refcnt == 0 -> unlink
291a25439b6SCy Schubert *
292a25439b6SCy Schubert * event_base_free() -> finilizers -> EVTHREAD_FREE_LOCK(bev1->lock)
293a25439b6SCy Schubert * -> BEV_LOCK(bev2->lock) <-- already freed
294a25439b6SCy Schubert *
295a25439b6SCy Schubert * Where bev1 == pair[0], bev2 == pair[1].
296a25439b6SCy Schubert */
297a25439b6SCy Schubert if (bev_p->unlinked_partner && bev_p->bev.own_lock) {
298a25439b6SCy Schubert bev_p->unlinked_partner->bev.own_lock = 1;
299a25439b6SCy Schubert bev_p->bev.own_lock = 0;
300a25439b6SCy Schubert }
301a25439b6SCy Schubert bev_p->unlinked_partner = NULL;
302a25439b6SCy Schubert }
303a25439b6SCy Schubert
3042b15cb3dSCy Schubert static int
be_pair_flush(struct bufferevent * bev,short iotype,enum bufferevent_flush_mode mode)3052b15cb3dSCy Schubert be_pair_flush(struct bufferevent *bev, short iotype,
3062b15cb3dSCy Schubert enum bufferevent_flush_mode mode)
3072b15cb3dSCy Schubert {
3082b15cb3dSCy Schubert struct bufferevent_pair *bev_p = upcast(bev);
3092b15cb3dSCy Schubert struct bufferevent *partner;
310*a466cc55SCy Schubert
3112b15cb3dSCy Schubert if (!bev_p->partner)
3122b15cb3dSCy Schubert return -1;
3132b15cb3dSCy Schubert
3142b15cb3dSCy Schubert if (mode == BEV_NORMAL)
3152b15cb3dSCy Schubert return 0;
3162b15cb3dSCy Schubert
317*a466cc55SCy Schubert incref_and_lock(bev);
318*a466cc55SCy Schubert
319*a466cc55SCy Schubert partner = downcast(bev_p->partner);
320*a466cc55SCy Schubert
3212b15cb3dSCy Schubert if ((iotype & EV_READ) != 0)
3222b15cb3dSCy Schubert be_pair_transfer(partner, bev, 1);
3232b15cb3dSCy Schubert
3242b15cb3dSCy Schubert if ((iotype & EV_WRITE) != 0)
3252b15cb3dSCy Schubert be_pair_transfer(bev, partner, 1);
3262b15cb3dSCy Schubert
3272b15cb3dSCy Schubert if (mode == BEV_FINISHED) {
328*a466cc55SCy Schubert short what = BEV_EVENT_EOF;
329*a466cc55SCy Schubert if (iotype & EV_READ)
330*a466cc55SCy Schubert what |= BEV_EVENT_WRITING;
331*a466cc55SCy Schubert if (iotype & EV_WRITE)
332*a466cc55SCy Schubert what |= BEV_EVENT_READING;
333*a466cc55SCy Schubert bufferevent_run_eventcb_(partner, what, 0);
3342b15cb3dSCy Schubert }
3352b15cb3dSCy Schubert decref_and_unlock(bev);
3362b15cb3dSCy Schubert return 0;
3372b15cb3dSCy Schubert }
3382b15cb3dSCy Schubert
3392b15cb3dSCy Schubert struct bufferevent *
bufferevent_pair_get_partner(struct bufferevent * bev)3402b15cb3dSCy Schubert bufferevent_pair_get_partner(struct bufferevent *bev)
3412b15cb3dSCy Schubert {
3422b15cb3dSCy Schubert struct bufferevent_pair *bev_p;
3432b15cb3dSCy Schubert struct bufferevent *partner = NULL;
3442b15cb3dSCy Schubert bev_p = upcast(bev);
3452b15cb3dSCy Schubert if (! bev_p)
3462b15cb3dSCy Schubert return NULL;
3472b15cb3dSCy Schubert
3482b15cb3dSCy Schubert incref_and_lock(bev);
3492b15cb3dSCy Schubert if (bev_p->partner)
3502b15cb3dSCy Schubert partner = downcast(bev_p->partner);
3512b15cb3dSCy Schubert decref_and_unlock(bev);
3522b15cb3dSCy Schubert return partner;
3532b15cb3dSCy Schubert }
3542b15cb3dSCy Schubert
3552b15cb3dSCy Schubert const struct bufferevent_ops bufferevent_ops_pair = {
3562b15cb3dSCy Schubert "pair_elt",
3572b15cb3dSCy Schubert evutil_offsetof(struct bufferevent_pair, bev.bev),
3582b15cb3dSCy Schubert be_pair_enable,
3592b15cb3dSCy Schubert be_pair_disable,
3602b15cb3dSCy Schubert be_pair_unlink,
361a25439b6SCy Schubert be_pair_destruct,
3622b15cb3dSCy Schubert bufferevent_generic_adj_timeouts_,
3632b15cb3dSCy Schubert be_pair_flush,
3642b15cb3dSCy Schubert NULL, /* ctrl */
3652b15cb3dSCy Schubert };
366