Coverage Report

Created: 2026-11-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/uWebSockets/src/TopicTree.h
Line
Count
Source
1
/*
2
 * Authored by Alex Hultman, 2018-2021.
3
 * Intellectual property of third-party.
4
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#ifndef UWS_TOPICTREE_H
19
#define UWS_TOPICTREE_H
20
21
#include <map>
22
#include <list>
23
#include <iostream>
24
#include <unordered_set>
25
#include <utility>
26
#include <memory>
27
#include <unordered_map>
28
#include <vector>
29
#include <string_view>
30
#include <functional>
31
#include <set>
32
#include <string>
33
#include <exception>
34
35
namespace uWS {
36
37
struct Subscriber;
38
39
struct Topic : std::unordered_set<Subscriber *> {
40
41
6.76M
    Topic(std::string_view topic) : name(topic) {
42
43
6.76M
    }
44
45
    std::string name;
46
};
47
48
struct Subscriber {
49
50
    template <typename, typename> friend struct TopicTree;
51
52
private:
53
    /* We use a factory */
54
2.33M
    Subscriber() = default;
55
56
    /* State of prev, next does not matter unless we are needsDrainage() since we are not in the list */
57
    Subscriber *prev, *next;
58
59
    /* Any one subscriber can be part of at most 32 publishes before it needs a drain,
60
     * or whatever encoding of runs or whatever we might do in the future */
61
    uint16_t messageIndices[32];
62
63
    /* This one matters the most, if it is 0 we are not in the list of drainableSubscribers */
64
    unsigned char numMessageIndices = 0;
65
66
public:
67
68
    /* We have a list of topics we subscribe to (read by WebSocket::iterateTopics) */
69
    std::set<Topic *> topics;
70
71
    /* User data */
72
    void *user;
73
74
2.85M
    bool needsDrainage() {
75
2.85M
        return numMessageIndices;
76
2.85M
    }
