Skip to content

Comments

UDP: set max buffer size to 4MB and fallback to 1MB#6989

Merged
elgiano merged 4 commits intosupercollider:3.14from
xunil-cloud:topic/fix-message-too-long-error
Jun 21, 2025
Merged

UDP: set max buffer size to 4MB and fallback to 1MB#6989
elgiano merged 4 commits intosupercollider:3.14from
xunil-cloud:topic/fix-message-too-long-error

Conversation

@xunil-cloud
Copy link
Contributor

Purpose and Motivation

Fix #6969. Set UDP buffer size to 4 MB and fallback to 1MB if we fail to do that.

Types of changes

  • Bug fix

To-do list

  • Code is tested
  • All tests are passing
  • Updated documentation
  • This PR is ready for review

We fallback to 1MB if we fail to do that.
Copy link
Member

@telephon telephon left a comment

Choose a reason for hiding this comment

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

Thank you!

I think it would be good if sclang would inform us always about the current maximum OSC buffer size, and even keep a variable for this that can be accessed from within sclang.

There are places in the class library where we depend on the size limit, so these need to adjust themselves.

This could be a separate PR, of course.

@elgiano
Copy link
Contributor

elgiano commented Jun 16, 2025

@telephon I found these four matches searching for 65535, do you think there are more?
In all these cases, SC is ensuring that a single message is not larger than the UDP limit for a single message, which is not dependent on the max buffer size (unless buffer size < max message size). That is, even if we have a 4MB udp buffer, we can't send a single message larger than 65535 bytes. (about why are we dividing 65535 by four, I think it's a conservative measure that could be the subject of a separate investigation :D)

