Three questions on running remote server

Dear users and developers,

While testing running a remote server, the following three questions came into mind:

  1. s.options.maxLogins = 4; on remote servers is necessary to allow clients to connect to the machine. However, why are there no differences

    • between
      s.options.maxLogins = 4;
      and
      s.options.maxLogins = 3;

    • between
      s.options.maxLogins = 5;
      and
      s.options.maxLogins = 8;

    Should the number be one of Nth power of 2, i.e. 2, 4, 8, 16 and 32?

    // on remote servers
    
    (
    s.options.bindAddress = "0.0.0.0"; 
    s.options.maxLogins = 4;  // <- question 1
    s.boot;
    )
    
  2. Why do we need serverOptions.maxLogins = 2; in the code below on the client machines? It does not affect the s.options.maxLogins on the connected server. I know that sclang returns an error if serverOptions (i.e.: ServerOption().maxLogins = 2) is missed. However, what does the number indeed? There seems to be no difference if I write 1, 4 or 10 instead 2.

  3. The third question would be very foolish: Is it possible to assign, by evaluating a function, an interpreter variable from an argument? To use the connected remote server globally, I assigned the interpreter variables and in the following code block as follows: l = ~connectRemote.("10.211.55.13", "lin") I had tried to assign with the code ~connectRemote.("10.211.55.13", "l") , but could not achieve the goal even though having had variously combined .interpret, compile and this.interpret(...). Would there be a sophisticated way to do it? I think the code below could be handy if there is a way to do it.

I really appreciate any help you can provide.

Best regards,

// on the client
(
~connectRemote = { |ip, serverName|
	var serverOptions, condition, runResponder;
	serverOptions = ServerOption();
	serverOptions.maxLogins = 2; // <- question 2
	serverName = Server.remote(serverName.asSymbol, NetAddr(ip, 57110), serverOptions);  // <-question 3
	fork{
		condition = Condition{ serverName.serverRunning };
		runResponder = SimpleController(serverName)
		.put(\serverRunning, { condition.signal });
		condition.wait;
		runResponder.remove;
		defer{
			serverName.makeWindow;
			serverName.meter };
		CmdPeriod.add{
			serverName.freeAll
		}
	};
	serverName  // <-question 3
}
)

l = ~connectRemote.("10.211.55.13", "lin") // <-question 3
l.class

w = ~connectRemote.("10.211.55.3", "win") // <-question 3
w.class

Server.all
Server.named

(
	[l, w].do{|item|
		SynthDef(\sineTest, { |out, freq| Out.ar(out, SinOsc.ar(freq, 0, 0.1)) }).send(item);
}
)

x = Synth(\sineTest, [out: 0, freq: 440 ], target: l)

y = Synth(\sineTest, [out: 1, freq: 550 ], target: w)

y.free

x.free

l.sendMsg("s_new", "sineTest", ~l = l.nextNodeID, 0, 1, "out", 0, "freq", 550);

l.sendMsg("/n_free", ~l);

w.sendMsg("s_new", "sineTest", ~w = l.nextNodeID, 0, 1, "out", 1, "freq", 1100);

w.sendMsg("/n_free", ~w);

This is quite obscure – buried in scsynth source code, there is:

            options.mMaxLogins = NEXTPOWEROFTWO(atoi(argv[j + 1]));

Next power of two…

To be honest, I don’t understand why this is being done. My gut feeling is that this may not be necessary, but there could also be a reason…?

Anyway, to answer your question directly – there is no requirement for you to set maxLogins to a power of two – but if you set it to something other than a power of two, the server will change it for you. This is admittedly a bit confusing, in that you might have expected, for instance, available buses to be divided into three groups instead of four, but they will be divided into four whether that’s what you wanted or not.

You should not need this.

To demonstrate, I booted a server in the terminal (outside of sclang):

scsynth -u 57109 -a 1024 -i 2 -o 2 -m 524288 -R 0 -C 1 -l 2

Note -l 2 == 2 logins.

Then, in sclang:

o = ServerOptions.new;  // all defaults
r = Server.remote(\test, NetAddr("127.0.0.1", 57109), o);

// after boot
r.maxNumClients  // 2

I did not set maxLogins, but the server picked up the 2 from the remote server.

Usually this is a bad idea. Here, you could simply write l = ~connectRemote.(...) instead of ~connectRemote.(..., \l) – there’s really no benefit to the latter.

You can get and set interpreter variables by thisProcess.interpreter.r (and use thisProcess.interpreter.perform('r') or thisProcess.interpreter.perform('r_', newValue) if you must) – but I think in this case, it’s easier just to return the thing and let the caller assign it.

hjh

1 Like

Thank you for your kind answer! The following code is what I originally intended for:

(
~asServer = { |ip, serverName, variable|
	var serverOptions, condition, runResponder, selector;
	serverOptions = ServerOptions();
	serverName = Server.remote(
		serverName.asSymbol,
		NetAddr(ip, 57110),
		serverOptions);
	selector = variable++$_;
	fork{
		condition = Condition{ serverName.serverRunning };
		runResponder = SimpleController(serverName)
		.put(\serverRunning, { condition.signal });
		condition.wait;
		runResponder.remove;
		defer{
			serverName.makeWindow;
			serverName.meter };
		~test = { SinOsc.ar(
			(Scale.major[~servers.size - 1] + 60).midicps
		)!2 }.play(serverName);
		CmdPeriod.add{
			serverName.freeAll
		};
	};
	thisProcess.interpreter.perform(selector.asSymbol, serverName);
	~servers = ~servers.add(thisProcess.interpreter.perform(variable));
	serverName
}
)

~servers = []

~asServer.("10.211.55.13", \remoteServer1, \a)
~test.free

~asServer.("10.211.55.3", \remoteServer2, \b)
~test.free

~asServer.("localhost", \localhost, \s)
~test.free


~servers

However, letting a function make an interpreter variable is not so intuitive.

I think the following is more practical:

(
~asServer_ = { |ip, serverName, serverGroup|
	var serverOptions, condition, runResponder, selector;
	serverOptions = ServerOptions();
	serverName = Server.remote(
		serverName.asSymbol,
		NetAddr(ip, 57110),
		serverOptions);
	fork{
		condition = Condition{ serverName.serverRunning };
		runResponder = SimpleController(serverName)
		.put(\serverRunning, { condition.signal });
		condition.wait;
		runResponder.remove;
		defer{
			serverName.makeWindow;
			serverName.meter };
		~test = { SinOsc.ar(
			(Scale.major[serverGroup.interpret.size - 1] + 60).midicps
		)!2 }.play(serverName);
		CmdPeriod.add{
			serverName.freeAll
		};
	};
	(serverGroup + "=" + serverGroup ++".add('" ++ serverName ++"')").interpret;
	serverName
}
)

~parallels = []
~real = []

a = ~asServer_.("10.211.55.13", \remoteServer1, "~parallels")

b = ~asServer_.("10.211.55.3", \remoteServer2, "~parallels")

c = ~asServer_.("127.0.0.1", \localServer, "~real")

~parallels
~real