77
};
78
79
template <typename T, typename B>
80
struct TopicTree {
81
82
    enum IteratorFlags {
83
        LAST = 1,
84
        FIRST = 2
85
    };
86
87
    /* Whomever is iterating this topic is locked to not modify its own list */
88
    Subscriber *iteratingSubscriber = nullptr;
89
90
private:
91
92
    /* The drain callback must not publish, unsubscribe or subscribe.
93
     * It must only cork, uncork, send, write */
94
    std::function<bool(Subscriber *, T &, IteratorFlags)> cb;
95
96
    /* The topics */
97
    std::unordered_map<std::string_view, std::unique_ptr<Topic>> topics;
98
99
    /* List of subscribers that needs drainage */
100
    Subscriber *drainableSubscribers = nullptr;
101
102
    /* Palette of outgoing messages, up to 64k */
103
    std::vector<T> outgoingMessages;
104
105
8.92M
    void checkIteratingSubscriber(Subscriber *s) {
106
        /* Notify user that they are doing something wrong here */
107
8.92M
        if (iteratingSubscriber == s) {
108
0
            std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl;
109
0
            std::terminate();
110
0
        }
111
8.92M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::checkIteratingSubscriber(uWS::Subscriber*)
Line
Count
Source
105
2.27M
    void checkIteratingSubscriber(Subscriber *s) {
106
        /* Notify user that they are doing something wrong here */
107
2.27M
        if (iteratingSubscriber == s) {
108
0
            std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl;
109
0
            std::terminate();
110
0
        }
111
2.27M
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::checkIteratingSubscriber(uWS::Subscriber*)
Line
Count
Source
105
6.64M
    void checkIteratingSubscriber(Subscriber *s) {
106
        /* Notify user that they are doing something wrong here */
107
6.64M
        if (iteratingSubscriber == s) {
108
0
            std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl;
109
0
            std::terminate();
110
0
        }
111
6.64M
    }
112
113
    /* Warning: does NOT unlink from drainableSubscribers or modify next, prev. */
114
214k
    void drainImpl(Subscriber *s) {
115
        /* Before we call cb we need to make sure this subscriber will not report needsDrainage()
116
         * since WebSocket::send will call drain from within the cb in that case.*/
117
214k
        int numMessageIndices = s->numMessageIndices;
118
214k
        s->numMessageIndices = 0;
119
120
        /* Then we emit cb */
121
4.61M
        for (int i = 0; i < numMessageIndices; i++) {
122
4.40M
            T &outgoingMessage = outgoingMessages[s->messageIndices[i]];
123
124
4.40M
            int flags = (i == numMessageIndices - 1) ? LAST : 0;
125
126
            /* Returning true will stop drainage short (such as when backpressure is too high) */
127
4.40M
            if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) {
128
2.02k
                break;
129
2.02k
            }
130
4.40M
        }
131
214k
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::drainImpl(uWS::Subscriber*)
Line
Count
Source
114
184k
    void drainImpl(Subscriber *s) {
115
        /* Before we call cb we need to make sure this subscriber will not report needsDrainage()
116
         * since WebSocket::send will call drain from within the cb in that case.*/
117
184k
        int numMessageIndices = s->numMessageIndices;
118
184k
        s->numMessageIndices = 0;
119
120
        /* Then we emit cb */
121
4.21M
        for (int i = 0; i < numMessageIndices; i++) {
122
4.03M
            T &outgoingMessage = outgoingMessages[s->messageIndices[i]];
123
124
4.03M
            int flags = (i == numMessageIndices - 1) ? LAST : 0;
125
126
            /* Returning true will stop drainage short (such as when backpressure is too high) */
127
4.03M
            if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) {
128
958
                break;
129
958
            }
130
4.03M
        }
131
184k
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::drainImpl(uWS::Subscriber*)
Line
Count
Source
114
29.9k
    void drainImpl(Subscriber *s) {
115
        /* Before we call cb we need to make sure this subscriber will not report needsDrainage()
116
         * since WebSocket::send will call drain from within the cb in that case.*/
117
29.9k
        int numMessageIndices = s->numMessageIndices;
118
29.9k
        s->numMessageIndices = 0;
119
120
        /* Then we emit cb */
121
405k
        for (int i = 0; i < numMessageIndices; i++) {
122
376k
            T &outgoingMessage = outgoingMessages[s->messageIndices[i]];
123
124
376k
            int flags = (i == numMessageIndices - 1) ? LAST : 0;
125
126
            /* Returning true will stop drainage short (such as when backpressure is too high) */
127
376k
            if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) {
128
1.06k
                break;
129
1.06k
            }
130
376k
        }
131
29.9k
    }
132
133
286k
    void unlinkDrainableSubscriber(Subscriber *s) {
134
286k
        if (s->prev) {
135
168k
            s->prev->next = s->next;
136
168k
        }
137
286k
        if (s->next) {
138
120k
            s->next->prev = s->prev;
139
120k
        }
140
        /* If we are the head, then we also need to reset the head */
141
286k
        if (drainableSubscribers == s) {
142
117k
            drainableSubscribers = s->next;
143
117k
        }
144
286k
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::unlinkDrainableSubscriber(uWS::Subscriber*)
Line
Count
Source
133
183k
    void unlinkDrainableSubscriber(Subscriber *s) {
134
183k
        if (s->prev) {
135
109k
            s->prev->next = s->next;
136
109k
        }
137
183k
        if (s->next) {
138
84.2k
            s->next->prev = s->prev;
139
84.2k
        }
140
        /* If we are the head, then we also need to reset the head */
141
183k
        if (drainableSubscribers == s) {
142
73.6k
            drainableSubscribers = s->next;
143
73.6k
        }
144
183k
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::unlinkDrainableSubscriber(uWS::Subscriber*)
Line
Count
Source
133
102k
    void unlinkDrainableSubscriber(Subscriber *s) {
134
102k
        if (s->prev) {
135
58.5k
            s->prev->next = s->next;
136
58.5k
        }
137
102k
        if (s->next) {
138
35.8k
            s->next->prev = s->prev;
139
35.8k
        }
140
        /* If we are the head, then we also need to reset the head */
141
102k
        if (drainableSubscribers == s) {
142
44.2k
            drainableSubscribers = s->next;
143
44.2k
        }
144
102k
    }
145
146
public:
147
148
26.4k
    TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) {
149
150
26.4k
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::TopicTree(std::__1::function<bool (uWS::Subscriber*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::IteratorFlags)>)
Line
Count
Source
148
3.11k
    TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) {
149
150
3.11k
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::TopicTree(std::__1::function<bool (uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)>)
Line
Count
Source
148
23.3k
    TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) {
149
150
23.3k
    }
151
152
    /* Returns nullptr if not found */
153
14.8M
    Topic *lookupTopic(std::string_view topic) {
154
14.8M
        auto it = topics.find(topic);
155
14.8M
        if (it == topics.end()) {
156
7.24M
            return nullptr;
157
7.24M
        }
158
7.56M
        return it->second.get();
159
14.8M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::lookupTopic(std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
153
8.16M
    Topic *lookupTopic(std::string_view topic) {
154
8.16M
        auto it = topics.find(topic);
155
8.16M
        if (it == topics.end()) {
156
737k
            return nullptr;
157
737k
        }
158
7.42M
        return it->second.get();
159
8.16M
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::lookupTopic(std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
153
6.64M
    Topic *lookupTopic(std::string_view topic) {
154
6.64M
        auto it = topics.find(topic);
155
6.64M
        if (it == topics.end()) {
156
6.51M
            return nullptr;
157
6.51M
        }
158
135k
        return it->second.get();
159
6.64M
    }
160
161
    /* Subscribe fails if we already are subscribed */
162
8.87M
    Topic *subscribe(Subscriber *s, std::string_view topic) {
163
        /* Notify user that they are doing something wrong here */
164
8.87M
        checkIteratingSubscriber(s);
165
166
        /* Lookup or create new topic */
167
8.87M
        Topic *topicPtr = lookupTopic(topic);
168
8.87M
        if (!topicPtr) {
169
6.76M
            Topic *newTopic = new Topic(topic);
170
6.76M
            topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)});
171
6.76M
            topicPtr = newTopic;
172
6.76M
        }
173
174
        /* Insert us in topic, insert topic in us */
175
8.87M
        auto [it, inserted] = s->topics.insert(topicPtr);
176
8.87M
        if (!inserted) {
177
59.0k
            return nullptr;
178
59.0k
        }
179
8.81M
        topicPtr->insert(s);
180
181
        /* Success */
182
8.81M
        return topicPtr;
183
8.87M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::subscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
162
2.22M
    Topic *subscribe(Subscriber *s, std::string_view topic) {
163
        /* Notify user that they are doing something wrong here */
164
2.22M
        checkIteratingSubscriber(s);
165
166
        /* Lookup or create new topic */
167
2.22M
        Topic *topicPtr = lookupTopic(topic);
168
2.22M
        if (!topicPtr) {
169
249k
            Topic *newTopic = new Topic(topic);
170
249k
            topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)});
171
249k
            topicPtr = newTopic;
172
249k
        }
173
174
        /* Insert us in topic, insert topic in us */
175
2.22M
        auto [it, inserted] = s->topics.insert(topicPtr);
176
2.22M
        if (!inserted) {
177
59.0k
            return nullptr;
178
59.0k
        }
179
2.16M
        topicPtr->insert(s);
180
181
        /* Success */
182
2.16M
        return topicPtr;
183
2.22M
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::subscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
162
6.64M
    Topic *subscribe(Subscriber *s, std::string_view topic) {
163
        /* Notify user that they are doing something wrong here */
164
6.64M
        checkIteratingSubscriber(s);
165
166
        /* Lookup or create new topic */
167
6.64M
        Topic *topicPtr = lookupTopic(topic);
168
6.64M
        if (!topicPtr) {
169
6.51M
            Topic *newTopic = new Topic(topic);
170
6.51M
            topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)});
171
6.51M
            topicPtr = newTopic;
172
6.51M
        }
173
174
        /* Insert us in topic, insert topic in us */
175
6.64M
        auto [it, inserted] = s->topics.insert(topicPtr);
176
6.64M
        if (!inserted) {
177
0
            return nullptr;
178
0
        }
179
6.64M
        topicPtr->insert(s);
180
181
        /* Success */
182
6.64M
        return topicPtr;
183
6.64M
    }
