Skip to content

Commit 24c4ef3

Browse files
authored
add DeferCall to replace QMetaObject::invokeMethod (#48101)
1 parent 5a9fc08 commit 24c4ef3

File tree

9 files changed

+150
-6
lines changed

9 files changed

+150
-6
lines changed

src/core/core.pri

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ HEADERS += \
4343
$$PWD/timerwheel.h \
4444
$$PWD/jwt.h \
4545
$$PWD/rtimer.h \
46+
$$PWD/defercall.h \
4647
$$PWD/logutil.h \
4748
$$PWD/uuidutil.h \
4849
$$PWD/zutil.h \
@@ -66,6 +67,7 @@ SOURCES += \
6667
$$PWD/timerwheel.cpp \
6768
$$PWD/jwt.cpp \
6869
$$PWD/rtimer.cpp \
70+
$$PWD/defercall.cpp \
6971
$$PWD/logutil.cpp \
7072
$$PWD/uuidutil.cpp \
7173
$$PWD/zutil.cpp \

src/core/defercall.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (C) 2025 Fastly, Inc.
3+
*
4+
* This file is part of Pushpin.
5+
*
6+
* $FANOUT_BEGIN_LICENSE:APACHE2$
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
* $FANOUT_END_LICENSE$
21+
*/
22+
23+
#include "defercall.h"
24+
#include <assert.h>
25+
26+
static thread_local DeferCall *g_instance = nullptr;
27+
28+
DeferCall::DeferCall() = default;
29+
30+
DeferCall::~DeferCall() = default;
31+
32+
void DeferCall::defer(std::function<void ()> handler)
33+
{
34+
Call c;
35+
c.handler = handler;
36+
37+
deferredCalls_.push_back(c);
38+
39+
QMetaObject::invokeMethod(this, "callNext", Qt::QueuedConnection);
40+
}
41+
42+
DeferCall *DeferCall::global()
43+
{
44+
if(!g_instance)
45+
g_instance = new DeferCall;
46+
47+
return g_instance;
48+
}
49+
50+
void DeferCall::cleanup()
51+
{
52+
delete g_instance;
53+
g_instance = nullptr;
54+
}
55+
56+
void DeferCall::callNext()
57+
{
58+
// there can't be more invokeMethod resolutions than queued calls
59+
assert(!deferredCalls_.empty());
60+
61+
Call c = deferredCalls_.front();
62+
deferredCalls_.pop_front();
63+
64+
c.handler();
65+
}

src/core/defercall.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (C) 2025 Fastly, Inc.
3+
*
4+
* This file is part of Pushpin.
5+
*
6+
* $FANOUT_BEGIN_LICENSE:APACHE2$
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
* $FANOUT_END_LICENSE$
21+
*/
22+
23+
#ifndef DEFERCALL_H
24+
#define DEFERCALL_H
25+
26+
#include <QObject>
27+
28+
// queues calls to be run after returning to the event loop
29+
class DeferCall : public QObject
30+
{
31+
Q_OBJECT
32+
33+
public:
34+
DeferCall();
35+
~DeferCall();
36+
37+
// queue handler to be called after returning to the event loop. if
38+
// handler contains references, they must outlive DeferCall. the
39+
// recommended usage is for each object needing to perform deferred calls
40+
// to keep a DeferCall as a member variable, and only refer to the
41+
// object's own data in the handler. that way, any references are
42+
// guaranteed to live long enough.
43+
void defer(std::function<void ()> handler);
44+
45+
static DeferCall *global();
46+
static void cleanup();
47+
48+
template <typename T>
49+
static void deleteLater(T *p)
50+
{
51+
global()->defer([=]() { delete p; });
52+
}
53+
54+
private slots:
55+
void callNext();
56+
57+
private:
58+
class Call
59+
{
60+
public:
61+
std::function<void ()> handler;
62+
};
63+
64+
std::list<Call> deferredCalls_;
65+
};
66+
67+
#endif

src/core/zhttprequest.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (C) 2012-2021 Fanout, Inc.
3-
* Copyright (C) 2023 Fastly, Inc.
3+
* Copyright (C) 2023-2025 Fastly, Inc.
44
*
55
* This file is part of Pushpin.
66
*
@@ -30,6 +30,7 @@
3030
#include "bufferlist.h"
3131
#include "log.h"
3232
#include "rtimer.h"
33+
#include "defercall.h"
3334
#include "zhttpmanager.h"
3435
#include "uuidutil.h"
3536

@@ -105,6 +106,7 @@ class ZhttpRequest::Private : public QObject
105106
bool quiet;
106107
Connection expTimerConnection;
107108
Connection keepAliveTimerConnection;
109+
DeferCall deferCall;
108110

109111
Private(ZhttpRequest *_q) :
110112
QObject(_q),
@@ -163,15 +165,15 @@ class ZhttpRequest::Private : public QObject
163165
{
164166
expTimerConnection.disconnect();
165167
expireTimer->setParent(0);
166-
expireTimer->deleteLater();
168+
DeferCall::deleteLater(expireTimer);
167169
expireTimer = 0;
168170
}
169171

170172
if(keepAliveTimer)
171173
{
172174
keepAliveTimerConnection.disconnect();
173175
keepAliveTimer->setParent(0);
174-
keepAliveTimer->deleteLater();
176+
DeferCall::deleteLater(keepAliveTimer);
175177
keepAliveTimer = 0;
176178
}
177179

@@ -367,7 +369,7 @@ class ZhttpRequest::Private : public QObject
367369
if(!pendingUpdate)
368370
{
369371
pendingUpdate = true;
370-
QMetaObject::invokeMethod(this, "doUpdate", Qt::QueuedConnection);
372+
deferCall.defer([&]() { doUpdate(); });
371373
}
372374
}
373375

