From 0dc329c9932a63ac431c0199c7e44bb36bb3bd89 Mon Sep 17 00:00:00 2001 From: mcfnord Date: Sat, 8 Aug 2026 19:39:28 +0000 Subject: [PATCH 1/9] src/README.md: map the source folder, its threads and its locks --- src/README.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/README.md diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000000..a7af8595ab --- /dev/null +++ b/src/README.md @@ -0,0 +1,121 @@ +### Copyright (c) 2026 + +Author(s): +* mcfnord +* The Jamulus Development Team + +As of Jamulus 3.12.1dev (commit eb172d47): All new source code contributions must be licensed +under AGPL 3.0 or any later version. + +--- + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see [](https://www.gnu.org/licenses/). + +# The src folder + +This file is a map of `src/` for a reader new to the code: which class lives where, which +threads exist at runtime, and which lock protects what. Like [sound/README.md](sound/README.md), +it describes how the code behaves today, not why it was designed that way. It is not complete; +the last section lists what is still missing. + +## Where things live + +Code used by both client and server: + +- [main.cpp](main.cpp) parses the command line and constructs a `CClient` or a `CServer`. +- [protocol.cpp](protocol.cpp) — `CProtocol`: protocol message framing, acknowledgement, and + retransmission of unacknowledged messages from `SendMessQueue`. The wire format itself is + described in [../docs/JAMULUS_PROTOCOL.md](../docs/JAMULUS_PROTOCOL.md). +- [channel.cpp](channel.cpp) — `CChannel`: one connection, holding the receive jitter buffer + (`SockBuf`) and a `CProtocol` instance. The client has one; the server has a fixed array of + `MAX_NUM_CHANNELS` of them (`vecChannels`). +- [socket.cpp](socket.cpp) — `CSocket`, wrapped in `CHighPrioSocket` together with its receive + thread: the UDP socket shared by all sending and receiving. +- [buffer.h](buffer.h) — `CNetBuf` and `CNetBufWithStats`: the jitter buffer itself, including + the automatic size decision. +- [util.h](util.h) / [util.cpp](util.cpp) — `CHighPrecisionTimer` (the server's frame clock) + and assorted helpers. + +Client only: [client.cpp](client.cpp) (`CClient`), the sound layer in [sound/](sound/), the GUI +([clientdlg.cpp](clientdlg.cpp), [clientsettingsdlg.cpp](clientsettingsdlg.cpp), +[audiomixerboard.cpp](audiomixerboard.cpp), [connectdlg.cpp](connectdlg.cpp), +[chatdlg.cpp](chatdlg.cpp)), and [clientrpc.cpp](clientrpc.cpp). + +Server only: [server.cpp](server.cpp) (`CServer`: the channels and the mix), +[serverlist.cpp](serverlist.cpp) (directory registration and the server list), +[recorder/](recorder/), [serverlogging.cpp](serverlogging.cpp), +[serverrpc.cpp](serverrpc.cpp) and [serverdlg.cpp](serverdlg.cpp). + +The JSON-RPC API ([rpcserver.cpp](rpcserver.cpp), [clientrpc.cpp](clientrpc.cpp), +[serverrpc.cpp](serverrpc.cpp)) is documented in [../docs/JSON-RPC.md](../docs/JSON-RPC.md). + +## Threads + +| thread | exists | started from | what runs on it | +|---|---|---|---| +| Qt main thread | always | — | the GUI; every protocol message, parsed and created, on client and server; directory registration; JSON-RPC; and the server's complete frame cycle (see below) | +| `CSocketThread` | always | `CHighPrioSocket::Start()`, at `QThread::TimeCriticalPriority` | a blocking UDP receive loop. Audio packets are decoded into the jitter buffer synchronously, in `CChannel::PutAudioData` (client) or `CServer::PutAudioData` (server). Protocol frames are not parsed here: they are re-emitted as queued signals and handled on the main thread. | +| audio driver threads | client | the sound driver | the backend callback, which runs `CClient::AudioCallback`: Opus decode of the received stream, Opus encode of the sound card input, and the UDP send of the encoded packet | +| `CHighPrecisionT…` | server, except on Windows | `CHighPrecisionTimer::Start()`, at `QThread::TimeCriticalPriority` | only `emit timeout()` once per frame, plus the absolute-time sleep that paces it | +| `CThreadPool` workers | server with `--multithreading` | `CServer`'s constructor | Opus decode and mix/encode/send work, in per-block chunks handed out by `CServer::OnTimer` | +| recorder thread | server with recording | `CJamController` | `CJamRecorder`, fed by queued `AudioFrame` signals from the frame cycle | +| `QThreadPool` global pool | client GUI | the connect dialog | one task per listed server for the ping/info fan-out (`QtConcurrent::run`) | + +Three consequences that are easy to miss: + +- **The server's frame cycle runs on the main thread.** `CHighPrecisionTimer`'s thread runs at + `QThread::TimeCriticalPriority`, but the only work it does is `emit timeout()`. The slot on the + other side of that signal, `CServer::OnTimer` — jitter buffer drain, Opus decode, mix, encode, + transmit — executes on the main thread, because the connection between the two is queued + (verified with a debugger on Linux; a TODO in [util.cpp](util.cpp) notes the same). On Windows, + `CHighPrecisionTimer` is built on a plain `QTimer`, which fires on the main thread as well. + With `--multithreading`, the heavy blocks run on the pool, but `OnTimer` still runs every + frame and waits for all futures before it returns. +- **`CSoundBase` inherits `QThread`, but no such thread ever runs.** Nothing in + [sound/](sound/) overrides `run()` or calls `start()` on it. Audio callbacks always arrive on + threads owned by the driver. +- **The client's audio send is clocked by the sound hardware; its receive is clocked by the + network.** `CClient::ProcessAudioDataIntern` sends from inside the driver callback; arriving + packets are buffered by `CSocketThread`. The two paths meet only at the jitter buffer and at + the socket send lock. + +## Locks + +The locks taken from more than one thread: + +| lock | protects | taken from | +|---|---|---| +| `CChannel::MutexSocketBuf` | the jitter buffer | put on `CSocketThread`; get from the client's driver callback or the server's frame cycle; re-init from the main thread | +| `CSocket::Mutex` | the send path of the shared UDP socket | every `SendPacket()` call: the driver callback (client), the frame cycle and pool workers (server), and protocol code on the main thread | +| `CServer::Mutex` | connect and disconnect of channels against the frame cycle | `CServer::OnTimer` holds it while it collects the connected channels and drains and decodes their jitter buffers, and releases it before mix and send; `CServer::PutAudioData` (`CSocketThread`) and the protocol slots (main thread) take it too | +| `CChannel::Mutex` | per-channel state: the enable flag, gain and pan tables, name | setters in protocol slots on the main thread; getters in the server's frame cycle | +| `CChannel::MutexConvBuf` | the send-side conversion buffer | `PrepAndSendPacket()` on the sending thread; re-init from the main thread | + +Smaller ones: `CProtocol::Mutex` (the queue of sent but not yet acknowledged messages), +`CServer::MutexChanOrder` (channel allocation in `FindChannel` and `FreeChannel`), +`CServer::MutexWelcomeMessage`, `CClient::MutexChannels` (the client-side channel number map), +`CClient::MutexGainOrPan` (the gain/pan message rate limiter), and +`CClient::MutexDriverReinit` (serializes sound device re-initialization). The sound layer's own +locks — `MutexAudioProcessCallback`, `MutexDevProperties`, and the per-backend ones — are +covered in [sound/README.md](sound/README.md). + +## Not yet documented + +- the jitter buffer's automatic size algorithm (`CNetBufWithStats`) +- the connection lifecycle: how a channel goes from first packet to connected to timed out +- the directory: registration, the server list, and the split of + [serverlist.cpp](serverlist.cpp) between the directory role and the registered-server role +- the recorder +- [serverlogging.cpp](serverlogging.cpp), [signalhandler.cpp](signalhandler.cpp), the GUI + classes, and translation loading From 61468d2ea512446fe08f42e9def2382d5ffce675 Mon Sep 17 00:00:00 2001 From: jrd Date: Sat, 8 Aug 2026 22:51:02 +0000 Subject: [PATCH 2/9] src/README.md: shorten per review Applies @ann0see's review on #3875: - Intro cut to two sentences; the paragraph about what the file does and does not assert is gone. - File list back to one line each: the SendMessQueue detail, the SockBuf and CProtocol members and the vecChannels name are all readable in the file itself. Kept "the client has one; the server an array of MAX_NUM_CHANNELS", which is in server.h, not channel.cpp. - The three-bullet block after the thread table is one paragraph. The CSoundBase QThread note moves to src/sound/README.md (#3873), where a reader meets the class; the send/receive clocking bullet is dropped, as the table above already carries it. The parenthetical about how the thread identities were checked is dropped too: it describes the method, not the code, and the util.cpp TODO makes the point on its own. 122 lines to 105. No claim changed. --- src/README.md | 51 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/README.md b/src/README.md index a7af8595ab..3ebe1cee64 100644 --- a/src/README.md +++ b/src/README.md @@ -24,28 +24,24 @@ along with this program. If not, see [](https:// # The src folder -This file is a map of `src/` for a reader new to the code: which class lives where, which -threads exist at runtime, and which lock protects what. Like [sound/README.md](sound/README.md), -it describes how the code behaves today, not why it was designed that way. It is not complete; -the last section lists what is still missing. +A map of `src/`: which class lives where, which threads exist at runtime, and which lock +protects what. The last section lists what is not covered yet. ## Where things live Code used by both client and server: - [main.cpp](main.cpp) parses the command line and constructs a `CClient` or a `CServer`. -- [protocol.cpp](protocol.cpp) — `CProtocol`: protocol message framing, acknowledgement, and - retransmission of unacknowledged messages from `SendMessQueue`. The wire format itself is - described in [../docs/JAMULUS_PROTOCOL.md](../docs/JAMULUS_PROTOCOL.md). -- [channel.cpp](channel.cpp) — `CChannel`: one connection, holding the receive jitter buffer - (`SockBuf`) and a `CProtocol` instance. The client has one; the server has a fixed array of - `MAX_NUM_CHANNELS` of them (`vecChannels`). -- [socket.cpp](socket.cpp) — `CSocket`, wrapped in `CHighPrioSocket` together with its receive - thread: the UDP socket shared by all sending and receiving. -- [buffer.h](buffer.h) — `CNetBuf` and `CNetBufWithStats`: the jitter buffer itself, including - the automatic size decision. -- [util.h](util.h) / [util.cpp](util.cpp) — `CHighPrecisionTimer` (the server's frame clock) - and assorted helpers. +- [protocol.cpp](protocol.cpp) — `CProtocol`: message framing, acknowledgement and + retransmission. Wire format: [../docs/JAMULUS_PROTOCOL.md](../docs/JAMULUS_PROTOCOL.md). +- [channel.cpp](channel.cpp) — `CChannel`: the connection and its receive jitter buffer, used by + both client and server. The client has one; the server an array of `MAX_NUM_CHANNELS`. +- [socket.cpp](socket.cpp) — `CSocket` and `CHighPrioSocket`: the UDP socket shared by all + sending and receiving, with its receive thread. +- [buffer.h](buffer.h) — `CNetBuf` and `CNetBufWithStats`: the jitter buffer, including the + automatic size decision. +- [util.h](util.h) / [util.cpp](util.cpp) — `CHighPrecisionTimer`, the server's frame clock, and + assorted helpers. Client only: [client.cpp](client.cpp) (`CClient`), the sound layer in [sound/](sound/), the GUI ([clientdlg.cpp](clientdlg.cpp), [clientsettingsdlg.cpp](clientsettingsdlg.cpp), @@ -72,23 +68,12 @@ The JSON-RPC API ([rpcserver.cpp](rpcserver.cpp), [clientrpc.cpp](clientrpc.cpp) | recorder thread | server with recording | `CJamController` | `CJamRecorder`, fed by queued `AudioFrame` signals from the frame cycle | | `QThreadPool` global pool | client GUI | the connect dialog | one task per listed server for the ping/info fan-out (`QtConcurrent::run`) | -Three consequences that are easy to miss: - -- **The server's frame cycle runs on the main thread.** `CHighPrecisionTimer`'s thread runs at - `QThread::TimeCriticalPriority`, but the only work it does is `emit timeout()`. The slot on the - other side of that signal, `CServer::OnTimer` — jitter buffer drain, Opus decode, mix, encode, - transmit — executes on the main thread, because the connection between the two is queued - (verified with a debugger on Linux; a TODO in [util.cpp](util.cpp) notes the same). On Windows, - `CHighPrecisionTimer` is built on a plain `QTimer`, which fires on the main thread as well. - With `--multithreading`, the heavy blocks run on the pool, but `OnTimer` still runs every - frame and waits for all futures before it returns. -- **`CSoundBase` inherits `QThread`, but no such thread ever runs.** Nothing in - [sound/](sound/) overrides `run()` or calls `start()` on it. Audio callbacks always arrive on - threads owned by the driver. -- **The client's audio send is clocked by the sound hardware; its receive is clocked by the - network.** `CClient::ProcessAudioDataIntern` sends from inside the driver callback; arriving - packets are buffered by `CSocketThread`. The two paths meet only at the jitter buffer and at - the socket send lock. +**The server's frame cycle runs on the main thread.** The `CHighPrecisionTimer` thread only +emits `timeout()`; the slot behind that queued connection, `CServer::OnTimer`, does the jitter +buffer drain, decode, mix, encode and transmit — on the main thread. The TODO in +[util.cpp](util.cpp) notes the same escape from the timer thread. On Windows the pacer is a +plain `QTimer`, also main thread. With `--multithreading` the heavy blocks go to the pool, but +`OnTimer` waits for them. ## Locks From 677a0d97cee175ce5bf9c7e6c6cd32310b6237bf Mon Sep 17 00:00:00 2001 From: John Dempsey <1750243+mcfnord@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:26:03 -0700 Subject: [PATCH 3/9] Apply suggestion from @mcfnord --- src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/README.md b/src/README.md index 3ebe1cee64..65b7447995 100644 --- a/src/README.md +++ b/src/README.md @@ -27,7 +27,7 @@ along with this program. If not, see [](https:// A map of `src/`: which class lives where, which threads exist at runtime, and which lock protects what. The last section lists what is not covered yet. -## Where things live +# Where things live Code used by both client and server: From 9b59d4fe8a1d3f30bfdd22296579de57d2d88397 Mon Sep 17 00:00:00 2001 From: John Dempsey <1750243+mcfnord@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:26:10 -0700 Subject: [PATCH 4/9] Apply suggestion from @mcfnord --- src/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/README.md b/src/README.md index 65b7447995..f8da958968 100644 --- a/src/README.md +++ b/src/README.md @@ -22,7 +22,6 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see [](https://www.gnu.org/licenses/). -# The src folder A map of `src/`: which class lives where, which threads exist at runtime, and which lock protects what. The last section lists what is not covered yet. From 691c1c5856e1f18ca739ec2610782bda4279877d Mon Sep 17 00:00:00 2001 From: John Dempsey <1750243+mcfnord@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:26:18 -0700 Subject: [PATCH 5/9] Apply suggestion from @mcfnord --- src/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/README.md b/src/README.md index f8da958968..e758cecc36 100644 --- a/src/README.md +++ b/src/README.md @@ -23,7 +23,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see [](https://www.gnu.org/licenses/). -A map of `src/`: which class lives where, which threads exist at runtime, and which lock protects what. The last section lists what is not covered yet. # Where things live From 24b85662e71f8b24f1665111c7b754cf798a3535 Mon Sep 17 00:00:00 2001 From: John Dempsey <1750243+mcfnord@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:26:25 -0700 Subject: [PATCH 6/9] Apply suggestion from @mcfnord --- src/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/README.md b/src/README.md index e758cecc36..6c9cd75a55 100644 --- a/src/README.md +++ b/src/README.md @@ -23,7 +23,6 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see [](https://www.gnu.org/licenses/). -protects what. The last section lists what is not covered yet. # Where things live From 33202fad810b10630e217e0fc9a8d93b27a50191 Mon Sep 17 00:00:00 2001 From: jrd Date: Tue, 11 Aug 2026 17:25:42 +0000 Subject: [PATCH 7/9] docs(src/README.md): format smaller locks as a bulleted list for clarity --- src/README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/README.md b/src/README.md index 6c9cd75a55..38aee9a8b3 100644 --- a/src/README.md +++ b/src/README.md @@ -84,13 +84,15 @@ The locks taken from more than one thread: | `CChannel::Mutex` | per-channel state: the enable flag, gain and pan tables, name | setters in protocol slots on the main thread; getters in the server's frame cycle | | `CChannel::MutexConvBuf` | the send-side conversion buffer | `PrepAndSendPacket()` on the sending thread; re-init from the main thread | -Smaller ones: `CProtocol::Mutex` (the queue of sent but not yet acknowledged messages), -`CServer::MutexChanOrder` (channel allocation in `FindChannel` and `FreeChannel`), -`CServer::MutexWelcomeMessage`, `CClient::MutexChannels` (the client-side channel number map), -`CClient::MutexGainOrPan` (the gain/pan message rate limiter), and -`CClient::MutexDriverReinit` (serializes sound device re-initialization). The sound layer's own -locks — `MutexAudioProcessCallback`, `MutexDevProperties`, and the per-backend ones — are -covered in [sound/README.md](sound/README.md). +**Smaller locks:** + +- `CProtocol::Mutex` — queue of sent but not yet acknowledged messages +- `CServer::MutexChanOrder` — channel allocation in `FindChannel` and `FreeChannel` +- `CServer::MutexWelcomeMessage` +- `CClient::MutexChannels` — client-side channel number map +- `CClient::MutexGainOrPan` — gain/pan message rate limiter +- `CClient::MutexDriverReinit` — serializes sound device re-initialization +- Sound layer locks (`MutexAudioProcessCallback`, `MutexDevProperties`, per-backend) — see [sound/README.md](sound/README.md) ## Not yet documented From 8d81181e13ae5356181cfc08987473799d161138 Mon Sep 17 00:00:00 2001 From: jrd Date: Thu, 13 Aug 2026 02:24:05 +0000 Subject: [PATCH 8/9] src/README.md: correct the protocol-parsing thread split and the pool condition Two rows of the Threads table were wrong, both found by binding each quantified sentence to a command that would make it false. The CSocketThread row said "Protocol frames are not parsed here". CProtocol::ParseMessageFrame is called on exactly that thread (socket.cpp:643, its only call site) and does the tag, length and CRC validation plus extraction of the body, ID and counter. What crosses to the main thread is the message body, via the queued ProtocolMessageReceived signal, where ParseMessageBody runs it. The protocol API splits frame from body by name and the row asserted the inverse; the Qt main thread row inherited the same error and now says "message body". The CThreadPool row said "server with --multithreading". CServer's constructor also requires more than one core: on idealThreadCount() == 1 it logs "found only one core, disabling multithreading", clears the flag and creates no pool. A single-core server is a normal deployment, so the row described threads that do not exist there. Added to the frame-cycle paragraph as well. Also collapses the blank lines left in the licence block by an earlier suggestion apply. Co-Authored-By: Claude Opus 5 --- src/README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/README.md b/src/README.md index 38aee9a8b3..490e3accb3 100644 --- a/src/README.md +++ b/src/README.md @@ -22,8 +22,6 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see [](https://www.gnu.org/licenses/). - - # Where things live Code used by both client and server: @@ -57,11 +55,11 @@ The JSON-RPC API ([rpcserver.cpp](rpcserver.cpp), [clientrpc.cpp](clientrpc.cpp) | thread | exists | started from | what runs on it | |---|---|---|---| -| Qt main thread | always | — | the GUI; every protocol message, parsed and created, on client and server; directory registration; JSON-RPC; and the server's complete frame cycle (see below) | -| `CSocketThread` | always | `CHighPrioSocket::Start()`, at `QThread::TimeCriticalPriority` | a blocking UDP receive loop. Audio packets are decoded into the jitter buffer synchronously, in `CChannel::PutAudioData` (client) or `CServer::PutAudioData` (server). Protocol frames are not parsed here: they are re-emitted as queued signals and handled on the main thread. | +| Qt main thread | always | — | the GUI; every protocol message body, parsed and created, on client and server; directory registration; JSON-RPC; and the server's complete frame cycle (see below) | +| `CSocketThread` | always | `CHighPrioSocket::Start()`, at `QThread::TimeCriticalPriority` | a blocking UDP receive loop. Audio packets are decoded into the jitter buffer synchronously, in `CChannel::PutAudioData` (client) or `CServer::PutAudioData` (server). Protocol messages are split across the two threads: `CProtocol::ParseMessageFrame` validates the frame here, then the body is re-emitted as a queued signal and `ParseMessageBody` runs it on the main thread. | | audio driver threads | client | the sound driver | the backend callback, which runs `CClient::AudioCallback`: Opus decode of the received stream, Opus encode of the sound card input, and the UDP send of the encoded packet | | `CHighPrecisionT…` | server, except on Windows | `CHighPrecisionTimer::Start()`, at `QThread::TimeCriticalPriority` | only `emit timeout()` once per frame, plus the absolute-time sleep that paces it | -| `CThreadPool` workers | server with `--multithreading` | `CServer`'s constructor | Opus decode and mix/encode/send work, in per-block chunks handed out by `CServer::OnTimer` | +| `CThreadPool` workers | server with `--multithreading`, on more than one core | `CServer`'s constructor | Opus decode and mix/encode/send work, in per-block chunks handed out by `CServer::OnTimer` | | recorder thread | server with recording | `CJamController` | `CJamRecorder`, fed by queued `AudioFrame` signals from the frame cycle | | `QThreadPool` global pool | client GUI | the connect dialog | one task per listed server for the ping/info fan-out (`QtConcurrent::run`) | @@ -70,7 +68,8 @@ emits `timeout()`; the slot behind that queued connection, `CServer::OnTimer`, d buffer drain, decode, mix, encode and transmit — on the main thread. The TODO in [util.cpp](util.cpp) notes the same escape from the timer thread. On Windows the pacer is a plain `QTimer`, also main thread. With `--multithreading` the heavy blocks go to the pool, but -`OnTimer` waits for them. +`OnTimer` waits for them — and on a machine reporting one core, `CServer`'s constructor turns the +option back off, so no pool thread is created at all. ## Locks From 58b82bd37515225e3d5a89a2631cdf53e79c57dc Mon Sep 17 00:00:00 2001 From: jrd Date: Thu, 13 Aug 2026 02:27:03 +0000 Subject: [PATCH 9/9] src/README.md: address the three open review comments pljones on #3875: "Doesn't need these lines. This only applied to existing files. New files should only have the AGPL header." -- the "As of Jamulus 3.12.1dev" transition note is dropped. A file created after 3.12.1dev has no pre-3.12.1dev history for it to describe. tools/update-copyright-notices.sh carries that sentence only in its own header comment and does not scan .md files for it, so nothing depends on it being here. "Section, laid out as bullets like the shared code. Same for the following ones." -- Client only and Server only are bulleted like the shared list, one line per file with what it holds. "Does this mean it's unused?" on CServer::MutexWelcomeMessage -- no, it is taken in OnNewConnection and SetWelcomeMessage. But it is not taken by the other three readers of strWelcomeMessage (OnCLReqServerFeatures, OnCLReqWelcomeMessage, GetWelcomeMessage), and all five accessors are reached on the main thread: the two CL slots hang off ConnLessProtocol, whose signals are emitted on the main thread behind the queued OnProtocolCLMessageReceived, and GetWelcomeMessage is called from serverdlg, serverrpc and settings. The bullet now says so rather than standing bare. Co-Authored-By: Claude Opus 5 --- src/README.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/README.md b/src/README.md index 490e3accb3..eab6de4888 100644 --- a/src/README.md +++ b/src/README.md @@ -4,9 +4,6 @@ Author(s): * mcfnord * The Jamulus Development Team -As of Jamulus 3.12.1dev (commit eb172d47): All new source code contributions must be licensed -under AGPL 3.0 or any later version. - --- This program is free software: you can redistribute it and/or modify @@ -38,15 +35,23 @@ Code used by both client and server: - [util.h](util.h) / [util.cpp](util.cpp) — `CHighPrecisionTimer`, the server's frame clock, and assorted helpers. -Client only: [client.cpp](client.cpp) (`CClient`), the sound layer in [sound/](sound/), the GUI -([clientdlg.cpp](clientdlg.cpp), [clientsettingsdlg.cpp](clientsettingsdlg.cpp), -[audiomixerboard.cpp](audiomixerboard.cpp), [connectdlg.cpp](connectdlg.cpp), -[chatdlg.cpp](chatdlg.cpp)), and [clientrpc.cpp](clientrpc.cpp). +Client only: + +- [client.cpp](client.cpp) — `CClient`: the client's audio path and its one channel. +- [sound/](sound/) — the sound layer, one backend per platform. See [sound/README.md](sound/README.md). +- [clientdlg.cpp](clientdlg.cpp), [clientsettingsdlg.cpp](clientsettingsdlg.cpp), + [audiomixerboard.cpp](audiomixerboard.cpp), [connectdlg.cpp](connectdlg.cpp), + [chatdlg.cpp](chatdlg.cpp) — the GUI. +- [clientrpc.cpp](clientrpc.cpp) — the client half of the JSON-RPC API. + +Server only: -Server only: [server.cpp](server.cpp) (`CServer`: the channels and the mix), -[serverlist.cpp](serverlist.cpp) (directory registration and the server list), -[recorder/](recorder/), [serverlogging.cpp](serverlogging.cpp), -[serverrpc.cpp](serverrpc.cpp) and [serverdlg.cpp](serverdlg.cpp). +- [server.cpp](server.cpp) — `CServer`: the channels and the mix. +- [serverlist.cpp](serverlist.cpp) — directory registration and the server list. +- [recorder/](recorder/) — `CJamController` and `CJamRecorder`. +- [serverlogging.cpp](serverlogging.cpp) — the connection log. +- [serverrpc.cpp](serverrpc.cpp) — the server half of the JSON-RPC API. +- [serverdlg.cpp](serverdlg.cpp) — the server GUI. The JSON-RPC API ([rpcserver.cpp](rpcserver.cpp), [clientrpc.cpp](clientrpc.cpp), [serverrpc.cpp](serverrpc.cpp)) is documented in [../docs/JSON-RPC.md](../docs/JSON-RPC.md). @@ -87,7 +92,9 @@ The locks taken from more than one thread: - `CProtocol::Mutex` — queue of sent but not yet acknowledged messages - `CServer::MutexChanOrder` — channel allocation in `FindChannel` and `FreeChannel` -- `CServer::MutexWelcomeMessage` +- `CServer::MutexWelcomeMessage` — the welcome message string. Taken in `OnNewConnection` and + `SetWelcomeMessage` only; the other readers (`OnCLReqServerFeatures`, `OnCLReqWelcomeMessage`, + `GetWelcomeMessage`) do not take it, and every one of them is reached on the main thread. - `CClient::MutexChannels` — client-side channel number map - `CClient::MutexGainOrPan` — gain/pan message rate limiter - `CClient::MutexDriverReinit` — serializes sound device re-initialization