I run a mud that has a bit of smaug code in it, especially the socket code. I recently added mccp and took it from smaug fuss. I then noticed my mud started lagging very ocasionally when it never used to, sometimes the lag could be a couple of minutes, sometimes barely noticeable but often for a few seconds. I thought what have I done? surely this would be obvious to everyone else if it were a bug.. but a quick look at the code revealed a big problem and one very easy to fix too, well a quick fix at least :).
Anyway problem is below, I posted this to the smaugfuss site too but thought it was large enough to warrent posting here..
If the socket is blocked, you continue the loop.
What should happen is either you check how many times this has failed, or you just return false immediately
as it is it can lag a mud for minutes at a time if the socket blcoks more commonly it just lags the mud for a few seconds as sockets usually die..
Why bother using non blocking sockets when you basically block them waiting until you can write??
The same problem is also further down in write_to_descriptor also, best solution would be to put data back into buffer and let the mud try again later and perhaps a counter in descriptor_data that could count the bad retries each time flush_buffer is called and perhaps after 10 blocked writes dump the socket.
of course the easiest fix is just to return false straight away, but this could dump people when it isn't necessary as sockets can block temporarily for various reasons.
len = d->mccp->out_compress->next_out - d->mccp->out_compress_buf;
if( len > 0 )
{
for( iStart = 0; iStart < len; iStart += nWrite )
{
nBlock = UMIN( len - iStart, 4096 );
nWrite = send( d->descriptor, d->mccp->out_compress_buf + iStart, nBlock, 0 );
if( nWrite == -1 )
{
iErr = errno;
if( iErr == EWOULDBLOCK )
{
/*
* This is a SPAMMY little bug error. I would suggest
* not using it, but I've included it in case. -Orion
*
perror( "Write_to_descriptor: Send is blocking" );
*/
nWrite = 0;
continue;
}
else
{
perror( "Write_to_descriptor" );
return FALSE;
}
}
if( !nWrite )
break;
}
if( !iStart )
break;
if( iStart < len )
memmove( d->mccp->out_compress_buf, d->mccp->out_compress_buf + iStart, len - iStart );
d->mccp->out_compress->next_out = d->mccp->out_compress_buf + len - iStart;
}
<snip>
and same problem here for players not using mccp
probably best to put data back into outbuf and let it try again later
for( iStart = 0; iStart < length; iStart += nWrite )
{
nBlock = UMIN( length - iStart, 4096 );
nWrite = send( d->descriptor, txt + iStart, nBlock, 0 );
if( nWrite == -1 )
{
iErr = errno;
if( iErr == EWOULDBLOCK )
{
/*
* This is a SPAMMY little bug error. I would suggest
* not using it, but I've included it in case. -Orion
*
perror( "Write_to_descriptor: Send is blocking" );
*/
nWrite = 0;
continue;
}
else
{
perror( "Write_to_descriptor" );
return FALSE;
}
}
}
Anyway problem is below, I posted this to the smaugfuss site too but thought it was large enough to warrent posting here..
If the socket is blocked, you continue the loop.
What should happen is either you check how many times this has failed, or you just return false immediately
as it is it can lag a mud for minutes at a time if the socket blcoks more commonly it just lags the mud for a few seconds as sockets usually die..
Why bother using non blocking sockets when you basically block them waiting until you can write??
The same problem is also further down in write_to_descriptor also, best solution would be to put data back into buffer and let the mud try again later and perhaps a counter in descriptor_data that could count the bad retries each time flush_buffer is called and perhaps after 10 blocked writes dump the socket.
of course the easiest fix is just to return false straight away, but this could dump people when it isn't necessary as sockets can block temporarily for various reasons.
len = d->mccp->out_compress->next_out - d->mccp->out_compress_buf;
if( len > 0 )
{
for( iStart = 0; iStart < len; iStart += nWrite )
{
nBlock = UMIN( len - iStart, 4096 );
nWrite = send( d->descriptor, d->mccp->out_compress_buf + iStart, nBlock, 0 );
if( nWrite == -1 )
{
iErr = errno;
if( iErr == EWOULDBLOCK )
{
/*
* This is a SPAMMY little bug error. I would suggest
* not using it, but I've included it in case. -Orion
*
perror( "Write_to_descriptor: Send is blocking" );
*/
nWrite = 0;
continue;
}
else
{
perror( "Write_to_descriptor" );
return FALSE;
}
}
if( !nWrite )
break;
}
if( !iStart )
break;
if( iStart < len )
memmove( d->mccp->out_compress_buf, d->mccp->out_compress_buf + iStart, len - iStart );
d->mccp->out_compress->next_out = d->mccp->out_compress_buf + len - iStart;
}
<snip>
and same problem here for players not using mccp
probably best to put data back into outbuf and let it try again later
for( iStart = 0; iStart < length; iStart += nWrite )
{
nBlock = UMIN( length - iStart, 4096 );
nWrite = send( d->descriptor, txt + iStart, nBlock, 0 );
if( nWrite == -1 )
{
iErr = errno;
if( iErr == EWOULDBLOCK )
{
/*
* This is a SPAMMY little bug error. I would suggest
* not using it, but I've included it in case. -Orion
*
perror( "Write_to_descriptor: Send is blocking" );
*/
nWrite = 0;
continue;
}
else
{
perror( "Write_to_descriptor" );
return FALSE;
}
}
}