in reply to Re^2: Trying to thread a daemon with threads and Thread::Queue
in thread Trying to thread a daemon with threads and Thread::Queue
Try replacing your handledata() sub with this:
sub handleData { my $tid = threads->tid(); my $fno = $Q->dequeue(); my $clientAddr = $Q->dequeue(); next if $fno eq 'STOP'; my ( $clientPort, $clientIp ) = sockaddr_in( $clientAddr ); my $ipStr = inet_ntoa( $clientIp ); open my $socket, '+<&=' . $fno or die $!; my $request = <$socket>; chomp $request; if ($request eq '<policy-file-request/>') { warn("($tid) : XML Policy file request from: $ipStr"); print $socket $content.$NULLBYTE; } else { warn("($tid) : Connection from: $ipStr"); print $socket "<data><ip>$ipStr</ip></data>".$NULLBYTE; } shutdown $socket, 2; close $socket; return 1; }
I don't anticipate that it will perform better than your non-threaded solution for teh reasons I stated above. Create a new thread to reply with a single line of code is never going to be quicker than replying inline, but it might just work well enough to allow you to see that for yourself.
For the application as you've posted, I am quite certain that you will not improve your performance using threads this way.
You'd almost certainly be better off using IO::Socket::INET and setting the Listener option to allow the tcpip stack to queue inbound requests.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^4: Trying to thread a daemon with threads and Thread::Queue
by jasmineaura (Initiate) on Aug 29, 2008 at 04:01 UTC | |
Re^5: multithreaded tcp listener with IO::Socket You mentioned: "The main problem with this approach is that you have to ensure that the socket returned by accept() does not go out of scope (causing the socket to be closed), before the thread has had chance to reopen it. That's easily achieved by pushing a copy of the socket handle into a (non-shared) array in the accept thread, as well as queueing it." So I went ahead and took a look at your sample code (threads::Server) from the next reply on that same thread, here: Re^7: multithreaded tcp listener with IO::Socket I realized you're using an associative array (hash) in the main accept thread to store the main handle and keep the socket open, and also using a seperate queue (Qclean) in another routine ( while($Qclean->pending) ) to close the socket once the thread is done with it, iiuc.. Only problem is that the "close delete $hash{$fno}" line didn't work for me, leaving the connection on the client side open, so instead I tried: And that worked, the request went through. But... The process just mysteriously died!!! No errors, no warnings in the log, nothing! I tried taking out the line "shutdown $socket, 2;" from the end of the handleConnection() sub, and reran it, then the process doesn't die for each request, but also the client still doesn't close :( Removed the line "shutdown $socket, 2;" from the sub, and added "shutdown $sockets{ $fileno }, 2;" right before the "close $sockets{ $fileno };" line in the main accept thread, then the process doesn't die after a request, but also leaves the client open :| I'm puzzled... Any ideas on this one? Below is my full code (revised and cleaned of some minor bugs).
| [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Aug 29, 2008 at 04:25 UTC | |
BrowserUK, an older post of yours on the subject (from 2006) enlightened me, and I was able to find out what the problem was and solved my issues. Thank you so much.. Gah! You're too generous. I should have remembered and recognised that problem and saved you a couple of days. I'm glad you've achieved your goal. However, I still fail to see how spawning a new thread, or just transfering the socket to a thread pool is going to operate more quickly than just printing one short line, closing the connection and going back to the accept. Whilst a single push to a queue is going to be faster than IO to a socket, given that you're now setting up the listener queue, the tcp stack should be able to buffer the connections long enough to make the difference moot. Did you do any soak testing yet? Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
by jasmineaura (Initiate) on Aug 29, 2008 at 04:35 UTC | |
I can't do any testing yet, due to the problem I described in my previous (updated) reply.. I am not really concerned about the speed of the response of the threaded version of this script vs. the non-threaded version. The whole idea was to allow handling multiple simultaneous connections/requests, in case one client is really lazy or slow, I don't want it to delay other clients/requests for several seconds. Still, I am curious to test it and see the difference, once I get it working :) | [reply] |
|
Re^4: Trying to thread a daemon with threads and Thread::Queue
by jasmineaura (Initiate) on Aug 29, 2008 at 16:55 UTC | |
Hmm. Have you managed to track down what code is executing when that silent abnormal termination occurs?Yep, with some extra logging.. First, I added this in main to prevent the abend: Then added this in the handler thread: I simulate a connection from client: The connection is immediately closed as desired, but I get nothing back from the server for my request, when --according to the code in the thread-- I should be getting this back: The output in the log: Caused by the close() call after shutdown() in the handler thread (the code you previously suggested), not the close() in Main! I go ahead and remove the close() call in the handler thread keeping only the shutdown() call, so it will look like this: And in the main accept() loop I also remove the close() call, so it will look like this: Again, I simulate another connection from client via shell: I should've got this response: But again, the connection is immediately closed on the client side before they even receive a response to their request as coded for in the handler thread.. This is very bad! Now the log shows: Probably as I said previously, having the shutdown on the dup handle in the thread causes SIGPIPE to be thrown because the main handle is still open in the main accept() thread. And for whatever reason (which I am unaware of), whatever the handler thread wrote back on the socket prior to the shutdown is lost and is not sent to the client. Hmmm??? However, if I _remove_ the shutdown() call from the handler thread (keeping only the "close $socket" line), the client's request gets back the proper response, only it doesn't get immediately closed (until another connection comes in as we previously established) In my tests (under win32), if I dont use shutdown in the hendler thread, the client connection stays open until the main thread gets around to closing its copy of the socket.Did you actually get a response back for your request when you have shutdown() in the handler thread? Are you ignoring SIGPIPE? Maybe Win32 doesn't blow up with SIGPIPE when a handle is closed somewhere in the code while it's still open elsewhere? Still, it certainly does on *nix according to my testing... | [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Aug 29, 2008 at 17:37 UTC | |
Are you ignoring SIGPIPE? There is no such concept as SIGPIPE under Win32. Hence why I said that I cannot help you debug this further, I simply cannot reproduce the failure here. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
Re^4: Trying to thread a daemon with threads and Thread::Queue
by jasmineaura (Initiate) on Aug 27, 2008 at 22:32 UTC | |
The socket doesn't receive anything, hangs, until on the client connect side I CTRL+C, then the server log would show just one log message "(3) : Connection from: x.x.x.x", and the server immediately dies with no errors/warnings right after. I will try IO::Socket, it's something I've been meaning to do.. Perhaps two different variations, one script as you describe, and another with threads and threads::shared, kind of like the 4th point discussed on this thread: Re: Passing an IO:Handle to a Thread My main concern behind the limitation of handling one connection at a time is the possibility of a really slow (or dysfunctional) client that takes too long to send its request (or doesn't send a request at all) after it connects to the server, making all other incoming connections queued as long as the connected (slow/dysfunctional) client has not timed out. | [reply] |
by BrowserUk (Patriarch) on Aug 27, 2008 at 23:03 UTC | |
Sorry, I don't think I can help further. Your code with that change works for me here. I have stayed with (actually reverted to) 5.8.6 because I've had all kinds of unresolvable problems using threads with 5.8.8. Too many changes have been made for change sake. The once pretty reliable techniques I used successfully since 5.8.0 stop working. The current maintainer has his own agenda, which from my perspective comes down to complexity for its own sake. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
by jasmineaura (Initiate) on Aug 27, 2008 at 23:25 UTC | |
A thought that comes to mind, is to keep the connection threads alive for a pre-defined number of connections, say $threadLifeTime = 100. I'll do some more reading and research to see if I can do without Thread::Queue. | [reply] |
|
Re^4: Trying to thread a daemon with threads and Thread::Queue
by jasmineaura (Initiate) on Aug 28, 2008 at 21:09 UTC | |
Here is the relative lines from the sub:
When I try to connect from client side, I see this in the server log: Which is the "open my $socket, '+<&='.$fno" Line.. If I try connecting again, the fileno is still 5, only the thread number changes (1..3 when running only 3 listener threads) and each thread is "terminated abnormally" due to this error. Other instance, I get this in the log (both lines returned on one connection only) Or this: And the client gets nothing back to its request. Running: Perl 5.8.5 and Thread::Queue 2.11 OS: Linux | [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Aug 29, 2008 at 02:32 UTC | |
Sorry, but you've posted several versions and partial version of your code in this thread and there is no way for me to guess which version the above is applied to. If you post the full version that demonstrates the problem I will take a look at it. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
Re^4: Trying to thread a daemon with threads and Thread::Queue
by jasmineaura (Initiate) on Aug 29, 2008 at 06:48 UTC | |
Re^6: Trying to thread a daemon with threads and Thread::Queue The following part of the code (in the handleConnection() sub) has a serious flaw: From: perldoc on shutdown() "It's also a more insistent form of close because it also disables the file descriptor in any forked copies in other processes." The thread tries to force closing the socket using shutdown() (after the thread is done with the connection) while the main accept thread still has it open, causing the script to abnormally terminate without any errors messages. Removing this line from the handleConnection() sub and keeping only "close socket;", re-running the script, then simulating a client connection and request returns the expected data, but the client sees the connection still open (because the socket wasn't closed from server) This is due to the construct of the main accept thread: Basically the while ( $Qclean->pending ) is not immediately triggerd (to close the socket in the main accept thread) because it is inside the while ( ... $sock->accept() ), and won't happen until another client connects, so the first client will just hang because it sees the socket on the server is still open. In other words, client-A connects and sends request, gets response, but the server doesn't close the socket, so the client stays connected even though the server is done sending. Then when client-B connects, Qclean is checked and sees the socket associated with client-A needs to be closed, so it goes ahead and closes it. I'm stumped... Any thoughts/ideas on how to fix this? Update: I tried adding this early in my script: After re-adding the shutdown() line before close() in the handleConnection() sub to avoid the script blowing up because of SIGPIPE: But then the client doesn't get the response (what is written on the socket by the server thread from the handleConnection() sub), just a connection drop :S | [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Aug 29, 2008 at 15:07 UTC | |
The thread tries to force closing the socket using shutdown() (after the thread is done with the connection) while the main accept thread still has it open, causing the script to abnormally terminate without any errors messages. Hmm. Have you managed to track down what code is executing when that silent abnormal termination occurs? In my tests (under win32), if I dont use shutdown in the hendler thread, the client connection stays open until the main thread gets around to closing its copy of the socket. Which doesn't sound so bad until you realise that the main thread will never get around to closing the socket until it receives a new incoming connect to terminate the accept. So, you get a burst of incoming connects, queue up the file numbers and store the sockets in the main thread until the main threads timeslice ends. Then the child threads get a lookin, and process the requests, and close their copies of the handles. But, until the main thread both gets a timeslice, and receives new connect request all the clients your child threads processed are sitting there with open connections. As the main thread will be able to queue up (potentially) several hundred connections before it relinguishes its timeslice, and your child threads will process and close but not disconnect those same several hundred once they get timeslices, there is the potential for having several hundred clients hanging around waiting for a new client to connect before their connections will be terminated. If your traffic is in any way 'bursty', that could prove to be a big problem. If the abend is down to the main thread trying to close a socket that has already been closed--though I wouldn't have though that likely; There is not logic to the idea that shutdown would cause this failure, as this use is exactly what it is designed for--then it would be better to do the shutdown in the handler threads and just discard the sockets in the main thread rather than attempting to close them. Needless to say, but I will anyway :) I cannot reproduce your failure here, so you are going to have to explore the reason for the abend yourself. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
Re^4: Trying to thread a daemon with threads and Thread::Queue
by jasmineaura (Initiate) on Aug 30, 2008 at 02:19 UTC | |
1. Setting up IO::Socket::INET in main, and passing the $main_sock to a pool on pre-forked threads which all handle the accept() 2. Multiplexing with IO::Select in each thread to avoid the blocking nature of the accept() call. This provides us the advantage to pickup the faster client (the one who's able to send the request faster) and avoid a lazy client who takes their sweet time before they send their request (in the case of simultaneous connections/bursts) I mentioned previously that I simulated a pair of sequential connections a 100 times with the non-threaded/non-select script which took about 1.990 seconds. Doing the same simulation test on the new version of the script (a pre-forked pool of 3 threads and using the select method in each thread) took about 2.063 seconds. Therefore, IMHO, the overhead is very minimal, and quite a fair trade off for having the ability to handle multiple clients simultaneously. Only if I am wasn't so tired now, I would've quickly coded a script using threads to simulate maybe 20 or 30 simultaneous (asynchronous) connections a few times in a row and compare the results of the two versions of the script, even though I'm sure the threaded/non-blocking version would be way faster. Here's my new threaded version of the script. Comments/Suggestions welcome..
| [reply] [d/l] |
by BrowserUk (Patriarch) on Aug 30, 2008 at 14:55 UTC | |
This empty loop in your main thread:
will consume as much cpu as the scheduler can give it. In essence, this means that when the main thread gets a timeslice, it will consume 100% of your cpu for its entire timeslice, doing absolutely nothing and preventing any other threads or processes getting a look in (unless you have multiple cores). Even if you have mutliple cores, this would just be 'busy work' consuming resources and power for no good reason. At the very least you should slow that loop down:
Beyond that, what you have seems way too complex--mixing as it does fork, threads and select--and I seriously doubt that it will perform any better than a simple, single-threaded process. Of course, you can run your tests and 'prove' me wrong and I cannot argue with you. And after all, it's your code and you will have to maintain it. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
by jasmineaura (Initiate) on Sep 02, 2008 at 21:33 UTC | |
was to keep the script from ending (or code falling off the end of the script) soon after it spawns the threads... guess you're right.. So what do you suggest I do to prevent the script from ending after the main thread spawns the threads ? sleep while ($running); ? | [reply] [d/l] |
by BrowserUk (Patriarch) on Sep 02, 2008 at 22:05 UTC | |