Skip to content

Commit ae3e59b

Browse files
authored
Merge PR #6382: Backport "Merge PR #6372: FIX(server): Stale user pointers in whisper cache " to 1.5.x
2 parents dc1ac26 + 5fb748b commit ae3e59b

File tree

4 files changed

+140
-91
lines changed

4 files changed

+140
-91
lines changed

src/murmur/Messages.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,26 +2192,30 @@ void Server::msgVoiceTarget(ServerUser *uSource, MumbleProto::VoiceTarget &msg)
21922192
const MumbleProto::VoiceTarget_Target &t = msg.targets(i);
21932193
for (int j = 0; j < t.session_size(); ++j) {
21942194
unsigned int s = t.session(j);
2195-
if (qhUsers.contains(s))
2196-
wt.qlSessions << s;
2195+
if (qhUsers.contains(s)) {
2196+
wt.sessions.push_back(s);
2197+
}
21972198
}
21982199
if (t.has_channel_id()) {
21992200
unsigned int id = t.channel_id();
22002201
if (qhChannels.contains(id)) {
22012202
WhisperTarget::Channel wtc;
2202-
wtc.iId = static_cast< int >(id);
2203-
wtc.bChildren = t.children();
2204-
wtc.bLinks = t.links();
2205-
if (t.has_group())
2206-
wtc.qsGroup = u8(t.group());
2207-
wt.qlChannels << wtc;
2203+
wtc.id = id;
2204+
wtc.includeChildren = t.children();
2205+
wtc.includeLinks = t.links();
2206+
if (t.has_group()) {
2207+
wtc.targetGroup = u8(t.group());
2208+
}
2209+
2210+
wt.channels.push_back(wtc);
22082211
}
22092212
}
22102213
}
2211-
if (wt.qlSessions.isEmpty() && wt.qlChannels.isEmpty())
2214+
if (wt.sessions.empty() && wt.channels.empty()) {
22122215
uSource->qmTargets.remove(target);
2213-
else
2214-
uSource->qmTargets.insert(target, wt);
2216+
} else {
2217+
uSource->qmTargets.insert(target, std::move(wt));
2218+
}
22152219
}
22162220
}
22172221

src/murmur/Server.cpp