@@ -935,7 +937,6 @@ class ZhttpRequest::Private : public QObject
935937
return ErrorGeneric;
936938
}
937939

938-
public slots:
939940
void doUpdate()
940941
{
941942
pendingUpdate = false;
@@ -1157,7 +1158,6 @@ public slots:
11571158
}
11581159
}
11591160

1160-
public:
11611161
void expire_timeout()
11621162
{
11631163
state = Stopped;

src/handler/handlerenginetest.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "zhttpresponsepacket.h"
3434
#include "packet/httpresponsedata.h"
3535
#include "rtimer.h"
36+
#include "defercall.h"
3637
#include "handlerengine.h"
3738

3839
namespace {
@@ -309,6 +310,7 @@ private slots:
309310
delete wrapper;
310311

311312
RTimer::deinit();
313+
DeferCall::cleanup();
312314
}
313315

314316
void acceptNoHold()

src/handler/handlermain.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <QCoreApplication>
2525
#include <QTimer>
2626
#include "rtimer.h"
27+
#include "defercall.h"
2728
#include "handlerapp.h"
2829

2930
class HandlerAppMain
@@ -60,6 +61,7 @@ int handler_main(int argc, char **argv)
6061

6162
// deinit here, after all event loop activity has completed
6263
RTimer::deinit();
64+
DeferCall::cleanup();
6365

6466
return ret;
6567
}

src/proxy/app.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <QFileInfo>
3232
#include "processquit.h"
3333
#include "rtimer.h"
34+
#include "defercall.h"
3435
#include "log.h"
3536
#include "settings.h"
3637
#include "xffrule.h"
@@ -299,6 +300,7 @@ class EngineThread : public QThread
299300

300301
// deinit here, after all event loop activity has completed
301302
RTimer::deinit();
303+
DeferCall::cleanup();
302304
}
303305

304306
private:

src/proxy/domainmap.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <QFileSystemWatcher>
3636
#include "log.h"
3737
#include "rtimer.h"
38+
#include "defercall.h"
3839
#include "routesfile.h"
3940

4041
#define WORKER_THREAD_TIMERS 1
@@ -756,6 +757,7 @@ class DomainMap::Thread : public QThread
756757
delete worker;
757758

758759
RTimer::deinit();
760+
DeferCall::cleanup();
759761
}
760762

761763
public:

src/proxy/proxyenginetest.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "packet/httpresponsedata.h"
3939
#include "packet/statspacket.h"
4040
#include "rtimer.h"
41+
#include "defercall.h"
4142
#include "zhttpmanager.h"
4243
#include "statsmanager.h"
4344
#include "domainmap.h"
@@ -636,6 +637,7 @@ private slots:
636637

637638
QCoreApplication::instance()->sendPostedEvents();
638639
RTimer::deinit();
640+
DeferCall::cleanup();
639641
}
640642

641643
void passthrough()

0 commit comments

Comments
 (0)