184
185
    /* Returns ok, last, newCount */
186
49.9k
    std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) {
187
        /* Notify user that they are doing something wrong here */
188
49.9k
        checkIteratingSubscriber(s);
189
190
        /* Lookup topic */
191
49.9k
        Topic *topicPtr = lookupTopic(topic);
192
49.9k
        if (!topicPtr) {
193
            /* If the topic doesn't exist we are assumed to still be subscribers of something */
194
1.69k
            return {false, false, -1};
195
1.69k
        }
196
197
        /* Erase from our list first */
198
48.2k
        if (s->topics.erase(topicPtr) == 0) {
199
1.68k
            return {false, false, -1};
200
1.68k
        }
201
202
        /* Remove us from topic */
203
46.5k
        topicPtr->erase(s);
204
205
46.5k
        int newCount = (int) topicPtr->size();
206
207
        /* If there is no subscriber to this topic, remove it */
208
46.5k
        if (!topicPtr->size()) {
209
            /* Unique_ptr deletes the topic */
210
22.9k
            topics.erase(topic);
211
22.9k
        }
212
213
        /* If we don't hold any topics we are to be freed altogether */
214
46.5k
        return {true, s->topics.size() == 0, newCount};
215
48.2k
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::unsubscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
186
49.3k
    std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) {
187
        /* Notify user that they are doing something wrong here */
188
49.3k
        checkIteratingSubscriber(s);
189
190
        /* Lookup topic */
191
49.3k
        Topic *topicPtr = lookupTopic(topic);
192
49.3k
        if (!topicPtr) {
193
            /* If the topic doesn't exist we are assumed to still be subscribers of something */
194
1.04k
            return {false, false, -1};
195
1.04k
        }
196
197
        /* Erase from our list first */
198
48.2k
        if (s->topics.erase(topicPtr) == 0) {
199
1.68k
            return {false, false, -1};
200
1.68k
        }
201
202
        /* Remove us from topic */
203
46.5k
        topicPtr->erase(s);
204
205
46.5k
        int newCount = (int) topicPtr->size();
206
207
        /* If there is no subscriber to this topic, remove it */
208
46.5k
        if (!topicPtr->size()) {
209
            /* Unique_ptr deletes the topic */
210
22.9k
            topics.erase(topic);
211
22.9k
        }
212
213
        /* If we don't hold any topics we are to be freed altogether */
214
46.5k
        return {true, s->topics.size() == 0, newCount};
215
48.2k
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::unsubscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
186
649
    std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) {
187
        /* Notify user that they are doing something wrong here */
188
649
        checkIteratingSubscriber(s);
189
190
        /* Lookup topic */
191
649
        Topic *topicPtr = lookupTopic(topic);
192
649
        if (!topicPtr) {
193
            /* If the topic doesn't exist we are assumed to still be subscribers of something */
194
649
            return {false, false, -1};
195
649
        }
196
197
        /* Erase from our list first */
198
0
        if (s->topics.erase(topicPtr) == 0) {
199
0
            return {false, false, -1};
200
0
        }
201
202
        /* Remove us from topic */
203
0
        topicPtr->erase(s);
204
205
0
        int newCount = (int) topicPtr->size();
206
207
        /* If there is no subscriber to this topic, remove it */
208
0
        if (!topicPtr->size()) {
209
            /* Unique_ptr deletes the topic */
210
0
            topics.erase(topic);
211
0
        }
212
213
        /* If we don't hold any topics we are to be freed altogether */
214
0
        return {true, s->topics.size() == 0, newCount};
215
0
    }
