Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

socketnotifier: add optional support for rust event loop #48108

Merged
merged 1 commit into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/coretests.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
82 changes: 82 additions & 0 deletions src/core/eventlooptest.cpp
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>
#include <QtTest/QtTest>
#include <boost/signals2.hpp>
#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"
10 changes: 10 additions & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -124,4 +129,9 @@ mod tests {
fn jwt() {
assert!(qtest::run(jwt_test));
}

#[test]
fn eventloop() {
assert!(qtest::run(eventloop_test));
}
}
97 changes: 74 additions & 23 deletions src/core/socketnotifier.cpp
Original file line number Diff line number Diff line change
@@ -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_);
}
44 changes: 27 additions & 17 deletions src/core/socketnotifier.h
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
/*
* 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

#include <QSocketNotifier>
#include <boost/signals2.hpp>

class EventLoop;

class SocketNotifier : public QObject
{
Q_OBJECT
Expand All @@ -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);

Expand All @@ -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
3 changes: 2 additions & 1 deletion src/core/tests.pri
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ INCLUDES += \

SOURCES += \
$$PWD/httpheaderstest.cpp \
$$PWD/jwttest.cpp
$$PWD/jwttest.cpp \
$$PWD/eventlooptest.cpp
1 change: 1 addition & 0 deletions src/handler/handlertests.h
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down