diff --git a/.github/workflows/c-cpp-Linux.yml b/.github/workflows/c-cpp-Linux.yml index c906317..670d57e 100644 --- a/.github/workflows/c-cpp-Linux.yml +++ b/.github/workflows/c-cpp-Linux.yml @@ -30,6 +30,8 @@ jobs: run: make -f Makefile.Linux MAILPROXY=true FTP=true - name: regression tests run: python3 tests/run.py + - name: regression tests, a thread for each client + run: python3 tests/run.py --loopmode off - name: mkdir run: mkdir ~/3proxy - name: make install diff --git a/man/3proxy.cfg.5 b/man/3proxy.cfg.5 index d1ffb0d..98572d1 100644 --- a/man/3proxy.cfg.5 +++ b/man/3proxy.cfg.5 @@ -254,9 +254,10 @@ authorization and to drop the per-destination log records only. (for udppm) singlepacket, expect only one packet from both client and server .br .B -L - (for TCP services) serve the clients on event loops (see \fBloopmode\fR): a -few threads each serve many clients, instead of a thread for each. -\fB-L0\fR gives each client a thread of its own whatever \fBloopmode\fR says. + (for TCP services) how the clients are served, whatever \fBloopmode\fR says +(see there): \fB-L2\fR on event loops, which also relay their data (the +default), \fB-L\fR on event loops relaying the data as a thread would, +\fB-L0\fR a thread for each client. .br .B -u Never ask for username/password @@ -1526,16 +1527,21 @@ many connections were given to a waiting one on its Running Services page. .br .BR loopmode -\fIon\fR|\fIoff\fR +\fIcb\fR|\fIon\fR|\fIoff\fR .br Whether the clients of the TCP services which follow (until the next \fBloopmode\fR) are served on event loops: a few threads (see \fBloops\fR) each wait for the sockets and timers of many clients and switch between them, each client running on a stack of its own as large as its thread's would be. A client costs a stack and no thread, and waking it costs no -switch of threads, so many connections take less memory and CPU. Off by -default; the \fB-L\fR option of a service overrides it, \fB-L0\fR for a -thread for each client. +switch of threads, so many connections take less memory and CPU. +\fIcb\fR, the default, also has the loop relay a connection's data itself +once the request is handled, where nothing but the sockets is between the +sides (no filters, no limits but that of a body's length), without +switching to the client's stack for each read. \fIon\fR relays the data +on the client's stack, as a thread of its own would, and \fIoff\fR gives +each client a thread. The \fB-L\fR option of a service overrides it: \fB-L2\fR, +\fB-L\fR, \fB-L0\fR. .br Clients keep a thread each, whatever is asked, where something they use would hold up the others sharing their thread: a service with FilePlugin, diff --git a/src/conf.c b/src/conf.c index d7086a8..ecb3854 100644 --- a/src/conf.c +++ b/src/conf.c @@ -429,7 +429,7 @@ static int h_loops(int argc, unsigned char **argv){ static int h_loopmode(int argc, unsigned char **argv){ if(!strcmp((char *)argv[1], "on")) conf->loopmode = 1; else if(!strcmp((char *)argv[1], "off")) conf->loopmode = 0; - /* and their data relayed by the loop (a draft) */ + /* and their data relayed by the loop: the default */ else if(!strcmp((char *)argv[1], "cb")) conf->loopmode = 2; else return 1; return 0; diff --git a/src/confinit.h b/src/confinit.h index de1c91e..af05924 100644 --- a/src/confinit.h +++ b/src/confinit.h @@ -22,6 +22,8 @@ .trafcounter = NULL, .services = NULL, .stacksize = 0, + /* clients served on event loops, their data relayed by the loops */ + .loopmode = 2, .poolmax = DEFAULT_POOLMAX, .pooltimeout = DEFAULT_POOLTIMEOUT, .counterd = -1, diff --git a/src/mainconf.c b/src/mainconf.c index 0dad946..c2d3732 100644 --- a/src/mainconf.c +++ b/src/mainconf.c @@ -480,7 +480,7 @@ static int srvprepare(struct srvstart *s, int argc, char **argv){ srv->stacksize = atoi(argv[i]+2); break; case 'L': - /* -L2: and their data relayed by the loop (a draft) */ + /* -L2: and their data relayed by the loop (the default) */ srv->loopmode = argv[i][2]? atoi(argv[i]+2) : 1; break; case 'a': diff --git a/src/mainloop.c b/src/mainloop.c index a539e8e..dcc76d3 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -7,9 +7,10 @@ */ /* Clients served on event loops: a few threads, a CPU each by default - (loops), each running a loop, and each client of a service given -L (or - loopmode on) served by a fiber of one of them, with the same code as a - client with a thread of its own: its waits in sockpoll() wait in the loop. + (loops), each running a loop, and each client of a service (unless given + -L0 or loopmode off) served by a fiber of one of them, with the same code + as a client with a thread of its own: its waits in sockpoll() wait in the + loop. A service whose clients cannot share a thread (threadperclient), one listening for connections back (-R), all of its clients waiting for the same socket, which a loop waits for once, or a build or processor without @@ -426,13 +427,13 @@ DONE: } -/* ---- sockmap() as callbacks (-L2, a draft) ------------------------------- */ +/* ---- sockmap() as callbacks (-L2, loopmode cb) --------------------------- */ /* The data of a connection mapped by a fiber of a loop with neither filters, limits but that of a body's length, nor anything but the socket functions of the system between the sides, is relayed by the loop itself: the sockets call it back, on the loop's own stack, and the fiber, parked meanwhile, is resumed only once - the mapping is over. No splice() yet: data is copied. */ + the mapping is over. */ /* rounds of a read and a write each way at most in a callback, before what is still ready waits for the next turn of the loop */ diff --git a/src/structures.h b/src/structures.h index df20696..b146e0a 100644 --- a/src/structures.h +++ b/src/structures.h @@ -1009,7 +1009,8 @@ struct extparam { int filtermaxsize; int poolmax, pooltimeout; /* threadpool: idle workers kept, -1 automatic */ int loops; /* threads serving clients on event loops, 0: a CPU each */ - int loopmode; /* services from here on serve clients on loops */ + int loopmode; /* services from here on serve clients on loops: 1, and + relay their data there: 2 */ int gracetraf, gracenum, gracedelay; int maxseg; unsigned char **archiver; diff --git a/tests/run.py b/tests/run.py index 6e7db07..152e762 100644 --- a/tests/run.py +++ b/tests/run.py @@ -58,11 +58,14 @@ def main(): help="keep the temporary directory") parser.add_argument("-v", "--verbose", action="store_true", help="print every check, not just the failures") - parser.add_argument("--loopmode", action="store_true", - help="serve the clients of every service on event loops") + parser.add_argument("--loopmode", choices=("cb", "on", "off"), + help="serve the clients of every service so (cb, the " + "default, and on: on two event loops; off: a thread " + "each)") args = parser.parse_args() if args.loopmode: - os.environ["THREEPROXY_TESTCONFIG"] = "loops 2\nloopmode on\n" + os.environ["THREEPROXY_TESTCONFIG"] = \ + "loops 2\nloopmode %s\n" % args.loopmode binary = args.binary or os.environ.get("BIN") or default_binary() binary = os.path.abspath(binary)