#include "event2/listener.h"
#include "event2/event.h"
#include "event2/util.h"
+#ifndef EVENT__DISABLE_THREAD_SUPPORT
+#include "event2/thread.h"
+#include "regress_thread.h"
+#endif
+
#include "regress.h"
#include "tinytest.h"
}
#endif
+#ifndef EVENT__DISABLE_THREAD_SUPPORT
+
+static THREAD_FN
+disable_thread(void * arg)
+{
+ struct evconnlistener *lev = (struct evconnlistener *)arg;
+ evconnlistener_disable(lev);
+ return NULL;
+}
+
+static void
+acceptcb_for_thread_test(struct evconnlistener *listener, evutil_socket_t fd,
+ struct sockaddr *addr, int socklen, void *arg)
+{
+ THREAD_T *threadid_arg;
+ THREAD_T threadid;
+ struct timeval delay = { .tv_sec = 0, .tv_usec = 5000 };
+
+ threadid_arg = (THREAD_T *)arg;
+
+ evutil_closesocket(fd);
+
+ /* We need to run evconnlistener_disable() from disable_thread
+ * in parallel with processing this callback to trigger deadlock regression
+ */
+ THREAD_START(threadid, disable_thread, listener);
+ evutil_usleep_(&delay);
+
+ *threadid_arg = threadid;
+}
+
+static void
+regress_listener_disable_in_thread(void *arg)
+{
+ struct basic_test_data *data = arg;
+ struct event_base *base = data->base;
+ struct evconnlistener *listener = NULL;
+ struct sockaddr_in sin;
+ struct sockaddr_storage ss;
+ ev_socklen_t slen = sizeof(ss);
+ evutil_socket_t fd = EVUTIL_INVALID_SOCKET;
+ unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_THREADSAFE;
+ THREAD_T threadid;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
+ sin.sin_port = 0; /* "You pick!" */
+
+
+ listener = evconnlistener_new_bind(base, acceptcb_for_thread_test, (void*)(&threadid),
+ flags, -1, (struct sockaddr *)&sin, sizeof(sin));
+
+ tt_assert(listener);
+
+ tt_assert(getsockname(evconnlistener_get_fd(listener),
+ (struct sockaddr*)&ss, &slen) == 0);
+
+ tt_assert(evutil_socket_connect_(&fd, (struct sockaddr*)&ss, slen) != -1);
+
+ event_base_loop(base, EVLOOP_ONCE);
+
+ THREAD_JOIN(threadid);
+end:
+ if (listener)
+ evconnlistener_free(listener);
+}
+
+static void
+errorcb_for_thread_test(struct evconnlistener *listener, void *arg)
+{
+ THREAD_T *threadid_arg;
+ THREAD_T threadid;
+ struct timeval delay = { .tv_sec = 0, .tv_usec = 5000 };
+
+ threadid_arg = (THREAD_T *)arg;
+
+ /* We need to run evconnlistener_disable() from disable_thread
+ * in parallel with processing this callback to trigger deadlock regression
+ */
+ THREAD_START(threadid, disable_thread, listener);
+ evutil_usleep_(&delay);
+
+ *threadid_arg = threadid;
+}
+
+static void
+acceptcb_for_thread_test_error(struct evconnlistener *listener, evutil_socket_t fd,
+ struct sockaddr *addr, int socklen, void *arg)
+{
+}
+
+static void
+regress_listener_disable_in_thread_error(void *arg)
+{
+ struct basic_test_data *data = arg;
+ struct event_base *base = data->base;
+ struct evconnlistener *listener = NULL;
+ unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_THREADSAFE;
+ THREAD_T threadid;
+
+ /* send, so that pair[0] will look 'readable'*/
+ tt_int_op(send(data->pair[1], "hello", 5, 0), >, 0);
+
+ /* Start a listener with a bogus socket. */
+ listener = evconnlistener_new(base, acceptcb_for_thread_test_error, (void*)&threadid,
+ flags, 0,
+ data->pair[0]);
+ tt_assert(listener);
+
+ evconnlistener_set_error_cb(listener, errorcb_for_thread_test);
+
+ event_base_loop(base, EVLOOP_ONCE);
+
+ THREAD_JOIN(threadid);
+end:
+ if (listener)
+ evconnlistener_free(listener);
+}
+#endif
+
struct testcase_t listener_testcases[] = {
{ "randport", regress_pick_a_port, TT_FORK|TT_NEED_BASE,
{ "immediate_close", regress_listener_immediate_close,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL, },
+#ifndef EVENT__DISABLE_THREAD_SUPPORT
+ { "disable_in_thread", regress_listener_disable_in_thread,
+ TT_FORK|TT_NEED_BASE|TT_NEED_THREADS,
+ &basic_setup, NULL, },
+
+ { "disable_in_thread_error", regress_listener_disable_in_thread_error,
+ TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_NEED_SOCKETPAIR,
+ &basic_setup, NULL, },
+#endif
+
END_OF_TESTCASES,
};