Skip to content

src/README.md: map the source folder, its threads and its locks - #3875

Open
mcfnord wants to merge 10 commits into
jamulussoftware:mainfrom
mcfnord:docs-src-readme
Open

src/README.md: map the source folder, its threads and its locks#3875
mcfnord wants to merge 10 commits into
jamulussoftware:mainfrom
mcfnord:docs-src-readme

Conversation

@mcfnord

@mcfnord mcfnord commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

MY LLM WROTE:

Short description of changes

src/ has no README. This adds one: a map for a reader new to the code — which class lives where, which threads exist at runtime, and which lock protects what. Same shape as the section recently proposed for src/sound/README.md in #3873: it describes how the code behaves today, asserts no intent, and ends with an explicit list of what is still missing.

What it covers:

  • Where things live — one line per major class, split into shared / client-only / server-only, with links to the protocol and JSON-RPC docs.
  • Threads — a table of every thread: the Qt main thread, CSocketThread, the audio driver threads, CHighPrecisionTimer's thread, the CThreadPool workers, the recorder thread, and the connect dialog's use of the global pool. Followed by the three facts easiest to miss: the server's complete frame cycle (CServer::OnTimer) executes on the main thread — the TimeCriticalPriority timer thread only paces a queued emit timeout() (confirmed with a debugger on Linux, and the TODO in util.cpp says the same); CSoundBase inherits QThread but that thread is never started; the client sends audio from inside the driver callback while receiving on CSocketThread.
  • Locks — a table of the five locks taken from more than one thread (MutexSocketBuf, CSocket::Mutex, CServer::Mutex including exactly which part of OnTimer holds it, CChannel::Mutex, MutexConvBuf), each with what it protects and the threads that take it, plus one line each for the smaller ones.
  • Not yet documented — the jitter buffer size algorithm, connection lifecycle, directory registration, the recorder, logging, signal handling, the GUI classes.

The thread table is measured, not read: each identity was confirmed by breaking on the function in a release build under gdb on Linux and recording which thread hit (CServer::OnTimer and CProtocol::ParseMessageBody on the main thread, CServer::PutAudioData and CChannel::PutAudioData on CSocketThread, CClient::ProcessAudioDataIntern on the JACK callback thread, CHighPrecisionTimer::run on its own thread).

Two design choices, offered for discussion since later doc files could follow the pattern:

  • Every cross-reference is a relative link whose link text is the path itself — [channel.cpp](channel.cpp), [../docs/JAMULUS_PROTOCOL.md](../docs/JAMULUS_PROTOCOL.md). Rendered on GitHub, the map becomes browseable: every named file is one click away. Read raw in a terminal or an editor, each link degrades to the path plus punctuation, so the text still works for a reader without a browser.
  • No link carries a line number, because line numbers go stale with the next commit to the file. Functions, classes and members are named in backticks instead, so a plain grep finds them from either the rendered or the raw form.

CHANGELOG: SKIP

Context: Fixes an issue?

No issue. Follows #3873, which starts the sound-layer half of the same documentation; this file links to it rather than repeating it.

Does this change need documentation? What needs to be documented and how?

This is the documentation. Developer-facing, so it belongs next to the code rather than on the website.

Status of this Pull Request

Working implementation. Every statement is checkable against the tree at the commit it was written on, and the thread identities were verified at runtime rather than inferred from the source.

What is missing until this pull request can be merged?

Review. One open question for reviewers: whether the license header on a brand-new documentation file should carry the full historical GPL paragraph or only the AGPL block used here.

Checklist

  • I've verified that this Pull Request follows the general code principles
  • I tested my code and it does what I want
  • My code follows the style guide
  • I waited some time after this Pull Request was opened and all GitHub checks completed without errors.
  • I've filled all the content above

