digitalmars.D.learn - NIO+Multithreaded TCPSocket listener, very low cpu utilisation
- ade90036 (121/121) Nov 14 2017 Hi Forum,
- kdevel (22/34) Nov 14 2017 Some cosmetic changes: It is possible, that your HTTP client gets
- kdevel (3/7) Nov 14 2017 Why do you ever timeout? This loop consumes 100 % (a single core)
- ade90036 (9/16) Nov 15 2017 Thanks for you feedback, i'll make those cosmetic changes to the
- ade90036 (91/91) Nov 15 2017 So thanks for the suggestions, i have fixed HTTP response not
- Daniel Kozak (96/96) Nov 15 2017 Do not use your own taskPool, just use global taskPool proerty (import
- Daniel Kozak (6/102) Nov 15 2017 This one works ok for me, but I am on linux:
- kdevel (21/23) Nov 15 2017 It works, but it does not handle two connects in parallel. STR:
- ade90036 (21/45) Nov 16 2017 Hi Guys,
- Daniel Kozak (7/27) Nov 16 2017 Hmm works ok for me. What OS?
- ade90036 (2/6) Nov 16 2017 I'm running MacOS..
- Daniel Kozak (5/39) Nov 16 2017 It works for me because I have multiple threads, but when I use only one
- ade90036 (6/11) Nov 16 2017 Ok, let me force the: "defaultPoolThreads(8)" and let me re-test
- ade90036 (20/20) Nov 16 2017 Result:
- ade90036 (14/16) Nov 16 2017 These are the tests for a similar program in java.
- ade90036 (2/2) Nov 16 2017 So, what is next?
- Daniel Kozak (3/5) Nov 16 2017 can you post both code, java and d, for sure we are all testing the same...
- kdevel (17/18) Nov 17 2017 You may compile the code with dmd -g -O -profile -profile=gc
- kdevel (22/32) Nov 17 2017 This works:
- Daniel Kozak (12/152) Nov 15 2017 And this one
- kdevel (45/47) Nov 15 2017 I thrash your code with two shell processes
Hi Forum, Let's cut the chase, i'm a newby in Dlang. I have 15+ years experience in java and 7+ years experience in C++. I found D very fascinating and the sugar coated syntax very appealing to my style of coding. (groovy like) I've been trying to learn Dland and bring it thought the motions by creating a very simple and basic TCPSocket listerner that when you send a request it responds with an HTTP response over a specific port. (localhost:4445) I have the code working on a worked thread and when the socket accept() it defers the processing of the request (socket) in a different thread backed by TaskPool(8). i have 8 logical core, which is a macBook pro retina 16gb, i7. What i'm expecting to see is the CPU of my 8 core I7 go through the roof and nearly melt (hope not) but at-least have the fan on at sustainable level and obtain full CPU utilisation. What i'm benchmarking it against is a JAVA NIO2 implementation. This implementation achieves very high CPU utilisation and high throughput. The process utilisation averages 400% at times and the fan is really searching for cold air. (Niceeee) However, when i run the Dlang program i see i very poor CPU utilisation. The fan is always in silent mode. Not sure if you are familiar with MacOS cpu metrics, but they are based per core. So dland program reports 100% under the process monitor (which equates to one core) and the overall system CPU utilisation is 13%. I would have expected to see a much higher cpu utilisation but it is not happening. I have been trying different variation of the same implementation but not luck. I'm starting to suspect that this is an BUG related to the macOS but i would like to confirm or atleast have a second pair of eyes having a look. ```Code import std.algorithm : remove; import std.conv : to; import core.thread: Thread; import std.socket : InternetAddress, Socket, SocketException, SocketSet, TcpSocket, SocketShutdown; import core.time : Duration, dur; import std.stdio : writeln, writefln; import std.parallelism : task, TaskPool; void main(string[] args) { ushort port; if (args.length >= 2) port = to!ushort(args[1]); else port = 4447; auto listener = new TcpSocket(); assert(listener.isAlive); listener.blocking = false; listener.bind(new InternetAddress(port)); listener.listen(100); writefln("Listening on port %d.", port); auto taskPool = new TaskPool(8); new Thread({ auto listeningSet = new SocketSet(); while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null, dur!"nsecs"(150)) > 0) { if (listeningSet.isSet(listener)) // connection request { Socket socket = null; scope (failure) { writefln("Error accepting"); if (socket) socket.close(); } socket = listener.accept(); assert(socket.isAlive); assert(listener.isAlive); //writefln("Connection from %s established.", socket.remoteAddress().toString()); auto task = task!handle_socket(socket); taskPool.put(task); } } listeningSet.reset(); } }).start(); } void handle_socket(Socket socket) { auto socketSet = new SocketSet(); while(true) { socketSet.add(socket); if (Socket.select(socketSet, null, null, dur!"nsecs"(150)) > 0) { char[1024] buf; auto datLength = socket.receive(buf[]); if (datLength == Socket.ERROR) writeln("Connection error."); else if (datLength != 0) { //writefln("Received %d bytes from %s: \"%s\"", datLength, socket.remoteAddress().toString(), buf[0..datLength]); //writefln("Writing response"); socket.send("HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/plain; charset=UTF-8 Content-Length: 32 <html><head></head><body>Hello World!</body></html>"); } // release socket resources now socket.shutdown(SocketShutdown.BOTH); socket.close(); break; } socketSet.reset(); } } ``` You help in understanding this matter is extremelly helpfull. Regards
Nov 14 2017
On Tuesday, 14 November 2017 at 19:57:54 UTC, ade90036 wrote:socket.send("HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/plain; charset=UTF-8 Content-Length: 32 <html><head></head><body>Hello World!</body></html>"); }Some cosmetic changes: It is possible, that your HTTP client gets confused by the data sent? ``` socket.send("HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 51 <html><head></head><body>Hello World!</body></html>".to_retlf); string to_retlf (string s) { import std.algorithm; import std.string; return s .lineSplitter .map!(a => chomp (a)) .join ("\r\n"); } ``` The Content-Length given is too short. The Content-Type also was wrong.
Nov 14 2017
On Tuesday, 14 November 2017 at 19:57:54 UTC, ade90036 wrote:while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null, dur!"nsecs"(150)) > 0) {Why do you ever timeout? This loop consumes 100 % (a single core) when idle on my machine.
Nov 14 2017
On Tuesday, 14 November 2017 at 21:09:40 UTC, kdevel wrote:On Tuesday, 14 November 2017 at 19:57:54 UTC, ade90036 wrote:Thanks for you feedback, i'll make those cosmetic changes to the HTTP response Why do i specify a timeout in the Socket.select? I have noticed that if i don't specify a timeout the Socket.select seems to block every so often for several seconds (2/5sec) and i have read in another post to specify a timeout value to the call. I'll retest now.while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null, dur!"nsecs"(150)) > 0) {Why do you ever timeout? This loop consumes 100 % (a single core) when idle on my machine.
Nov 15 2017
So thanks for the suggestions, i have fixed HTTP response not postman cal also parse the headers correctly!! happy days. I have removed the duration from the Socket.select but the application seems to process a bunch or requests and then it stalls for several seconds (3/5) and then it resumes. The httpclinet which i'm using to test the application is reporting: "connection timeout". Could this be caused by the GC? ```updated code import std.algorithm : remove; import std.conv : to; import core.thread: Thread; import std.socket : InternetAddress, Socket, SocketException, SocketSet, TcpSocket, SocketShutdown; import core.time : Duration, dur; import std.stdio : writeln, writefln; import std.parallelism : task, TaskPool; string to_retlf (string s) { import std.algorithm; import std.string; return s .lineSplitter .map!(a => chomp (a)) .join ("\r\n"); } void main(string[] args) { ushort port; if (args.length >= 2) port = to!ushort(args[1]); else port = 4444; auto listener = new TcpSocket(); assert(listener.isAlive); listener.blocking = false; listener.bind(new InternetAddress(port)); listener.listen(100); writefln("Listening on port %d.", port); auto taskPool = new TaskPool(8); string response = "HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 51 <html><head></head><body>Hello World!</body></html>".to_retlf; new Thread({ auto listeningSet = new SocketSet(); while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null)) { if (listeningSet.isSet(listener)) // connection request { Socket socket = listener.accept(); assert(socket.isAlive); //writefln("Connection from %s established.", socket.remoteAddress().toString()); auto task = task!handle_socket(socket, response); taskPool.put(task); } } listeningSet.reset(); } }).start(); } void handle_socket(Socket socket, string response) { auto socketSet = new SocketSet(); while(true) { socketSet.add(socket); if (Socket.select(socketSet, null, null)) { char[1024] buf; auto datLength = socket.receive(buf[]); if (datLength == Socket.ERROR) writeln("Connection error."); else if (datLength != 0) { //writefln("Received %d bytes from %s: \"%s\"", datLength, socket.remoteAddress().toString(), buf[0..datLength]); //writefln("Writing response"); socket.send(response); } // release socket resources now socket.close(); break; } socketSet.reset(); } ``` Regards
Nov 15 2017
Do not use your own taskPool, just use global taskPool proerty (import std.parallelism: taskPool). You should not set blocking to false. And dont use Thread here. There is no reason to do that. Just move that code into the main Dne 15. 11. 2017 12:15 odp. napsal u=C5=BEivatel "ade90036 via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com>: So thanks for the suggestions, i have fixed HTTP response not postman cal also parse the headers correctly!! happy days. I have removed the duration from the Socket.select but the application seems to process a bunch or requests and then it stalls for several seconds (3/5) and then it resumes. The httpclinet which i'm using to test the application is reporting: "connection timeout". Could this be caused by the GC? ```updated code import std.algorithm : remove; import std.conv : to; import core.thread: Thread; import std.socket : InternetAddress, Socket, SocketException, SocketSet, TcpSocket, SocketShutdown; import core.time : Duration, dur; import std.stdio : writeln, writefln; import std.parallelism : task, TaskPool; string to_retlf (string s) { import std.algorithm; import std.string; return s .lineSplitter .map!(a =3D> chomp (a)) .join ("\r\n"); } void main(string[] args) { ushort port; if (args.length >=3D 2) port =3D to!ushort(args[1]); else port =3D 4444; auto listener =3D new TcpSocket(); assert(listener.isAlive); listener.blocking =3D false; listener.bind(new InternetAddress(port)); listener.listen(100); writefln("Listening on port %d.", port); auto taskPool =3D new TaskPool(8); string response =3D "HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/html; charset=3DUTF-8 Content-Length: 51 <html><head></head><body>Hello World!</body></html>".to_retlf; new Thread({ auto listeningSet =3D new SocketSet(); while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null)) { if (listeningSet.isSet(listener)) // connection request { Socket socket =3D listener.accept(); assert(socket.isAlive); //writefln("Connection from %s established.", socket.remoteAddress().toString()); auto task =3D task!handle_socket(socket, response); taskPool.put(task); } } listeningSet.reset(); } }).start(); } void handle_socket(Socket socket, string response) { auto socketSet =3D new SocketSet(); while(true) { socketSet.add(socket); if (Socket.select(socketSet, null, null)) { char[1024] buf; auto datLength =3D socket.receive(buf[]); if (datLength =3D=3D Socket.ERROR) writeln("Connection error."); else if (datLength !=3D 0) { //writefln("Received %d bytes from %s: \"%s\"", datLength, socket.remoteAddress().toString(), buf[0..datLength]); //writefln("Writing response"); socket.send(response); } // release socket resources now socket.close(); break; } socketSet.reset(); } ``` Regards
Nov 15 2017
This one works ok for me, but I am on linux: https://dpaste.dzfl.pl/f54decee45bc On Wed, Nov 15, 2017 at 12:46 PM, Daniel Kozak <kozzi11 gmail.com> wrote:Do not use your own taskPool, just use global taskPool proerty (import std.parallelism: taskPool). You should not set blocking to false. And dont use Thread here. There is no reason to do that. Just move that code into the main Dne 15. 11. 2017 12:15 odp. napsal u=C5=BEivatel "ade90036 via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com>: So thanks for the suggestions, i have fixed HTTP response not postman ca=lalso parse the headers correctly!! happy days. I have removed the duration from the Socket.select but the application seems to process a bunch or requests and then it stalls for several secon=ds(3/5) and then it resumes. The httpclinet which i'm using to test the application is reporting: "connection timeout". Could this be caused by the GC? ```updated code import std.algorithm : remove; import std.conv : to; import core.thread: Thread; import std.socket : InternetAddress, Socket, SocketException, SocketSet, TcpSocket, SocketShutdown; import core.time : Duration, dur; import std.stdio : writeln, writefln; import std.parallelism : task, TaskPool; string to_retlf (string s) { import std.algorithm; import std.string; return s .lineSplitter .map!(a =3D> chomp (a)) .join ("\r\n"); } void main(string[] args) { ushort port; if (args.length >=3D 2) port =3D to!ushort(args[1]); else port =3D 4444; auto listener =3D new TcpSocket(); assert(listener.isAlive); listener.blocking =3D false; listener.bind(new InternetAddress(port)); listener.listen(100); writefln("Listening on port %d.", port); auto taskPool =3D new TaskPool(8); string response =3D "HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/html; charset=3DUTF-8 Content-Length: 51 <html><head></head><body>Hello World!</body></html>".to_retlf; new Thread({ auto listeningSet =3D new SocketSet(); while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null)) { if (listeningSet.isSet(listener)) // connection request { Socket socket =3D listener.accept(); assert(socket.isAlive); //writefln("Connection from %s established.", socket.remoteAddress().toString()); auto task =3D task!handle_socket(socket, response); taskPool.put(task); } } listeningSet.reset(); } }).start(); } void handle_socket(Socket socket, string response) { auto socketSet =3D new SocketSet(); while(true) { socketSet.add(socket); if (Socket.select(socketSet, null, null)) { char[1024] buf; auto datLength =3D socket.receive(buf[]); if (datLength =3D=3D Socket.ERROR) writeln("Connection error."); else if (datLength !=3D 0) { //writefln("Received %d bytes from %s: \"%s\"", datLength=,socket.remoteAddress().toString(), buf[0..datLength]); //writefln("Writing response"); socket.send(response); } // release socket resources now socket.close(); break; } socketSet.reset(); } ``` Regards
Nov 15 2017
On Wednesday, 15 November 2017 at 13:31:46 UTC, Daniel Kozak wrote:This one works ok for me, but I am on linux: https://dpaste.dzfl.pl/f54decee45bcIt works, but it does not handle two connects in parallel. STR: 1. start the binary in console 1 2. telnet localhost 4444 in console 2 3. telnet localhost 4444 in console 3 4. enter a [RETURN] in console (3) observed: nothing (the "thread" handling the first connect blocks) expected: response On my machine defaultPoolThreads() returns 1. This can easily be increased: ``` defaultPoolThreads(8); ``` There is also another problem with Socket.select: It may return -1. This frequently happens when the ACCEPT socket is non-blocking and the select is interrupted (by the GC?) then errno == EINTR. Also not having a timeout in the Socket.select of handle_socket allows for DOS attacks like the one above. In case of a timeout select also returns -1.
Nov 15 2017
On Wednesday, 15 November 2017 at 23:04:46 UTC, kdevel wrote:On Wednesday, 15 November 2017 at 13:31:46 UTC, Daniel Kozak wrote:Hi Guys, so i have tried the latest changes with destroy(socket) rather than socket.close(). Still the program is extremely slow. I see that the httpclinet is making successfully 100 request at a time and then it freezes for 5/10 seconds and then it process another 100 request. As Kdevel pointed out, this could be related to the Socket.select blocking on the interupt or it would be GC kicking in. I also have seen the value computed for the defaultThread is not correct, hence i manually started the TaskPool(int thread) manually. I'm going to share my benchmark program so you can guys test with the same tool. Unfortunately it seems that there is some underline issue which is blocking the program and preventing to process all the incoming requests in a performant fashion, or at-least utilising the full CPU cycles. reagrds ade90036This one works ok for me, but I am on linux: https://dpaste.dzfl.pl/f54decee45bcIt works, but it does not handle two connects in parallel. STR: 1. start the binary in console 1 2. telnet localhost 4444 in console 2 3. telnet localhost 4444 in console 3 4. enter a [RETURN] in console (3) observed: nothing (the "thread" handling the first connect blocks) expected: response On my machine defaultPoolThreads() returns 1. This can easily be increased: ``` defaultPoolThreads(8); ``` There is also another problem with Socket.select: It may return -1. This frequently happens when the ACCEPT socket is non-blocking and the select is interrupted (by the GC?) then errno == EINTR. Also not having a timeout in the Socket.select of handle_socket allows for DOS attacks like the one above. In case of a timeout select also returns -1.
Nov 16 2017
Hmm works ok for me. What OS? Dne 16. 11. 2017 12:05 dop. napsal u=C5=BEivatel "kdevel via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com>:On Wednesday, 15 November 2017 at 13:31:46 UTC, Daniel Kozak wrote:d:This one works ok for me, but I am on linux: https://dpaste.dzfl.pl/f54decee45bcIt works, but it does not handle two connects in parallel. STR: 1. start the binary in console 1 2. telnet localhost 4444 in console 2 3. telnet localhost 4444 in console 3 4. enter a [RETURN] in console (3) observed: nothing (the "thread" handling the first connect blocks) expected: response On my machine defaultPoolThreads() returns 1. This can easily be increase=``` defaultPoolThreads(8); ``` There is also another problem with Socket.select: It may return -1. This frequently happens when the ACCEPT socket is non-blocking and the select =isinterrupted (by the GC?) then errno =3D=3D EINTR. Also not having a timeout in the Socket.select of handle_socket allows fo=rDOS attacks like the one above. In case of a timeout select also returns =-1.
Nov 16 2017
On Thursday, 16 November 2017 at 18:20:36 UTC, Daniel Kozak wrote:Hmm works ok for me. What OS? Dne 16. 11. 2017 12:05 dop. napsal uživatel "kdevel via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com>:I'm running MacOS..[...]
Nov 16 2017
It works for me because I have multiple threads, but when I use only one thread per pool (defaultPoolThreads(1)), it obviosly blocks, which is correct behavior On Thu, Nov 16, 2017 at 7:20 PM, Daniel Kozak <kozzi11 gmail.com> wrote:Hmm works ok for me. What OS? Dne 16. 11. 2017 12:05 dop. napsal u=C5=BEivatel "kdevel via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com>: On Wednesday, 15 November 2017 at 13:31:46 UTC, Daniel Kozak wrote:isThis one works ok for me, but I am on linux: https://dpaste.dzfl.pl/f54decee45bcIt works, but it does not handle two connects in parallel. STR: 1. start the binary in console 1 2. telnet localhost 4444 in console 2 3. telnet localhost 4444 in console 3 4. enter a [RETURN] in console (3) observed: nothing (the "thread" handling the first connect blocks) expected: response On my machine defaultPoolThreads() returns 1. This can easily be increased: ``` defaultPoolThreads(8); ``` There is also another problem with Socket.select: It may return -1. This frequently happens when the ACCEPT socket is non-blocking and the select=interrupted (by the GC?) then errno =3D=3D EINTR. Also not having a timeout in the Socket.select of handle_socket allows for DOS attacks like the one above. In case of a timeout select also returns -1.
Nov 16 2017
On Thursday, 16 November 2017 at 18:44:11 UTC, Daniel Kozak wrote:It works for me because I have multiple threads, but when I use only one thread per pool (defaultPoolThreads(1)), it obviosly blocks, which is correct behaviorOk, let me force the: "defaultPoolThreads(8)" and let me re-test it. By the way, if the socket is blocking you can remove all the socketSet and the socket.select as they are not needed anymore. like this.. https://dpaste.dzfl.pl/ca09e4c54789
Nov 16 2017
Result: bombardier -c 200 -n 10000 http://localhost:4444 Bombarding http://localhost:4444 with 10000 requests using 200 connections 10000 / 10000 [======================================================================================================== ==================] 100.00% 1m24s Done! Statistics Avg Stdev Max Reqs/sec 122.27 909.33 20363 Latency 49.98ms 192.16ms 1.07s HTTP codes: 1xx - 0, 2xx - 9999, 3xx - 0, 4xx - 0, 5xx - 0 others - 1 Errors: the server closed connection before returning the first response byte. Make sure the server returns 'Connection: close' response header before closing the connection - 1 Throughput: 36.89KB/s Mediocre result... let me create a java equivalent program so we have a direct comparison......
Nov 16 2017
Mediocre result... let me create a java equivalent program so we have a direct comparison......These are the tests for a similar program in java. bombardier -c 200 -n 10000 http://localhost:8081 Bombarding http://localhost:8081/ with 10000 requests using 200 connections 10000 / 10000 [=========================================================================================================== ==================] 100.00% 0s Done! Statistics Avg Stdev Max Reqs/sec 24527.29 14797.09 46439 Latency 8.25ms 15.52ms 235.79ms HTTP codes: 1xx - 0, 2xx - 10000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 3.87MB/s
Nov 16 2017
So, what is next? Can we enable some sort of profiling to see what is going on?
Nov 16 2017
can you post both code, java and d, for sure we are all testing the sames On Thu, Nov 16, 2017 at 8:37 PM, ade90036 via Digitalmars-d-learn < digitalmars-d-learn puremagic.com> wrote:So, what is next? Can we enable some sort of profiling to see what is going on?
Nov 16 2017
On Thursday, 16 November 2017 at 19:37:31 UTC, ade90036 wrote:Can we enable some sort of profiling to see what is going on?You may compile the code with dmd -g -O -profile -profile=gc I currently struggle getting meaningful output. I want to terminate the program after a number (say 400) handled requests. When returning from main the program hangs, so I call exit. The running binary now produces a trace.log but even after wrapping new SocketSet(); into ``` auto socket_set_allocation_wrapper () { writeln (__FUNCTION__); return new SocketSet(); } ``` I see the invocations in handle_socket in the console but not in trace.log.
Nov 17 2017
On Friday, 17 November 2017 at 14:28:38 UTC, kdevel wrote:On Thursday, 16 November 2017 at 19:37:31 UTC, ade90036 wrote:This works: ``` if (++accept_cnt > 400) { writeln ("returning"); taskPool.stop; return; } ```Can we enable some sort of profiling to see what is going on?You may compile the code with dmd -g -O -profile -profile=gc I currently struggle getting meaningful output. I want to terminate the program after a number (say 400) handled requests. When returning from main the program hangs, so I call exit.I see the invocations [of the socket allocation wrapper] in handle_socket in the console but not in trace.log.Now they are in trace.log. Also I see this: --- Num Tree Func Per Calls Time Time Call [...] 399 14948526 11824366 29635 void httpdtasks.handle_socket(std.socket.Socket) --- 30 milliseconds for handle_socket while the client side (Firefox) reports <= 1 ms. What does this mean? There is another point that bothers me: In each handle_socket invocation a new SocketSet is created. How can this allocation be done once per thread (thread local storage)?
Nov 17 2017
And this one https://paste.ofcode.org/KNqxcrmACLZLseB45MvwC Here you can test if threads makes difference when compile with: dmd -O -release -version=3DSINGLE_THREAD xxx.d it will use only one thread when compile with: dmd -O -release xxx.d it will use thread pool On Wed, Nov 15, 2017 at 2:31 PM, Daniel Kozak <kozzi11 gmail.com> wrote:This one works ok for me, but I am on linux: https://dpaste.dzfl.pl/f54decee45bc On Wed, Nov 15, 2017 at 12:46 PM, Daniel Kozak <kozzi11 gmail.com> wrote:sDo not use your own taskPool, just use global taskPool proerty (import std.parallelism: taskPool). You should not set blocking to false. And dont use Thread here. There i=ndsno reason to do that. Just move that code into the main Dne 15. 11. 2017 12:15 odp. napsal u=C5=BEivatel "ade90036 via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com>: So thanks for the suggestions, i have fixed HTTP response not postman cal also parse the headers correctly!! happy days. I have removed the duration from the Socket.select but the application seems to process a bunch or requests and then it stalls for several seco=(3/5) and then it resumes. The httpclinet which i'm using to test the application is reporting: "connection timeout". Could this be caused by the GC? ```updated code import std.algorithm : remove; import std.conv : to; import core.thread: Thread; import std.socket : InternetAddress, Socket, SocketException, SocketSet, TcpSocket, SocketShutdown; import core.time : Duration, dur; import std.stdio : writeln, writefln; import std.parallelism : task, TaskPool; string to_retlf (string s) { import std.algorithm; import std.string; return s .lineSplitter .map!(a =3D> chomp (a)) .join ("\r\n"); } void main(string[] args) { ushort port; if (args.length >=3D 2) port =3D to!ushort(args[1]); else port =3D 4444; auto listener =3D new TcpSocket(); assert(listener.isAlive); listener.blocking =3D false; listener.bind(new InternetAddress(port)); listener.listen(100); writefln("Listening on port %d.", port); auto taskPool =3D new TaskPool(8); string response =3D "HTTP/1.1 200 OK Server: dland:v2.076.1 Date: Tue, 11 Nov 2017 15:56:02 GMT Content-Type: text/html; charset=3DUTF-8 Content-Length: 51 <html><head></head><body>Hello World!</body></html>".to_retlf; new Thread({ auto listeningSet =3D new SocketSet(); while(true) { listeningSet.add(listener); if (Socket.select(listeningSet, null, null)) { if (listeningSet.isSet(listener)) // connection request { Socket socket =3D listener.accept(); assert(socket.isAlive); //writefln("Connection from %s established.", socket.remoteAddress().toString()); auto task =3D task!handle_socket(socket, response); taskPool.put(task); } } listeningSet.reset(); } }).start(); } void handle_socket(Socket socket, string response) { auto socketSet =3D new SocketSet(); while(true) { socketSet.add(socket); if (Socket.select(socketSet, null, null)) { char[1024] buf; auto datLength =3D socket.receive(buf[]); if (datLength =3D=3D Socket.ERROR) writeln("Connection error."); else if (datLength !=3D 0) { //writefln("Received %d bytes from %s: \"%s\"", datLength, socket.remoteAddress().toString(), buf[0..datLength]); //writefln("Writing response"); socket.send(response); } // release socket resources now socket.close(); break; } socketSet.reset(); } ``` Regards
Nov 15 2017
On Wednesday, 15 November 2017 at 14:22:51 UTC, Daniel Kozak wrote:And this one https://paste.ofcode.org/KNqxcrmACLZLseB45MvwCI thrash your code with two shell processes ``` while true; do curl 127.0.0.1:4444 -o /dev/null; done ``` running parallel. Using strace -fFeclose on the binary this happens frequently: ``` : [pid 10926] close(5) = 0 [pid 10926] close(4) = 0 [pid 10926] close(4) = 0 [pid 10926] close(4) = 0 [pid 10926] close(4) = 0 [pid 10926] --- SIGUSR1 (User defined signal 1) 0 (0) --- [pid 10926] --- SIGUSR2 (User defined signal 2) 0 (0) --- [pid 10919] close(4294967295) = -1 EBADF (Bad file descriptor) [pid 10919] close(4294967295) = -1 EBADF (Bad file descriptor) [pid 10919] close(4294967295) = -1 EBADF (Bad file descriptor) : [pid 10919] close(4294967295) = -1 EBADF (Bad file descriptor) [pid 10919] close(4294967295) = -1 EBADF (Bad file descriptor) [pid 10919] close(4294967295) = -1 EBADF (Bad file descriptor) [pid 10926] close(4) = 0 [pid 10926] close(4) = 0 [pid 10926] close(4) = 0 : ``` The destructor of Socket is trying to close the already closed socket? If the worker I change ``` socket.close(); ``` to ``` destroy (socket); ``` these close(-1) calls go away.
Nov 15 2017