Lines changed: 114 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,90 +1230,31 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
12301230
} else {
12311231
ZoneScopedN(TracyConstants::AUDIO_WHISPER_CACHE_CREATE);
12321232

1233-
const WhisperTarget &wt = u->qmTargets.value(static_cast< int >(audioData.targetOrContext));
1234-
if (!wt.qlChannels.isEmpty()) {
1235-
QMutexLocker qml(&qmCache);
1236-
1237-
foreach (const WhisperTarget::Channel &wtc, wt.qlChannels) {
1238-
Channel *wc = qhChannels.value(static_cast< unsigned int >(wtc.iId));
1239-
if (wc) {
1240-
bool link = wtc.bLinks && !wc->qhLinks.isEmpty();
1241-
bool dochildren = wtc.bChildren && !wc->qlChannels.isEmpty();
1242-
bool group = !wtc.qsGroup.isEmpty();
1243-
if (!link && !dochildren && !group) {
1244-
// Common case
1245-
if (ChanACL::hasPermission(u, wc, ChanACL::Whisper, &acCache)) {
1246-
foreach (User *p, wc->qlUsers) { channel.insert(static_cast< ServerUser * >(p)); }
1247-
1248-
foreach (unsigned int currentSession,
1249-
m_channelListenerManager.getListenersForChannel(wc->iId)) {
1250-
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
1251-
1252-
if (pDst) {
1253-
addListener(cachedListeners, *pDst, *wc);
1254-
}
1255-
}
1256-
}
1257-
} else {
1258-
QSet< Channel * > channels;
1259-
if (link)
1260-
channels = wc->allLinks();
1261-
else
1262-
channels.insert(wc);
1263-
if (dochildren)
1264-
channels.unite(wc->allChildren());
1265-
const QString &redirect = u->qmWhisperRedirect.value(wtc.qsGroup);
1266-
const QString &qsg = redirect.isEmpty() ? wtc.qsGroup : redirect;
1267-
foreach (Channel *tc, channels) {
1268-
if (ChanACL::hasPermission(u, tc, ChanACL::Whisper, &acCache)) {
1269-
foreach (User *p, tc->qlUsers) {
1270-
ServerUser *su = static_cast< ServerUser * >(p);
1271-
1272-
if (!group || Group::appliesToUser(*tc, *tc, qsg, *su)) {
1273-
channel.insert(su);
1274-
}
1275-
}
1276-
1277-
foreach (unsigned int currentSession,
1278-
m_channelListenerManager.getListenersForChannel(tc->iId)) {
1279-
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
1280-
1281-
if (pDst && (!group || Group::appliesToUser(*tc, *tc, qsg, *pDst))) {
1282-
// Only send audio to listener if the user exists and it is in the group the
1283-
// speech is directed at (if any)
1284-
addListener(cachedListeners, *pDst, *tc);
1285-
}
1286-
}
1287-
}
1288-
}
1289-
}
1290-
}
1291-
}
1233+
const unsigned int uiSession = u->uiSession;
1234+
qrwlVoiceThread.unlock();
1235+
qrwlVoiceThread.lockForWrite();
1236+
1237+
if (!qhUsers.contains(uiSession)) {
1238+
return;
12921239
}
12931240

1294-
{
1295-
QMutexLocker qml(&qmCache);
1241+
// Create cache entry for the given target
1242+
// Note: We have to compute the cache entry and add it to the user's cache store in an atomic
1243+
// transaction (ensured by the lock) to avoid running into situations in which a user from the cache
1244+
// gets deleted without this particular cache entry being purged (which happens, if the cache entry is
1245+
// in the store at the point of deleting the user).
1246+
const WhisperTarget &wt = u->qmTargets.value(static_cast< int >(audioData.targetOrContext));
1247+
WhisperTargetCache cache = createWhisperTargetCacheFor(*u, wt);
12961248

1297-
foreach (unsigned int id, wt.qlSessions) {
1298-
ServerUser *pDst = qhUsers.value(id);
1299-
if (pDst && ChanACL::hasPermission(u, pDst->cChannel, ChanACL::Whisper, &acCache)
1300-
&& !channel.contains(pDst))
1301-
direct.insert(pDst);
1302-
}
1303-
}
1249+
u->qmTargetCache.insert(static_cast< int >(audioData.targetOrContext), std::move(cache));
13041250

1305-
unsigned int uiSession = u->uiSession;
1306-
qrwlVoiceThread.unlock();
1307-
qrwlVoiceThread.lockForWrite();
13081251

1309-
if (qhUsers.contains(uiSession))
1310-
u->qmTargetCache.insert(static_cast< int >(audioData.targetOrContext),
1311-
{ channel, direct, cachedListeners });
13121252
qrwlVoiceThread.unlock();
13131253
qrwlVoiceThread.lockForRead();
13141254
if (!qhUsers.contains(uiSession))
13151255
return;
13161256
}
1257+
13171258
// These users receive the audio because someone is shouting to their channel
13181259
for (ServerUser *pDst : channel) {
13191260
buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::SHOUT, audioData.containsPositionalData);
@@ -2412,5 +2353,104 @@ bool Server::canNest(Channel *newParent, Channel *channel) const {
24122353
return (parentLevel + channelDepth) < iChannelNestingLimit;
24132354
}
24142355

2356+
WhisperTargetCache Server::createWhisperTargetCacheFor(ServerUser &speaker, const WhisperTarget &target) {
2357+
ZoneScoped;
2358+
2359+
QMutexLocker qml(&qmCache);
2360+
2361+
WhisperTargetCache cache;
2362+
2363+
if (!target.channels.empty()) {
2364+
for (const WhisperTarget::Channel &currentTarget : target.channels) {
2365+
Channel *targetChannel = qhChannels.value(currentTarget.id);
2366+
2367+
if (targetChannel) {
2368+
bool includeLinks = currentTarget.includeLinks && !targetChannel->qhLinks.isEmpty();
2369+
bool includeChildren = currentTarget.includeChildren && !targetChannel->qlChannels.isEmpty();
2370+
bool restrictToGroup = !currentTarget.targetGroup.isEmpty();
2371+
2372+
if (!includeLinks && !includeChildren && !restrictToGroup) {
2373+
// Common case
2374+
if (ChanACL::hasPermission(&speaker, targetChannel, ChanACL::Whisper, &acCache)) {
2375+
for (User *p : targetChannel->qlUsers) {
2376+
// Add users of the target channel
2377+
cache.channelTargets.insert(static_cast< ServerUser * >(p));
2378+
}
2379+
2380+
for (unsigned int currentSession :
2381+
m_channelListenerManager.getListenersForChannel(targetChannel->iId)) {
2382+
// Add users that listen to the target channel (duplicates with users directly
2383+
// in this channel are handled further down)
2384+
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
2385+
2386+
if (pDst) {
2387+
addListener(cache.listeningTargets, *pDst, *targetChannel);
2388+
}
2389+
}
2390+
}
2391+
} else {
2392+
QSet< Channel * > channels;
2393+
2394+
if (includeLinks) {
2395+
// allLinks contains the channel itself
2396+
channels = targetChannel->allLinks();
2397+
} else {
2398+
channels.insert(targetChannel);
2399+
}
2400+
2401+
if (includeChildren) {
2402+
channels.unite(targetChannel->allChildren());
2403+
}
2404+
2405+
// The target group might be changed by a redirect set up via RPC (Ice/gRPC). In that
2406+
// case the shout is sent to the redirection target instead the originally specified group
2407+
const QString &redirect = speaker.qmWhisperRedirect.value(currentTarget.targetGroup);
2408+
const QString &targetGroup = redirect.isEmpty() ? currentTarget.targetGroup : redirect;
2409+
2410+
for (Channel *subTargetChan : channels) {
2411+
if (ChanACL::hasPermission(&speaker, subTargetChan, ChanACL::Whisper, &acCache)) {
2412+
for (User *p : subTargetChan->qlUsers) {
2413+
ServerUser *su = static_cast< ServerUser * >(p);
2414+
2415+
if (!restrictToGroup
2416+
|| Group::appliesToUser(*subTargetChan, *subTargetChan, targetGroup, *su)) {
2417+
cache.channelTargets.insert(su);
2418+
}
2419+
}
2420+
2421+
for (unsigned int currentSession :
2422+
m_channelListenerManager.getListenersForChannel(subTargetChan->iId)) {
2423+
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
2424+
2425+
if (pDst
2426+
&& (!restrictToGroup
2427+
|| Group::appliesToUser(*subTargetChan, *subTargetChan, targetGroup, *pDst))) {
2428+
// Only send audio to listener if the user exists and it is in the group the
2429+
// speech is directed at (if any)
2430+
addListener(cache.listeningTargets, *pDst, *subTargetChan);
2431+
}
2432+
}
2433+
}
2434+
}
2435+
}
2436+
}
2437+
}
2438+
}
2439+
2440+
for (unsigned int id : target.sessions) {
2441+
ServerUser *pDst = qhUsers.value(id);
2442+
if (pDst && ChanACL::hasPermission(&speaker, pDst->cChannel, ChanACL::Whisper, &acCache)
2443+
&& !cache.channelTargets.contains(pDst))
2444+
cache.directTargets.insert(pDst);
2445+
}
2446+
2447+
// Make sure the speaker themselves is not contained in these lists
2448+
cache.channelTargets.remove(&speaker);
2449+
cache.directTargets.remove(&speaker);
2450+
cache.listeningTargets.remove(&speaker);
2451+
2452+
return cache;
2453+
}
2454+
24152455
#undef SIO_UDP_CONNRESET
24162456
#undef SENDTO

src/murmur/Server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ class Server : public QThread {
203203
QTimer qtTick;
204204
void initRegister();
205205

206+
WhisperTargetCache createWhisperTargetCacheFor(ServerUser &speaker, const WhisperTarget &target);
207+
206208
private:
207209
int iChannelNestingLimit;
208210
int iChannelCountLimit;

src/murmur/ServerUser.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
# include <sys/socket.h>
2828
#endif
2929

30+
#include <vector>
31+
3032
// Unfortunately, this needs to be "large enough" to hold
3133
// enough frames to account for both short-term and
3234
// long-term "maladjustments".
@@ -52,13 +54,14 @@ struct BandwidthRecord {
5254

5355
struct WhisperTarget {
5456
struct Channel {
55-
int iId;
56-
bool bChildren;
57-
bool bLinks;
58-
QString qsGroup;
57+
unsigned int id;
58+
bool includeChildren;
59+
bool includeLinks;
60+
QString targetGroup;
5961
};
60-
QList< unsigned int > qlSessions;
61-
QList< WhisperTarget::Channel > qlChannels;
62+
63+
std::vector< unsigned int > sessions;
64+
std::vector< WhisperTarget::Channel > channels;
6265
};
6366

6467
class ServerUser;

0 commit comments

Comments
 (0)