216
217
    /* Factory function for creating a Subscriber */
218
2.33M
    Subscriber *createSubscriber() {
219
2.33M
        return new Subscriber();
220
2.33M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::createSubscriber()
Line
Count
Source
218
2.09M
    Subscriber *createSubscriber() {
219
2.09M
        return new Subscriber();
220
2.09M
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::createSubscriber()
Line
Count
Source
218
237k
    Subscriber *createSubscriber() {
219
237k
        return new Subscriber();
220
237k
    }
221
222
    /* This is used to end a Subscriber, before freeing it */
223
2.40M
    void freeSubscriber(Subscriber *s) {
224
225
        /* I guess we call this one even if we are not subscribers */
226
2.40M
        if (!s) {
227
68.1k
            return;
228
68.1k
        }
229
230
        /* For all topics, unsubscribe */
231
8.76M
        for (Topic *topicPtr : s->topics) {
232
            /* If we are the last subscriber, simply remove the whole topic */
233
8.76M
            if (topicPtr->size() == 1) {
234
6.73M
                topics.erase(topicPtr->name);
235
6.73M
            } else {
236
                /* Otherwise just remove us */
237
2.03M
                topicPtr->erase(s);
238
2.03M
            }
239
8.76M
        }
240
241
        /* We also need to unlink us */
242
2.33M
        if (s->needsDrainage()) {
243
157k
            unlinkDrainableSubscriber(s);
244
157k
        }
245
246
2.33M
        delete s;
247
2.33M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::freeSubscriber(uWS::Subscriber*)
Line
Count
Source
223
2.09M
    void freeSubscriber(Subscriber *s) {
224
225
        /* I guess we call this one even if we are not subscribers */
226
2.09M
        if (!s) {
227
0
            return;
228
0
        }
229
230
        /* For all topics, unsubscribe */
231
2.12M
        for (Topic *topicPtr : s->topics) {
232
            /* If we are the last subscriber, simply remove the whole topic */
233
2.12M
            if (topicPtr->size() == 1) {
234
227k
                topics.erase(topicPtr->name);
235
1.89M
            } else {
236
                /* Otherwise just remove us */
237
1.89M
                topicPtr->erase(s);
238
1.89M
            }
239
2.12M
        }
240
241
        /* We also need to unlink us */
242
2.09M
        if (s->needsDrainage()) {
243
73.8k
            unlinkDrainableSubscriber(s);
244
73.8k
        }
245
246
2.09M
        delete s;
247
2.09M
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::freeSubscriber(uWS::Subscriber*)
Line
Count
Source
223
306k
    void freeSubscriber(Subscriber *s) {
224
225
        /* I guess we call this one even if we are not subscribers */
226
306k
        if (!s) {
227
68.1k
            return;
228
68.1k
        }
229
230
        /* For all topics, unsubscribe */
231
6.64M
        for (Topic *topicPtr : s->topics) {
232
            /* If we are the last subscriber, simply remove the whole topic */
233
6.64M
            if (topicPtr->size() == 1) {
234
6.51M
                topics.erase(topicPtr->name);
235
6.51M
            } else {
236
                /* Otherwise just remove us */
237
135k
                topicPtr->erase(s);
238
135k
            }
239
6.64M
        }
240
241
        /* We also need to unlink us */
242
237k
        if (s->needsDrainage()) {
243
83.9k
            unlinkDrainableSubscriber(s);
244
83.9k
        }
245
246
237k
        delete s;
247
237k
    }
248
249
    /* Mainly used by WebSocket::send to drain one socket before sending */
250
515k
    void drain(Subscriber *s) {
251
        /* The list is undefined and cannot be touched unless needsDrainage(). */
252
515k
        if (s->needsDrainage()) {
253
            /* This function differs from drainImpl by properly unlinking
254
            * the subscriber from drainableSubscribers. drainImpl does not. */
255
128k
            unlinkDrainableSubscriber(s);
256
257
            /* This one always resets needsDrainage before it calls any cb's.
258
             * Otherwise we would stackoverflow when sending after publish but before drain. */
259
128k
            drainImpl(s);
260
            
261
            /* If we drained last subscriber, also clear outgoingMessages */
262
128k
            if (!drainableSubscribers) {
263
8.36k
                outgoingMessages.clear();
264
8.36k
            }
265
128k
        }
266
515k
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::drain(uWS::Subscriber*)
Line
Count
Source
250
110k
    void drain(Subscriber *s) {
251
        /* The list is undefined and cannot be touched unless needsDrainage(). */
252
110k
        if (s->needsDrainage()) {
253
            /* This function differs from drainImpl by properly unlinking
254
            * the subscriber from drainableSubscribers. drainImpl does not. */
255
109k
            unlinkDrainableSubscriber(s);
256
257
            /* This one always resets needsDrainage before it calls any cb's.
258
             * Otherwise we would stackoverflow when sending after publish but before drain. */
259
109k
            drainImpl(s);
260
            
261
            /* If we drained last subscriber, also clear outgoingMessages */
262
109k
            if (!drainableSubscribers) {
263
554
                outgoingMessages.clear();
264
554
            }
265
109k
        }
266
110k
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::drain(uWS::Subscriber*)
Line
Count
Source
250
405k
    void drain(Subscriber *s) {
251
        /* The list is undefined and cannot be touched unless needsDrainage(). */
252
405k
        if (s->needsDrainage()) {
253
            /* This function differs from drainImpl by properly unlinking
254
            * the subscriber from drainableSubscribers. drainImpl does not. */
255
18.8k
            unlinkDrainableSubscriber(s);
256
257
            /* This one always resets needsDrainage before it calls any cb's.
258
             * Otherwise we would stackoverflow when sending after publish but before drain. */
259
18.8k
            drainImpl(s);
260
            
261
            /* If we drained last subscriber, also clear outgoingMessages */
262
18.8k
            if (!drainableSubscribers) {
263
7.80k
                outgoingMessages.clear();
264
7.80k
            }
265
18.8k
        }
266
405k
    }
267
268
    /* Called everytime we call send, to drain published messages so to sync outgoing messages */
269
21.5M
    void drain() {
270
21.5M
        if (drainableSubscribers) {
271
            /* Drain one socket a time */
272
99.6k
            for (Subscriber *s = drainableSubscribers; s; s = s->next) {
273
                /* Instead of unlinking every single subscriber, we just leave the list undefined
274
                 * and reset drainableSubscribers ptr below. */
275
86.3k
                drainImpl(s);
276
86.3k
            }
277
            /* Drain always clears drainableSubscribers and outgoingMessages */
278
13.2k
            drainableSubscribers = nullptr;
279
13.2k
            outgoingMessages.clear();
280
13.2k
        }
281
21.5M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::drain()
Line
Count
Source
269
156k
    void drain() {
270
156k
        if (drainableSubscribers) {
271
            /* Drain one socket a time */
272
80.9k
            for (Subscriber *s = drainableSubscribers; s; s = s->next) {
273
                /* Instead of unlinking every single subscriber, we just leave the list undefined
274
                 * and reset drainableSubscribers ptr below. */
275
75.3k
                drainImpl(s);
276
75.3k
            }
277
            /* Drain always clears drainableSubscribers and outgoingMessages */
278
5.64k
            drainableSubscribers = nullptr;
279
5.64k
            outgoingMessages.clear();
280
5.64k
        }
281
156k
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::drain()
Line
Count
Source
269
21.4M
    void drain() {
270
21.4M
        if (drainableSubscribers) {
271
            /* Drain one socket a time */
272
18.6k
            for (Subscriber *s = drainableSubscribers; s; s = s->next) {
273
                /* Instead of unlinking every single subscriber, we just leave the list undefined
274
                 * and reset drainableSubscribers ptr below. */
275
11.0k
                drainImpl(s);
276
11.0k
            }
277
            /* Drain always clears drainableSubscribers and outgoingMessages */
278
7.63k
            drainableSubscribers = nullptr;
279
7.63k
            outgoingMessages.clear();
280
7.63k
        }
281
21.4M
    }
282
283
    /* Big messages bypass all buffering and land directly in backpressure */
284
    template <typename F>
285
0
    bool publishBig(Subscriber *sender, std::string_view topic, B &&bigMessage, F cb) {
286
        /* Do we even have this topic? */
287
0
        auto it = topics.find(topic);
288
0
        if (it == topics.end()) {
289
0
            return false;
290
0
        }
291
292
        /* For all subscribers in topic */
293
0
        for (Subscriber *s : *it->second) {
294
295
            /* If we are sender then ignore us */
296
0
            if (sender != s) {
297
0
                cb(s, bigMessage);
298
0
            }
299
0
        }
300
301
0
        return true;
302
0
    }
Unexecuted instantiation: bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publishBig<uWS::TemplatedApp<true>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeBigMessage&&, uWS::TemplatedApp<true>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1})
Unexecuted instantiation: EpollEchoServer.cpp:bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publishBig<uWS::WebSocket<false, true, test()::PerSocketData>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeBigMessage&&, uWS::WebSocket<false, true, test()::PerSocketData>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1})
Unexecuted instantiation: bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publishBig<uWS::TemplatedApp<false>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeBigMessage&&, uWS::TemplatedApp<false>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1})
Unexecuted instantiation: bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::PreparedMessage*>::publishBig<uWS::TemplatedApp<false>::publishPrepared(std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::PreparedMessage&)::{lambda(uWS::Subscriber*, uWS::PreparedMessage*)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::PreparedMessage*&&, uWS::TemplatedApp<false>::publishPrepared(std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::PreparedMessage&)::{lambda(uWS::Subscriber*, uWS::PreparedMessage*)#1})
303
304
    /* Linear in number of affected subscribers */
305
1.36M
    bool publish(Subscriber *sender, std::string_view topic, T &&message) {
306
        /* Do we even have this topic? */
307
1.36M
        auto it = topics.find(topic);
308
1.36M
        if (it == topics.end()) {
309
43.1k
            return false;
310
43.1k
        }
311
312
        /* If we have more than 65k messages we need to drain every socket. */
313
1.32M
        if (outgoingMessages.size() == UINT16_MAX) {
314
            /* If there is a socket that is currently corked, this will be ugly as all sockets will drain
315
             * to their own backpressure */
316
3
            drain();
317
3
        }
318
319
        /* If nobody references this message, don't buffer it */
320
1.32M
        bool referencedMessage = false;
321
322
        /* For all subscribers in topic */
323
4.88M
        for (Subscriber *s : *it->second) {
324
325
            /* If we are sender then ignore us */
326
4.88M
            if (sender != s) {
327
328
                /* At least one subscriber wants this message */
329
4.75M
                referencedMessage = true;
330
331
                /* If we already have too many outgoing messages on this subscriber, drain it now */
332
4.75M
                if (s->numMessageIndices == 32) {
333
                    /* This one does not need to check needsDrainage here but still does. */
334
117k
                    drain(s);
335
117k
                }
336
337
                /* Finally we can continue */
338
4.75M
                s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size();
339
                /* First message adds subscriber to list of drainable subscribers */
340
4.75M
                if (s->numMessageIndices == 1) {
341
                    /* Insert us in the head of drainable subscribers */
342
372k
                    s->next = drainableSubscribers;
343
372k
                    s->prev = nullptr;
344
372k
                    if (s->next) {
345
291k
                        s->next->prev = s;
346
291k
                    }
347
372k
                    drainableSubscribers = s;
348
372k
                }
349
4.75M
            }
350
4.88M
        }
351
352
        /* Push this message and return with success */
353
1.32M
        if (referencedMessage) {
354
1.32M
            outgoingMessages.emplace_back(message);
355
1.32M
        }
356
357
        /* Success if someone wants it */
358
1.32M
        return referencedMessage;
359
1.36M
    }
uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::publish(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
305
1.09M
    bool publish(Subscriber *sender, std::string_view topic, T &&message) {
306
        /* Do we even have this topic? */
307
1.09M
        auto it = topics.find(topic);
308
1.09M
        if (it == topics.end()) {
309
24.8k
            return false;
310
24.8k
        }
311
312
        /* If we have more than 65k messages we need to drain every socket. */
313
1.07M
        if (outgoingMessages.size() == UINT16_MAX) {
314
            /* If there is a socket that is currently corked, this will be ugly as all sockets will drain
315
             * to their own backpressure */
316
3
            drain();
317
3
        }
318
319
        /* If nobody references this message, don't buffer it */
320
1.07M
        bool referencedMessage = false;
321
322
        /* For all subscribers in topic */
323
4.11M
        for (Subscriber *s : *it->second) {
324
325
            /* If we are sender then ignore us */
326
4.11M
            if (sender != s) {
327
328
                /* At least one subscriber wants this message */
329
4.11M
                referencedMessage = true;
330
331
                /* If we already have too many outgoing messages on this subscriber, drain it now */
332
4.11M
                if (s->numMessageIndices == 32) {
333
                    /* This one does not need to check needsDrainage here but still does. */
334
108k
                    drain(s);
335
108k
                }
336
337
                /* Finally we can continue */
338
4.11M
                s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size();
339
                /* First message adds subscriber to list of drainable subscribers */
340
4.11M
                if (s->numMessageIndices == 1) {
341
                    /* Insert us in the head of drainable subscribers */
342
258k
                    s->next = drainableSubscribers;
343
258k
                    s->prev = nullptr;
344
258k
                    if (s->next) {
345
225k
                        s->next->prev = s;
346
225k
                    }
347
258k
                    drainableSubscribers = s;
348
258k
                }
349
4.11M
            }
350
4.11M
        }
351
352
        /* Push this message and return with success */
353
1.07M
        if (referencedMessage) {
354
1.07M
            outgoingMessages.emplace_back(message);
355
1.07M
        }
356
357
        /* Success if someone wants it */
358
1.07M
        return referencedMessage;
359
1.09M
    }
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publish(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeMessage&&)
Line
Count
Source
305
271k
    bool publish(Subscriber *sender, std::string_view topic, T &&message) {
306
        /* Do we even have this topic? */
307
271k
        auto it = topics.find(topic);
308
271k
        if (it == topics.end()) {
309
18.2k
            return false;
310
18.2k
        }
311
312
        /* If we have more than 65k messages we need to drain every socket. */
313
252k
        if (outgoingMessages.size() == UINT16_MAX) {
314
            /* If there is a socket that is currently corked, this will be ugly as all sockets will drain
315
             * to their own backpressure */
316
0
            drain();
317
0
        }
318
319
        /* If nobody references this message, don't buffer it */
320
252k
        bool referencedMessage = false;
321
322
        /* For all subscribers in topic */
323
770k
        for (Subscriber *s : *it->second) {
324
325
            /* If we are sender then ignore us */
326
770k
            if (sender != s) {
327
328
                /* At least one subscriber wants this message */
329
640k
                referencedMessage = true;
330
331
                /* If we already have too many outgoing messages on this subscriber, drain it now */
332
640k
                if (s->numMessageIndices == 32) {
333
                    /* This one does not need to check needsDrainage here but still does. */
334
9.58k
                    drain(s);
335
9.58k
                }
336
337
                /* Finally we can continue */
338
640k
                s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size();
339
                /* First message adds subscriber to list of drainable subscribers */
340
640k
                if (s->numMessageIndices == 1) {
341
                    /* Insert us in the head of drainable subscribers */
342
113k
                    s->next = drainableSubscribers;
343
113k
                    s->prev = nullptr;
344
113k
                    if (s->next) {
345
65.5k
                        s->next->prev = s;
346
65.5k
                    }
347
113k
                    drainableSubscribers = s;
348
113k
                }
349
640k
            }
350
770k
        }
351
352
        /* Push this message and return with success */
353
252k
        if (referencedMessage) {
354
251k
            outgoingMessages.emplace_back(message);
355
251k
        }
356
357
        /* Success if someone wants it */
358
252k
        return referencedMessage;
359
271k
    }
360
};
361
362
}
363
364
#endif