www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - NIO+Multithreaded TCPSocket listener, very low cpu utilisation

reply ade90036 <andrea.rizzini gmail.com> writes:
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
next sibling parent kdevel <kdevel vogtner.de> writes:
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
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
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
parent reply ade90036 <andrea.rizzini gmail.com> writes:
On Tuesday, 14 November 2017 at 21:09:40 UTC, kdevel wrote:
 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.
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.
Nov 15 2017
parent reply ade90036 <andrea.rizzini gmail.com> writes:
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
next sibling parent Daniel Kozak <kozzi11 gmail.com> writes:
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
prev sibling next sibling parent reply Daniel Kozak <kozzi11 gmail.com> writes:
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=
l
 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 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
parent reply kdevel <kdevel vogtner.de> writes:
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/f54decee45bc
It 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
next sibling parent ade90036 <andrea.rizzini gmail.com> writes:
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:
 This one works ok for me, but I am on linux: 
 https://dpaste.dzfl.pl/f54decee45bc
It 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.
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 ade90036
Nov 16 2017
prev sibling next sibling parent reply Daniel Kozak <kozzi11 gmail.com> writes:
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:

 This one works ok for me, but I am on linux:
 https://dpaste.dzfl.pl/f54decee45bc
It 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=
d:
 ```
    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 =3D=3D EINTR.

 Also not having a timeout in the Socket.select of handle_socket allows fo=
r
 DOS attacks like the one above. In case of a timeout select also returns =
-1.

Nov 16 2017
parent ade90036 <andrea.rizzini gmail.com> writes:
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
prev sibling parent reply Daniel Kozak <kozzi11 gmail.com> writes:
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:
 This one works ok for me, but I am on linux:
 https://dpaste.dzfl.pl/f54decee45bc
It 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 =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
parent reply ade90036 <andrea.rizzini gmail.com> writes:
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 behavior
Ok, 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
parent reply ade90036 <andrea.rizzini gmail.com> writes:
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
parent reply ade90036 <andrea.rizzini gmail.com> writes:
 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
parent reply ade90036 <andrea.rizzini gmail.com> writes:
So, what is next?

Can we enable some sort of profiling to see what is going on?
Nov 16 2017
next sibling parent Daniel Kozak <kozzi11 gmail.com> writes:
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
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
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
parent kdevel <kdevel vogtner.de> writes:
On Friday, 17 November 2017 at 14:28:38 UTC, kdevel wrote:
 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.
This works: ``` if (++accept_cnt > 400) { writeln ("returning"); taskPool.stop; return; } ```
 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
prev sibling parent reply Daniel Kozak <kozzi11 gmail.com> writes:
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:

 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 i=
s
 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 seco=
nds
 (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
parent kdevel <kdevel vogtner.de> writes:
On Wednesday, 15 November 2017 at 14:22:51 UTC, Daniel Kozak 
wrote:
 And this one
 https://paste.ofcode.org/KNqxcrmACLZLseB45MvwC
I 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