|
@@ -8,6 +8,7 @@
|
|
|
#include <sys/utsname.h> // uname
|
|
|
#include <fcntl.h>
|
|
|
#include <gtest/gtest.h>
|
|
|
+#include <pthread.h>
|
|
|
#include "butil/gperftools_profiler.h"
|
|
|
#include "butil/time.h"
|
|
|
#include "butil/macros.h"
|
|
@@ -18,6 +19,10 @@
|
|
|
#include "bthread/interrupt_pthread.h"
|
|
|
#include "bthread/bthread.h"
|
|
|
#include "bthread/unstable.h"
|
|
|
+#if defined(OS_MACOSX)
|
|
|
+#include <sys/types.h> // struct kevent
|
|
|
+#include <sys/event.h> // kevent(), kqueue()
|
|
|
+#endif
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
namespace bthread {
|
|
@@ -77,10 +82,17 @@ void* process_thread(void* arg) {
|
|
|
return NULL;
|
|
|
}
|
|
|
#ifdef CREATE_THREAD_TO_PROCESS
|
|
|
+# if defined(OS_LINUX)
|
|
|
epoll_event evt = { EPOLLIN | EPOLLONESHOT, { m } };
|
|
|
if (epoll_ctl(m->epfd, EPOLL_CTL_MOD, m->fd, &evt) < 0) {
|
|
|
epoll_ctl(m->epfd, EPOLL_CTL_ADD, m->fd, &evt);
|
|
|
}
|
|
|
+# elif defined(OS_MACOSX)
|
|
|
+ struct kevent kqueue_event;
|
|
|
+ EV_SET(&kqueue_event, m->fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT,
|
|
|
+ 0, 0, m);
|
|
|
+ kevent(m->epfd, &kqueue_event, 1, NULL, 0, NULL);
|
|
|
+# endif
|
|
|
#endif
|
|
|
return NULL;
|
|
|
}
|
|
@@ -89,11 +101,16 @@ void* epoll_thread(void* arg) {
|
|
|
bthread_usleep(1);
|
|
|
EpollMeta* m = (EpollMeta*)arg;
|
|
|
const int epfd = m->epfd;
|
|
|
+#if defined(OS_LINUX)
|
|
|
epoll_event e[32];
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ struct kevent e[32];
|
|
|
+#endif
|
|
|
|
|
|
while (!stop) {
|
|
|
|
|
|
-#ifndef USE_BLOCKING_EPOLL
|
|
|
+#if defined(OS_LINUX)
|
|
|
+# ifndef USE_BLOCKING_EPOLL
|
|
|
const int n = epoll_wait(epfd, e, ARRAY_SIZE(e), 0);
|
|
|
if (stop) {
|
|
|
break;
|
|
@@ -102,7 +119,7 @@ void* epoll_thread(void* arg) {
|
|
|
bthread_fd_wait(epfd, EPOLLIN);
|
|
|
continue;
|
|
|
}
|
|
|
-#else
|
|
|
+# else
|
|
|
const int n = epoll_wait(epfd, e, ARRAY_SIZE(e), -1);
|
|
|
if (stop) {
|
|
|
break;
|
|
@@ -110,12 +127,25 @@ void* epoll_thread(void* arg) {
|
|
|
if (n == 0) {
|
|
|
continue;
|
|
|
}
|
|
|
+# endif
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ const int n = kevent(epfd, NULL, 0, e, ARRAY_SIZE(e), NULL);
|
|
|
+ if (stop) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (n == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
#endif
|
|
|
if (n < 0) {
|
|
|
if (EINTR == errno) {
|
|
|
continue;
|
|
|
}
|
|
|
+#if defined(OS_LINUX)
|
|
|
PLOG(FATAL) << "Fail to epoll_wait";
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ PLOG(FATAL) << "Fail to kevent";
|
|
|
+#endif
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -123,13 +153,21 @@ void* epoll_thread(void* arg) {
|
|
|
bthread_fvec vec[n];
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
vec[i].fn = process_thread;
|
|
|
+# if defined(OS_LINUX)
|
|
|
vec[i].arg = e[i].data.ptr;
|
|
|
+# elif defined(OS_MACOSX)
|
|
|
+ vec[i].arg = e[i].udata;
|
|
|
+# endif
|
|
|
}
|
|
|
bthread_t tid[n];
|
|
|
bthread_startv(tid, vec, n, &BTHREAD_ATTR_SMALL);
|
|
|
#else
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
+# if defined(OS_LINUX)
|
|
|
process_thread(e[i].data.ptr);
|
|
|
+# elif defined(OS_MACOSX)
|
|
|
+ process_thread(e[i].udata);
|
|
|
+# endif
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
@@ -146,7 +184,11 @@ void* client_thread(void* arg) {
|
|
|
#ifdef RUN_CLIENT_IN_BTHREAD
|
|
|
ssize_t rc;
|
|
|
do {
|
|
|
+# if defined(OS_LINUX)
|
|
|
const int wait_rc = bthread_fd_wait(m->fd, EPOLLIN);
|
|
|
+# elif defined(OS_MACOSX)
|
|
|
+ const int wait_rc = bthread_fd_wait(m->fd, EVFILT_READ);
|
|
|
+# endif
|
|
|
EXPECT_EQ(0, wait_rc) << berror();
|
|
|
rc = read(m->fd, &m->count, sizeof(m->count));
|
|
|
} while (rc < 0 && errno == EAGAIN);
|
|
@@ -174,7 +216,7 @@ inline uint32_t fmix32 ( uint32_t h ) {
|
|
|
// a kernel patch that lots of machines currently don't have
|
|
|
TEST(FDTest, ping_pong) {
|
|
|
#ifndef NDEBUG
|
|
|
- bthread::break_nums = 0;
|
|
|
+ bthread::break_nums = 0;
|
|
|
#endif
|
|
|
|
|
|
const size_t REP = 30000;
|
|
@@ -187,11 +229,19 @@ TEST(FDTest, ping_pong) {
|
|
|
pthread_t eth[NEPOLL];
|
|
|
#endif
|
|
|
int fds[2 * NCLIENT];
|
|
|
+#ifdef RUN_CLIENT_IN_BTHREAD
|
|
|
bthread_t cth[NCLIENT];
|
|
|
+#else
|
|
|
+ pthread_t cth[NCLIENT];
|
|
|
+#endif
|
|
|
ClientMeta* cm[NCLIENT];
|
|
|
|
|
|
for (size_t i = 0; i < NEPOLL; ++i) {
|
|
|
+#if defined(OS_LINUX)
|
|
|
epfd[i] = epoll_create(1024);
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ epfd[i] = kqueue();
|
|
|
+#endif
|
|
|
ASSERT_GT(epfd[i], 0);
|
|
|
}
|
|
|
|
|
@@ -204,12 +254,27 @@ TEST(FDTest, ping_pong) {
|
|
|
ASSERT_EQ(0, fcntl(m->fd, F_SETFL, fcntl(m->fd, F_GETFL, 0) | O_NONBLOCK));
|
|
|
|
|
|
#ifdef CREATE_THREAD_TO_PROCESS
|
|
|
+# if defined(OS_LINUX)
|
|
|
epoll_event evt = { EPOLLIN | EPOLLONESHOT, { m } };
|
|
|
+# elif defined(OS_MACOSX)
|
|
|
+ struct kevent kqueue_event;
|
|
|
+ EV_SET(&kqueue_event, m->fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT,
|
|
|
+ 0, 0, m);
|
|
|
+# endif
|
|
|
#else
|
|
|
+# if defined(OS_LINUX)
|
|
|
epoll_event evt = { EPOLLIN, { m } };
|
|
|
+# elif defined(OS_MACOSX)
|
|
|
+ struct kevent kqueue_event;
|
|
|
+ EV_SET(&kqueue_event, m->fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, m);
|
|
|
+# endif
|
|
|
#endif
|
|
|
- ASSERT_EQ(0, epoll_ctl(m->epfd, EPOLL_CTL_ADD, m->fd, &evt));
|
|
|
|
|
|
+#if defined(OS_LINUX)
|
|
|
+ ASSERT_EQ(0, epoll_ctl(m->epfd, EPOLL_CTL_ADD, m->fd, &evt));
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ ASSERT_EQ(0, kevent(m->epfd, &kqueue_event, 1, NULL, 0, NULL));
|
|
|
+#endif
|
|
|
cm[i] = new ClientMeta;
|
|
|
cm[i]->fd = fds[i * 2 + 1];
|
|
|
cm[i]->count = i;
|
|
@@ -249,8 +314,14 @@ TEST(FDTest, ping_pong) {
|
|
|
LOG(INFO) << "tid=" << REP*NCLIENT*1000000L/tm.u_elapsed();
|
|
|
stop = true;
|
|
|
for (size_t i = 0; i < NEPOLL; ++i) {
|
|
|
+#if defined(OS_LINUX)
|
|
|
epoll_event evt = { EPOLLOUT, { NULL } };
|
|
|
ASSERT_EQ(0, epoll_ctl(epfd[i], EPOLL_CTL_ADD, 0, &evt));
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ struct kevent kqueue_event;
|
|
|
+ EV_SET(&kqueue_event, 0, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL);
|
|
|
+ ASSERT_EQ(0, kevent(epfd[i], &kqueue_event, 1, NULL, 0, NULL));
|
|
|
+#endif
|
|
|
#ifdef RUN_EPOLL_IN_BTHREAD
|
|
|
bthread_join(eth[i], NULL);
|
|
|
#else
|
|
@@ -261,11 +332,12 @@ TEST(FDTest, ping_pong) {
|
|
|
bthread_usleep(100000);
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
- std::cout << "break_nums=" << bthread::break_nums << std::endl;
|
|
|
+ std::cout << "break_nums=" << bthread::break_nums << std::endl;
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
TEST(FDTest, mod_closed_fd) {
|
|
|
+#if defined(OS_LINUX)
|
|
|
// Conclusion:
|
|
|
// If fd is never added into epoll, MOD returns ENOENT
|
|
|
// If fd is inside epoll and valid, MOD returns 0
|
|
@@ -301,9 +373,11 @@ TEST(FDTest, mod_closed_fd) {
|
|
|
ASSERT_EQ(ENOENT, errno) << berror();
|
|
|
|
|
|
ASSERT_EQ(0, close(epfd));
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
TEST(FDTest, add_existing_fd) {
|
|
|
+#if defined(OS_LINUX)
|
|
|
const int epfd = epoll_create(1024);
|
|
|
epoll_event e = { EPOLLIN, { NULL } };
|
|
|
ASSERT_EQ(0, epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &e));
|
|
@@ -311,19 +385,31 @@ TEST(FDTest, add_existing_fd) {
|
|
|
ASSERT_EQ(-1, epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &e));
|
|
|
ASSERT_EQ(EEXIST, errno);
|
|
|
ASSERT_EQ(0, close(epfd));
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
void* epoll_waiter(void* arg) {
|
|
|
+#if defined(OS_LINUX)
|
|
|
epoll_event e;
|
|
|
if (1 == epoll_wait((int)(intptr_t)arg, &e, 1, -1)) {
|
|
|
std::cout << e.events << std::endl;
|
|
|
}
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ struct kevent e;
|
|
|
+ if (1 == kevent((int)(intptr_t)arg, NULL, 0, &e, 1, NULL)) {
|
|
|
+ std::cout << e.flags << std::endl;
|
|
|
+ }
|
|
|
+#endif
|
|
|
std::cout << pthread_self() << " quits" << std::endl;
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
TEST(FDTest, interrupt_pthread) {
|
|
|
+#if defined(OS_LINUX)
|
|
|
const int epfd = epoll_create(1024);
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ const int epfd = kqueue();
|
|
|
+#endif
|
|
|
pthread_t th, th2;
|
|
|
ASSERT_EQ(0, pthread_create(&th, NULL, epoll_waiter, (void*)(intptr_t)epfd));
|
|
|
ASSERT_EQ(0, pthread_create(&th2, NULL, epoll_waiter, (void*)(intptr_t)epfd));
|
|
@@ -345,22 +431,35 @@ void* close_the_fd(void* arg) {
|
|
|
|
|
|
TEST(FDTest, invalid_epoll_events) {
|
|
|
errno = 0;
|
|
|
+#if defined(OS_LINUX)
|
|
|
ASSERT_EQ(-1, bthread_fd_wait(-1, EPOLLIN));
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ ASSERT_EQ(-1, bthread_fd_wait(-1, EVFILT_READ));
|
|
|
+#endif
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
errno = 0;
|
|
|
+#if defined(OS_LINUX)
|
|
|
ASSERT_EQ(-1, bthread_fd_timedwait(-1, EPOLLIN, NULL));
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ ASSERT_EQ(-1, bthread_fd_timedwait(-1, EVFILT_READ, NULL));
|
|
|
+#endif
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
|
|
-
|
|
|
int fds[2];
|
|
|
ASSERT_EQ(0, pipe(fds));
|
|
|
+#if defined(OS_LINUX)
|
|
|
ASSERT_EQ(-1, bthread_fd_wait(fds[0], EPOLLET));
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
+#endif
|
|
|
bthread_t th;
|
|
|
ASSERT_EQ(0, bthread_start_urgent(&th, NULL, close_the_fd, &fds[1]));
|
|
|
butil::Timer tm;
|
|
|
tm.start();
|
|
|
+#if defined(OS_LINUX)
|
|
|
ASSERT_EQ(0, bthread_fd_wait(fds[0], EPOLLIN | EPOLLET));
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ ASSERT_EQ(0, bthread_fd_wait(fds[0], EVFILT_READ));
|
|
|
+#endif
|
|
|
tm.stop();
|
|
|
ASSERT_LT(tm.m_elapsed(), 20);
|
|
|
ASSERT_EQ(0, bthread_join(th, NULL));
|
|
@@ -369,7 +468,11 @@ TEST(FDTest, invalid_epoll_events) {
|
|
|
|
|
|
void* wait_for_the_fd(void* arg) {
|
|
|
timespec ts = butil::milliseconds_from_now(50);
|
|
|
+#if defined(OS_LINUX)
|
|
|
bthread_fd_timedwait(*(int*)arg, EPOLLIN, &ts);
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ bthread_fd_timedwait(*(int*)arg, EVFILT_READ, &ts);
|
|
|
+#endif
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
@@ -403,7 +506,11 @@ TEST(FDTest, close_should_wakeup_waiter) {
|
|
|
ASSERT_LT(tm.m_elapsed(), 5);
|
|
|
|
|
|
// Launch again, should quit soon due to EBADF
|
|
|
+#if defined(OS_LINUX)
|
|
|
ASSERT_EQ(-1, bthread_fd_timedwait(fds[0], EPOLLIN, NULL));
|
|
|
+#elif defined(OS_MACOSX)
|
|
|
+ ASSERT_EQ(-1, bthread_fd_timedwait(fds[0], EVFILT_READ, NULL));
|
|
|
+#endif
|
|
|
ASSERT_EQ(EBADF, errno);
|
|
|
|
|
|
ASSERT_EQ(0, bthread_close(fds[1]));
|