if (bytes.size < (65535 div: 4)) {
server.sendMsg("/d_recv", bytes, completionMsg)
} {
if (server.isLocal) {
if(warnAboutLargeSynthDefs) {
"SynthDef % too big for sending. Retrying via synthdef file".format(name).warn;
};
this.writeDefFile(synthDefDir);

if(args.bundleSize > 65535) {// udp max size.
args.clumpBundles.do { |item|

if(bundles.bundleSize > 20000/*65515*/) { // 65515 = 65535 - 16 - 4 (sync msg size)
bundles.clumpBundles.do { |item|

if(server.options.protocol === \tcp or: { size < 16383}) {
// full size: 65535, but does not work.
bundle.addPrepare([5, bytes]); // "/d_recv"
} {
// bridge exceeding bundle size by writing to disk
if(server.isLocal.not) {
Error("SynthDef too large (" ++ size
++ " bytes) to be sent to remote server via udp").throw;

About being able to query the maxBufferSize (and perhaps even setting it?) I agree it would be nice to have a couple of primitives (something like _NetAddr_getMaxBufferSize, _NetAddr_setMaxBufferSize), especially because now different systems could have different buffer sizes and being able to query it would help debugging eventual future issues.

static constexpr int receiveBufferSize = 8 * 1024 * 1024;
static constexpr int sendBufferSize = 8 * 1024 * 1024;
static constexpr int receiveBufferSize = 4 * 1024 * 1024;
static constexpr int sendBufferSize = 4 * 1024 * 1024;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should add static constexpr int fallbackBufferSize = 1 * 1024 * 1024, and perhaps move all these constexpr to common/SC_OscUtils.hpp?

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, we could make a function setUDPBufferSizes in there, and call it from sclang/scsynth/supernova. But we could also do this in a subsequent refactoring PR

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should add static constexpr int fallbackBufferSize = 1 * 1024 * 1024

Most definitely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, we could make a function setUDPBufferSizes in there, and call it from sclang/scsynth/supernova.

I'm thinking about this, too. If that is what we want, I plan to move them all to SC_OscUtils.hpp.

Copy link
Contributor

@elgiano elgiano Jun 17, 2025

Choose a reason for hiding this comment

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

I've seen that if we want to do this, there is a caveat to import SC_OscUtils.hpp in supernova

#define scprintf printf
#include "SC_OscUtils.hpp"
#undef scprintf

which is the same thing we do in lang/LangPrimSource/SC_ComPort.cpp.

But again, I don't think it's absolutely necessary to do it now, we could also do it after release in a subsequent PR, as you wish

@Spacechild1
Copy link
Contributor

I think it would be good if sclang would inform us always about the current maximum OSC buffer size, and even keep a variable for this that can be accessed from within sclang.

As @elgiano correctly pointed out, these size checks are only done to make sure we don't exceed the max. UDP packet size. The UDP socket buffers should always be large enough to hold at least a single UDP packet.

@Spacechild1
Copy link
Contributor

Spacechild1 commented Jun 16, 2025

(about why are we dividing 65535 by four, I think it's a conservative measure that could be the subject of a separate investigation :D)

Yes, that's what I think as well. It seems pretty arbitrary to me, though.

Note that 65535 is only the max. value allowed in the size field of the UDP packet header (because it is a 16-bit unsigned integer.) For the actual max. data payload, you must subtract the UDP header size (8 bytes) and IP header size (20 bytes for IPv4). For IPv4 this is 65507 bytes. See https://en.wikipedia.org/wiki/User_Datagram_Protocol#UDP_datagram_structure.

But that does not mean that you can actually send and receive such large UDP packets, in particular when an actual network is involved (as opposed to loopback devices). The ethernet standard only guarantees an MTU of 1500 bytes. Most modern switches support jumbo frames (https://en.wikipedia.org/wiki/Jumbo_frame), but they also have a limit well below 65507. Once you send things over the internet, you might want to limit your UDP packets to something like 576 (!) bytes.

IMO, the actual solution to all of this is to use a reliable protocol, like TCP, but that's another story.

@telephon
Copy link
Member

@telephon I found these four matches searching for 65535, do you think there are more?

No, methink that's it. Thank you for the explanation, sure!

@telephon
Copy link
Member

about why are we dividing 65535 by four, I think it's a conservative measure that could be the subject of a separate investigation :D)

iirc, i did experiments and found that value :D. Might be wrong by now!

Copy link
Contributor

@elgiano elgiano left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks! We can follow up later with a refactoring to reduce code duplication!

@dyfer dyfer added comp: sclang sclang C++ implementation (primitives, etc.). for changes to class lib use "comp: class library" comp: scsynth comp: supernova squash on merge labels Jun 18, 2025
@dyfer
Copy link
Member

dyfer commented Jun 18, 2025

@JordanHendersonMusic / @Spacechild1 is this good to be merged?

@dyfer dyfer added this to 3.14.0 Jun 19, 2025
@dyfer dyfer moved this to Committed in 3.14.0 Jun 19, 2025
@Spacechild1
Copy link
Contributor

Good from my side!

@elgiano elgiano changed the title Fix _NetAddr_SendMsg "Message too long" errors UDP: set max buffer size to 4MB and fallback to 1MB Jun 21, 2025
@elgiano elgiano merged commit 4790b95 into supercollider:3.14 Jun 21, 2025
23 of 24 checks passed
@github-project-automation github-project-automation bot moved this from Committed to Done in 3.14.0 Jun 21, 2025
@JordanHendersonMusic
Copy link
Contributor

Does this fix #6969?

@elgiano
Copy link
Contributor

elgiano commented Jun 21, 2025 via email

@ahihi
Copy link

ahihi commented Jun 21, 2025

Probably, but it would be nice if @ahihi could test it once we release rc2, since none of us could reproduce the error

i will! in fact i would be happy to try it already, but i dont currently have a SC build environment set up.

@dyfer
Copy link
Member

dyfer commented Jun 21, 2025

@ahihi you can find builds from Github Actions under "Checks":

Screenshot 2025-06-21 at 10 29 42 AM

On macOS you need to right-click -> open (two times) to be able to run an unsigned build.

@ahihi
Copy link

ahihi commented Jun 23, 2025

@ahihi you can find builds from Github Actions under "Checks":

thanks! it is working fine here :)

dyfer pushed a commit to dyfer/supercollider that referenced this pull request Jul 16, 2025
Failing to set the previous 8MB buffer size could leave some systems with a too small default, causing failures when sending bigger synthdefs, with _NetAddr_SendMsg error "Message too long". 4MB is still more than the default in 3.13, while being acceptable for most systems. If it's still too much, fallback to 1MB if system's default is smaller.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: sclang sclang C++ implementation (primitives, etc.). for changes to class lib use "comp: class library" comp: scsynth comp: supernova squash on merge

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

_NetAddr_SendMsg "Message too long" errors in 3.14.0-rc1 on SuperDirt startup

7 participants