No checks run on this one: autobuild.yml carries paths-ignore: '**README.md' and coding-style-check.yml only triggers on **.cpp/**.h, so the fourth box stays unticked rather than claiming a green run that never happened.

Comment thread src/README.md Outdated

@ann0see ann0see left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd aim this at human contributors for now.

@ann0see

ann0see commented Aug 8, 2026

Copy link
Copy Markdown
Member

Review. One open question for reviewers: whether the license header on a brand-new documentation file should carry the full historical GPL paragraph or only the AGPL block used here.

AGPL. But I think it's weird as is. I'd think that a HTML comment for the full license would be enough and a visible short link to the license file. But @pljones should judge. I really dislike a wall of text first just stating the license. Nobody reads it.

Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
@mcfnord

mcfnord commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

I'd aim this at human contributors for now.

I believe if we write for a human audience, we will dramatically improve LLM outcomes "for free".

Applies @ann0see's review on jamulussoftware#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 (jamulussoftware#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.
mcfnord pushed a commit to mcfnord/jamulus that referenced this pull request Aug 8, 2026
CSoundBase derives from QThread, so a reader can reasonably expect a
sound thread. There is none: no override of run() and no call to start()
exists in the sound layer -- the only two run() overrides in src/ are
CHighPrecisionTimer (util.h) and CSocketThread (socket.h). Audio
callbacks always arrive on driver-owned threads.

Moved here from the src/README.md draft (jamulussoftware#3875), where it sat under the
thread table; this is the file that introduces CSoundBase.
@mcfnord mcfnord mentioned this pull request Aug 8, 2026
4 tasks
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
@dingodoppelt dingodoppelt added the AI AI generated or potentially AI generated label Aug 9, 2026
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a separate Channel.md as it's pretty much "here's what Jamulus really is".

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I think we should have specifics in the code directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have specifics in the code directly.

You said this somewhere else... these are Markdown files in the repo, which means "in the code" to me, but are you saying you'd prefer detailed comments inline within C++ source code files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a somewhat larger comment in the cpp or h file describing roughly what this file does/fits into the architecture. The markdown files have a very high level description or none at all.

E.g. the protocol has in source file docs:

jamulus/src/protocol.cpp

Lines 47 to 72 in 4a43f6f

/*
Protocol message definition
---------------------------
- All messages received need to be acknowledged by an acknowledge packet (except
of connection less messages)
MAIN FRAME
----------
+-------------+------------+------------+------------------+ ...
| 2 bytes TAG | 2 bytes ID | 1 byte cnt | 2 bytes length n | ...
+-------------+------------+------------+------------------+ ...
... --------------+-------------+
... n bytes data | 2 bytes CRC |
... --------------+-------------+
- TAG is an all zero bit word to identify protocol messages
- message ID defined by the defines PROTMESSID_x
- cnt: counter which is increment for each message and wraps around at 255
- length n in bytes of the data
- actual data, dependent on message type
- 16 bits CRC, calculated over the entire message and is transmitted inverted
Generator polynom: G_16(x) = x^16 + x^12 + x^5 + 1, initial state: all ones

jrd and others added 2 commits August 13, 2026 02:24
… 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 <noreply@anthropic.com>
pljones on jamulussoftware#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 <noreply@anthropic.com>
@mcfnord

mcfnord commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 AI: Pushed. Two of these are corrections to the Threads table, found by binding each quantified sentence to a command that would make it false rather than one that confirms it.

The CSocketThread row said protocol frames are not parsed there. CProtocol::ParseMessageFrame is called on exactly that thread — its only call site — and does the tag, length and CRC check plus extraction of the body, ID and counter. What crosses to the main thread is the body, via the queued signal, where ParseMessageBody runs it. The protocol API splits frame from body by name and the row asserted the inverse; the main-thread row inherited the same error and now reads "message body".

The CThreadPool row said "server with --multithreading". CServer's constructor also requires more than one core: at 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 that row described threads which do not exist there.

The rest is review follow-up: the licence transition note is dropped from this new file, Client only and Server only are bulleted like the shared list, and the blank lines an earlier suggestion-apply left in the header block are collapsed.

Comment thread src/README.md Outdated
Comment thread src/README.md
Comment thread src/README.md Outdated
Comment thread src/README.md Outdated
…o, headings

ann0see on jamulussoftware#3875, 2026-08-31:

"I still don't like the license disclaimer here. I think this confuses people
and agents. There must be a short way to state that it's AGPL licensed (more
details in the COPYING file)" -- and, on the same point in the conversation,
"a HTML comment for the full license would be enough and a visible short link
to the license file". The notice moves into an HTML comment and the visible
form is one line linking ../COPYING. pljones had already settled the content:
AGPL only, no historical GPL paragraph.

"# Main Jamulus codebase" and "This directory contains the main code of
Jamulus." -- both suggestions applied verbatim.

"## Jamulus Architecture / ### Threading", plus "1-2 sentences about the very
general architecture ... especially the datapath from input device, client,
encoding/decoding, server, mixin, client. But very brief" -- four lines. Every
clause is from the code: the client encodes in ProcessAudioDataIntern and
hands the packet to Channel.PrepAndSendPacket; CSocket is SOCK_DGRAM
(socket.cpp:192, 252); CServer::OnTimer decodes each channel
(server.cpp:986); MixEncodeTransmitData sums every client j into the target
channel's buffer with vecvecfGains[iChanCnt][j], which is filled from
vecChannels[iCurChanID].GetGain(...) -- the listening channel's own gain
table, so the mix is per-client; the result is re-encoded (server.cpp:1282)
and sent. The client's own callback then does Channel.GetData followed by
opus_custom_decode into the sound card buffer. Locks becomes a subheading so
the hierarchy holds.

"I guess we could drop this section." / "Yes. Please drop it." -- the "Not yet
documented" section is gone.

pljones asked whether the connection lifecycle belonged in a separate
Channel.md; ann0see answered "No. I think we should have specifics in the code
directly", so no such file is added.

Also fixed: the CHighPrecisionTimer row of the thread table carried a
truncated class name, "CHighPrecisionT" followed by a U+2026 ellipsis, so a
grep for the class did not find the row that describes it.

Earlier, still-open note from ann0see -- "you should probably not describe too
much of the file content ... So here: Implements the channel + jitter buffer
used for Server and Client." -- the channel.cpp bullet loses its second
sentence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

This comment was marked as off-topic.

@ann0see

This comment was marked as off-topic.

@coderabbitai

This comment was marked as outdated.

@mcfnord
mcfnord marked this pull request as ready for review September 4, 2026 17:49
Comment thread src/README.md

# Main Jamulus codebase

Licensed under the AGPL 3.0 or any later version; full text in [../COPYING](../COPYING).

@ann0see ann0see Sep 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pljones do we really need to put the full license text - even as comment - into the file? It's better now though (since we have this short disclaimer). For code the header is fine, but for markdown files it just seems odd.

@ann0see ann0see left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need to verify the accuracy, but from the philosophy and style, I think it's good now. Basically serves as index which is good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI AI generated or potentially AI generated

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants