diff --git a/src/core/coretests.h b/src/core/coretests.h index 89ccc671..0bcd76b0 100644 --- a/src/core/coretests.h +++ b/src/core/coretests.h @@ -3,5 +3,6 @@ int httpheaders_test(int argc, char **argv); int jwt_test(int argc, char **argv); +int eventloop_test(int argc, char **argv); #endif diff --git a/src/core/eventlooptest.cpp b/src/core/eventlooptest.cpp new file mode 100644 index 00000000..8944be68 --- /dev/null +++ b/src/core/eventlooptest.cpp @@ -0,0 +1,82 @@ +/* +* Copyright (C) 2025 Fastly, Inc. +* +* This file is part of Pushpin. +* +* $FANOUT_BEGIN_LICENSE:APACHE2$ +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* $FANOUT_END_LICENSE$ +*/ + +#include +#include +#include +#include "defercall.h" +#include "eventloop.h" +#include "socketnotifier.h" + +class EventLoopTest : public QObject +{ + Q_OBJECT + +private slots: + void cleanupTestCase() + { + DeferCall::cleanup(); + } + + void socketNotifier() + { + EventLoop loop(1); + + int fds[2]; + QCOMPARE(pipe(fds), 0); + + SocketNotifier *sn = new SocketNotifier(fds[0], SocketNotifier::Read); + + int activatedFd = -1; + sn->activated.connect([&](int fd) { + activatedFd = fd; + loop.exit(123); + }); + + unsigned char c = 1; + QCOMPARE(write(fds[1], &c, 1), 1); + + QCOMPARE(loop.exec(), 123); + QCOMPARE(activatedFd, fds[0]); + + delete sn; + close(fds[1]); + close(fds[0]); + } +}; + +namespace { +namespace Main { +QTEST_MAIN(EventLoopTest) +} +} + +extern "C" { + +int eventloop_test(int argc, char **argv) +{ + return Main::main(argc, argv); +} + +} + +#include "eventlooptest.moc" diff --git a/src/core/mod.rs b/src/core/mod.rs index dfdc8ee0..7497dadc 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -115,6 +115,11 @@ mod tests { unsafe { call_c_main(ffi::jwt_test, args) as u8 } } + fn eventloop_test(args: &[&OsStr]) -> u8 { + // SAFETY: safe to call + unsafe { call_c_main(ffi::eventloop_test, args) as u8 } + } + #[test] fn httpheaders() { assert!(qtest::run(httpheaders_test)); @@ -124,4 +129,9 @@ mod tests { fn jwt() { assert!(qtest::run(jwt_test)); } + + #[test] + fn eventloop() { + assert!(qtest::run(eventloop_test)); + } } diff --git a/src/core/socketnotifier.cpp b/src/core/socketnotifier.cpp index 4d4f1ef2..b1a7cbcc 100644 --- a/src/core/socketnotifier.cpp +++ b/src/core/socketnotifier.cpp @@ -1,46 +1,97 @@ /* -* Copyright (C) 2025 Fastly, Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright (C) 2025 Fastly, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "socketnotifier.h" #include "defercall.h" +#include "eventloop.h" -SocketNotifier::SocketNotifier(int socket, Type type) +SocketNotifier::SocketNotifier(int socket, Type type) : + socket_(socket), + type_(type), + enabled_(true), + inner_(nullptr), + loop_(EventLoop::instance()), + regId_(-1) { - QSocketNotifier::Type qType = type == Read ? QSocketNotifier::Read : QSocketNotifier::Write; + if(loop_) + { + // if the rust-based eventloop is available, use it - inner_ = new QSocketNotifier(socket, qType); - connect(inner_, &QSocketNotifier::activated, this, &SocketNotifier::innerActivated); + unsigned char interest = 0; + switch(type_) + { + case SocketNotifier::Read: + interest = EventLoop::Readable; + break; + case SocketNotifier::Write: + interest = EventLoop::Writable; + break; + } + + regId_ = loop_->registerFd(socket_, interest, SocketNotifier::cb_fd_activated, this); + } + else + { + // else fall back to qt eventloop + + QSocketNotifier::Type qType = type == Read ? QSocketNotifier::Read : QSocketNotifier::Write; + + inner_ = new QSocketNotifier(socket, qType); + connect(inner_, &QSocketNotifier::activated, this, &SocketNotifier::innerActivated); + } } SocketNotifier::~SocketNotifier() { - inner_->setEnabled(false); + if(inner_) + { + inner_->setEnabled(false); - inner_->disconnect(this); - inner_->setParent(0); - DeferCall::deleteLater(inner_); + inner_->disconnect(this); + inner_->setParent(0); + DeferCall::deleteLater(inner_); + } + + if(regId_ >= 0) + loop_->deregister(regId_); } void SocketNotifier::setEnabled(bool enable) { - inner_->setEnabled(enable); + enabled_ = enable; + + if(inner_) + inner_->setEnabled(enabled_); } void SocketNotifier::innerActivated(int socket) { activated(socket); } + +void SocketNotifier::cb_fd_activated(void *ctx) +{ + SocketNotifier *self = (SocketNotifier *)ctx; + + self->fd_activated(); +} + +void SocketNotifier::fd_activated() +{ + if(enabled_) + activated(socket_); +} diff --git a/src/core/socketnotifier.h b/src/core/socketnotifier.h index fa5bd980..3df35e0a 100644 --- a/src/core/socketnotifier.h +++ b/src/core/socketnotifier.h @@ -1,18 +1,18 @@ /* -* Copyright (C) 2025 Fastly, Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright (C) 2025 Fastly, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef SOCKETNOTIFIER_H #define SOCKETNOTIFIER_H @@ -20,6 +20,8 @@ #include #include +class EventLoop; + class SocketNotifier : public QObject { Q_OBJECT @@ -34,9 +36,9 @@ class SocketNotifier : public QObject SocketNotifier(int socket, Type type); ~SocketNotifier(); - bool isEnabled() const { return inner_->isEnabled(); } - int socket() const { return inner_->socket(); } - Type type() const { return inner_->type() == QSocketNotifier::Read ? Read : Write; } + bool isEnabled() const { return enabled_; } + int socket() const { return socket_; } + Type type() const { return type_; } void setEnabled(bool enable); @@ -46,7 +48,15 @@ private slots: void innerActivated(int socket); private: + int socket_; + Type type_; + bool enabled_; QSocketNotifier *inner_; + EventLoop *loop_; + int regId_; + + static void cb_fd_activated(void *ctx); + void fd_activated(); }; #endif diff --git a/src/core/tests.pri b/src/core/tests.pri index 39130029..1fe8d960 100644 --- a/src/core/tests.pri +++ b/src/core/tests.pri @@ -3,4 +3,5 @@ INCLUDES += \ SOURCES += \ $$PWD/httpheaderstest.cpp \ - $$PWD/jwttest.cpp + $$PWD/jwttest.cpp \ + $$PWD/eventlooptest.cpp diff --git a/src/handler/handlertests.h b/src/handler/handlertests.h index 79dbcdeb..e891e945 100644 --- a/src/handler/handlertests.h +++ b/src/handler/handlertests.h @@ -1,6 +1,7 @@ #ifndef HANDLER_TEST_H #define HANDLER_TEST_H +int filter_test(int argc, char **argv); int jsonpatch_test(int argc, char **argv); int instruct_test(int argc, char **argv); int idformat_test(int argc, char **argv); diff --git a/src/lib.rs b/src/lib.rs index ca9f2a69..db46490a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,7 @@ pub mod ffi { import_cpptest! { pub fn httpheaders_test(argc: libc::c_int, argv: *const *const libc::c_char) -> libc::c_int; pub fn jwt_test(argc: libc::c_int, argv: *const *const libc::c_char) -> libc::c_int; + pub fn eventloop_test(argc: libc::c_int, argv: *const *const libc::c_char) -> libc::c_int; pub fn routesfile_test(argc: libc::c_int, argv: *const *const libc::c_char) -> libc::c_int; pub fn proxyengine_test(argc: libc::c_int, argv: *const *const libc::c_char) -> libc::c_int; pub fn filter_test(argc: libc::c_int, argv: *const *const libc::c_char) -> libc::c_int;