View previous topic :: View next topic |
Author |
Message |
RedShodan
Joined: 04 Dec 2004 Posts: 5 Location: Colorado, US
|
Posted: Mon Dec 20, 2004 8:25 pm Post subject: SocketConduit fix for non-blocking IO |
|
|
Howdy.
I found a slight problem with the select handling of SocketConduit.reader() when you have a timeout set and the socket set to non-blocking. Basicly the return of select needs to be checked for the 0 value and return before trying to read. Here is a patch against mango_release_1.0a. Thanks.
Code: |
diff -rc mango/mango/io/SocketConduit.d patch/mango/io/SocketConduit.d
*** mango/mango/io/SocketConduit.d 2004-11-30 13:28:06.000000000 +0000
--- patch/mango/io/SocketConduit.d 2004-12-20 19:07:43.957714600 +0000
***************
*** 316,323 ****
ss.add(this);
// wait until data is available
! if (select (ss, null, null, &tv) <= 0)
! return Eof;
}
int count = receive (src);
--- 316,326 ----
ss.add(this);
// wait until data is available
! int ret=select (ss, null, null, &tv);
! if (ret < 0)
! return Eof;
! else if (ret == 0)
! return 0;
}
int count = receive (src);
| [/code] |
|
Back to top |
|
|
kris
Joined: 27 Mar 2004 Posts: 1494 Location: South Pacific
|
Posted: Tue Dec 21, 2004 10:26 pm Post subject: |
|
|
Thanks!
I had vaguely hoped to avoid exposing those kinds of complexities, as you have probably gathered. May I ask why you're using an asynchronous socket in conjunction with a timeout? Is it to get around the simplistic socket model exposed by Mango.io, or for a deeper reason? I ask because there may be other areas to address, where some internal Mango peeking at the socket synchronous/asynchronous state might be a more compelling solution?
In a similar vein, are there any other scenarios you know of whereby select() returns a zero?
Appreciate your post and your input! |
|
Back to top |
|
|
RedShodan
Joined: 04 Dec 2004 Posts: 5 Location: Colorado, US
|
Posted: Sun Dec 26, 2004 8:29 pm Post subject: |
|
|
No problem.
I'm using nonblocking along with a select timeout so that I can do both read and writes on a socket within a single thread, and have that thread be able to exit easily. I use a 200ms timeout, provides good response and doesnt impact the system hardly any. After that timeout is up during the call to select, with that patch, my code looks at the zero return and says 'Oh, nothing to do, lets loop around'. When it loops around it checks to make sure that it should still be running. Without that patch, the select would timeout and then the read() would be called. Because the select timed out we know that there is nothing to read. With the rest of the IO framework that causes an error when the file descriptor is set to non-blocking. So this will just bypass the read. For example of how its used, heres a chunk of my code:
Code: |
void run()
{
.....
while (running())
{
if (!doWrite())
break;
if (!doRead())
break;
}
stop();
.....
}
protected bool doRead()
{
int readLen;
myConduit.setTimeout(200);
readLen=myConduit.read(myReadBuffer);
if (readLen == SocketConduit.Eof)
return false;
else if (readLen == 0)
return true;
.....
}
|
So assuming no pending output, the vast majority of time will be spent in the call to select from the SocketConduit.read(). Thats the perfect place to spend time, looking to see if we can read/write data. But if the timeout is reached, drop out and let the calling code be able to differentiate between an error and a timeout. This will let the calling code go off and 'do something' then come back repeat. In this case its looking to see if its still running. Another thread can (and will) set the running state to false at some point.
Incidently, the doWrite handles partial writing of bytes, keeps the remainder buffered, then tries to write more the next time its called. The doRead has to do the same, but inverted. With nonblocking IO it can read a partial 'packet of data'. So it has to know how much it needs to read, and keep trying to read, buffering as it goes until its got the right amount.
Its basicly a mixture of the 'a thread for every system call that could block' and the 'non-threaded socket server that has to be able to check other things every so often'.
I understand about trying to keep it simple . But, I think in this case its needed. Also, I dont do windows (I have before though), but I *think* this would work.
Thanks for the response
RedShodan |
|
Back to top |
|
|
kris
Joined: 27 Mar 2004 Posts: 1494 Location: South Pacific
|
Posted: Wed Dec 29, 2004 2:38 pm Post subject: |
|
|
That all seems reasonable to me. Can anyone think of a good reason for a non-blocking write, also? Or a good reason to not support such non-blocking operations? For example, should a read-length of 0 be illegal?
If no-one comes up with a solid argument against, I'll add RedShodan's patch to the next release.
Cheers; |
|
Back to top |
|
|
kris
Joined: 27 Mar 2004 Posts: 1494 Location: South Pacific
|
Posted: Sat Jan 01, 2005 8:47 pm Post subject: |
|
|
RedShodan wrote: | Code: |
void run()
{
.....
while (running())
{
if (!doWrite())
break;
if (!doRead())
break;
}
stop();
.....
}
protected bool doRead()
{
int readLen;
myConduit.setTimeout(200);
readLen=myConduit.read(myReadBuffer);
if (readLen == SocketConduit.Eof)
return false;
else if (readLen == 0)
return true;
.....
}
|
|
If it's all the same, I'd prefer to support it like this:
Code: | protected bool doRead()
{
int readLen;
myConduit.setTimeout(200);
readLen=myConduit.read(myReadBuffer);
if (readLen == SocketConduit.Eof)
return myConduit.hadTimeout;
.....
} |
... which is checked in, and will be included in the next release. BTW; one of the pleasant things about D is that you can replace this:
Code: | if (readLen == SocketConduit.Eof)
|
... with this:
Code: | if (readLen == myConduit.Eof)
|
I always find the latter to be more readable, for some reason or other. |
|
Back to top |
|
|
RedShodan
Joined: 04 Dec 2004 Posts: 5 Location: Colorado, US
|
Posted: Tue Jan 11, 2005 7:37 pm Post subject: |
|
|
hadTimeout is perfectly fine with me. A little more verbose, which is a good thing for maintanence.
Oh and about the instance versus namespace. I personaly prefer using the name space for non-member stuff. It reinforces where its used that this is not an instance variable, but something global to that name space.
Thanks! |
|
Back to top |
|
|
kris
Joined: 27 Mar 2004 Posts: 1494 Location: South Pacific
|
Posted: Tue Jan 11, 2005 8:01 pm Post subject: |
|
|
RedShodan wrote: | hadTimeout is perfectly fine with me. A little more verbose, which is a good thing for maintanence.
...
Thanks! |
Sure! And thanks for bringing this up; |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|