Re: Can I/O operations on the same IO::Socket be executed in different threads?
by BrowserUk (Patriarch) on May 12, 2015 at 05:51 UTC
|
| [reply] |
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by ikegami (Patriarch) on May 12, 2015 at 15:12 UTC
|
If the only thing you do with the IO::Socket::INET and read and write from it, you won't have any problem since the object is not used at all for that (just the file handle on which its built).
use strict;
use warnings;
use threads;
use IO::Socket::INET qw( );
use constant SHUT_WR => 1;
my $sock = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => $ARGV[0] // 'localhost:echo',
)
or die $!;
async {
print while <$sock>;
};
async {
print($sock "abc\n");
sleep(3);
print($sock "def\n");
shutdown($sock, SHUT_WR);
};
$_->join for threads->list;
I don't know for IO::Socket::SSL.
Update: Added default argument.
| [reply] [d/l] |
|
How laughably naive of you to produce code that does nothing useful and announce: "you won't have any problem".
No wonder p5p decided to "discourage" threads when "experts" go around handing out advice like that!
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
| [reply] |
|
Threads aren't discouraged by p5p. The docs mispeak. It's meant to be a warning on the heavyness of Perl threads. It's already in the process of being fixed.
| [reply] |
|
|
|
|
That junk dies with Invalid argument at - line 2
Or if you go and fix it and specify 'localhost:6666' it dies with No connection could be made because the target machine actively refused it.
Windows doesn't have Unix domain socket, if thats what your program is supposed to use
Very poor showing for such a super hyper pedant
| [reply] [d/l] [select] |
|
Sounds like you didn't actually run a server at localhost:6666? A simple echo server would do. (You can find one at localhost:echo sometimes, though it's trivial to write one.)
IO::Socket::INET->new creates a TCP socket, not a unix socket.
| [reply] [d/l] [select] |
|
|
|
I, too, don’t think that I fully understand the basis from which you speak, and I candidly suspect that your example might produce a “false positive” since it waits three seconds. Thus, it is probably not a representative example of what might go wrong, since in your example it is unlikely that the threads would ever the threads would never actually conflict with one another.
When you’re dealing with a real-world remote system, the status of that system is only represented by the single stream of messages that the system is sending-out on each of its sockets. I think that, therefore, you need to have one thread on your end which is responsible for knowing the status of every one of them. And, because socket-operations are not terribly fast, a select() loop works perfectly. One thread, processing “one message at a time” from all sources at once, can always keep up and will never present a bottleneck.
This venerable strategy elegantly avoids all timing problems. One thread can easily accommodate all of the connections, and can (centrally) keep track of the status of all of them, so that all of the clients get exactly what they need and want: “Please send this message to somebody, and give me the reply as soon as you get it.” Due to the realities of network-I/O (and, remote systems ...), there’s no reason to make things any more complicated, and, as it were, plenty of reasons not to.
| |
|
I, too, don’t think that I fully understand the basis from which you speak,
I figured I'd actually answer the OP's question. You see, the OP asked if one thread could read from a socket while another writes to the same socket.
BrowserUk's incredible hostility is completely unwarranted. There are common applications for what the OP asks. Tunnels, for one. Another would be to avoid using select+ioctl to do non-blocking output (which probably doesn't work on Windows). I think IPC::Run does exactly this in some circumstances.
| [reply] [d/l] [select] |
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by noxxi (Pilgrim) on May 12, 2015 at 20:20 UTC
|
Is this also true also for IO::Socket::SSL sockets (assuming a recent version of Net::SSLeay)?
As long as these actions are locked against each other and thus do not happen at the same time it should be safe enough. Without locking: no. It is better to restrict yourself so that all actions on the same socket are done in the same thread.
| [reply] |
|
Just to clarify—you are saying that the concerns in IO::Socket::SSL + fork problem do not apply in this case?
How do you envision such socket being managed? Perusing threads::shared and my $sock :shared; will ensure coherent state? The Net::SSLeay itself is thread-safe, but does it support shared sockets? Any special concerns, such as nonblocking mode or cross-thread callbacks?
| [reply] [d/l] |
|
Once the SSL socket is established all the necessary state for reading and writing is fully managed by the OpenSSL library. That is the Perl socket can be used between multiple threads, but only one thread should use the socket at one time (i.e. needs locking). Forking is different because in this case the SSL state gets duplicated and client and server try to manage their own state independently which will not work because then none of these will reflect the real state of the SSL connection.
Of course all this means that the Socket must be created before the threads are created. Creating a socket in one thread and then using it in another will not work since the Perl handle can not be shared.
| [reply] |
|
And yet, one thought that keeps banging against my head is this: “even if we can ‘make it to work,” does it ‘make sense?’” That, to me, is actually a key question here: “does it actually ‘make sense’ that one thread should ‘only read,’ and that another thread, ‘only write?’” I don’t think so. Why? Because somebody needs to be aware of: “the present-state of the remote client.” And, furthermore, this “present-state” can only be ascertained, subject to an unforeseen (and, unforeseeable ...) lag.
Therefore, I suggest that the only truly-practicable design must be this: “put your letter in the box, raise the flag, and then wait for the postman.” “The Postman” has no race-conditions within himself. Therefore, he is able to efficiently serve the needs of any number of clients without conflict.
To my way of thinking, both of the client-processes in question should be reading/writing from queues that are served by the Postman. They should not be attempting to read/write from the socket directly.
| |
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by Anonymous Monk on May 12, 2015 at 06:44 UTC
|
Threads may share a socket or file descriptor all right, but you probably ought to use low-level Socket module if that is your intent. Any issues with the resource sharing (synchronization, locking, state logic, teardown, and so on) are solely your responsibility. This precludes the use of protocol layers such as SSL.
| [reply] |
|
| [reply] |
|
I can't speak for the AM, but several years ago I wrote some C code on Linux that shared a socket between 2 threads, one reading, one writing (like what the OP wants to do). It worked for me. I don't know if this would work on MS Windows.
| [reply] |
|
|
Any threaded code that spits debugging info (say), ought to satisfy your stipulation. (Although technically, a pty is not a socket. But then again, one might redirect the output.)
| [reply] |
|
|
|
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by Anonymous Monk on May 13, 2015 at 02:35 UTC
|
This should be portable (works on window ) .... dunno if its better or worse than using Thread::Queue for ipc( qin/qout)
#!/usr/bin/perl --
use strict;
use warnings;
use threads stack_size => 4096;
use IO::Socket::INET;
our $host = "localhost:".int(rand 99999);
async( sub {
my $tid = threads->tid;
use Time::HiRes qw/ usleep /;
my $server = IO::Socket::INET->new(
Proto => "tcp",
LocalHost => $host,
Listen => 1,
Reuse => 1,
) or die "Cannot start tcp server: $!\n $@\n ";
print "listening on $host\n";
sleep 1;
my $client = $server->accept();
while(<$client>){
print "reader($tid) $_";
last if /exit/i;
}
});
async( sub {
my $tid = threads->tid;
use Time::HiRes qw/ usleep /;
my $socket = IO::Socket::INET->new(
Proto => "tcp",
PeerHost => $host,
) or die "Cannot start client : $!\n $@\n ";
for(1..20){
usleep 2000*rand(500); # 2000 sleep for 2 millisecond
$socket->print( "writer($tid) ".scalar(gmtime)."\n" );
}
$socket->print( "writer($tid) ".scalar(gmtime)." exit\n" );
});
$_->join for threads->list;
__END__
| [reply] [d/l] |
|
| [reply] |
|
And? You've got two connected endpoints, two descriptors. There's no shared socket. Yes, that is the point of this example -- it does seem to miss the point of the OP, so yeah :)
| [reply] |
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by locked_user sundialsvc4 (Abbot) on May 12, 2015 at 12:08 UTC
|
Excellent thread you refer-to ... everyone should read it carefully all the way through. (I just tossed-out a bunch of up-votes.)
There is one classic design for working with multiple sockets: a select-loop, which polls a list of sockets to find out which one(s) have data, managed by one thread, which acts as the incoming and outgoing gatekeeper for the entire system. This design works extremely well, and there are plenty of CPAN modules out there which already implement all of it.
Network I/O is a comparatively slow operation, so there is plenty of time, even with many sockets to tend. But there’s also one other important consideration: the need to track the state of the external systems with which you are communicating. If a single thread is pulling off a sequential stream of messages from multiple sockets, there will never be any timing issues. That one thread, with simple FSM = Finite-State Machine logic, can keep track of them all, and can do whatever protocol exchanges are needed. Meanwhile, the worker threads, who are listening and writing to thread-safe queues populated by the gatekeeper, need have no such concerns.
| |
|
| [reply] |
|
Yes, I did up-vote (yes, I said up...) BrowserUK’s comments to this thread and to the other thread that he referred-to ... while also (intending to be) speaking very favorably of it. I also up-voted several other comments including later ones in the that same thread (near the end of it).
| |
|
| [reply] |
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by Anonymous Monk on May 14, 2015 at 00:56 UTC
|
#!/usr/bin/perl --
use strict;
use warnings;
use threads stack_size => 4096;
use IO::Socket::INET;
use Time::HiRes qw/ usleep /;
use Data::Dump qw/ dd /;
Main( @ARGV );
exit( 0 );
sub Main {
my $host = "localhost:" . int( rand 99999 );
Dodo( $host, 'IO::Socket::INET' ); ## "works"
## Dodo( $host, 'IO::Socket::SSL' ); use IO::Socket::SSL; ## "doesn
+'t"
} ## end sub Main
sub Dodo {
my( $host, $socktype ) = @_;
async( \&Server, $host, $socktype );
sleep 1;
my $socket = $socktype->new(
Proto => "tcp",
PeerHost => $host,
) or die "Cannot start client : $!\n $@\n ";
$socket->blocking( 0 );
async( \&Reader, $socket );
async( \&Writer, $socket );
$_->join for threads->list;
} ## end sub Dodo
sub Server {
my( $host, $socktype ) = @_;
my $tid = threads->tid;
use Time::HiRes qw/ usleep /;
my $server = $socktype->new(
Proto => "tcp",
LocalHost => $host,
Listen => 1,
Reuse => 1,
) or die "Cannot start tcp server: $!\n $@\n ";
dd "listening on $host\n";
sleep 1;
my $client = $server->accept();
while( <$client> ) {
dd "echo reader($tid) $_";
print $client "echo reader($tid) $_";
last if /exit/i;
} ## end while( <$client> )
} ## end sub Server
sub Writer {
my( $socket ) = @_;
my $tid = threads->tid;
for( 1 .. 20 ) {
usleep 2000 * rand( 500 ); # 2000 sleep for 2 millisecond
$socket->print( "writer($tid) " . scalar( gmtime ) . "\n" );
} ## end for( 1 .. 20 )
$socket->shutdown( 1 ); ## done writing
} ## end sub Writer
sub Reader {
my( $socket ) = @_;
my $tid = threads->tid;
my $read = 0;
while( $read < 1000 ) {
my $readed = $socket->sysread( my $line, 51 );
next if !$readed;
$read += $readed;
dd "holymolyreaded($read/$readed) $line";
usleep 2000 * rand( 500 );
} ## end while( $read < 1000 )
$socket->print( "reader($tid) " . scalar( gmtime ) . " exit\n" );
} ## end sub Reader
__END__
| [reply] [d/l] |
Re: Can I/O operations on the same IO::Socket be executed in different threads?
by ikegami (Patriarch) on May 14, 2015 at 18:02 UTC
|
If you're trying to communicate between the two threads using that socket, see this simple solution. | [reply] |