[MPlayer-dev-eng] "lockless" cache2 looks buggy

Rich Felker dalias at aerifal.cx
Sat Aug 26 05:47:22 CEST 2006


On Fri, Aug 25, 2006 at 10:46:58PM +0200, Denis Vlasenko wrote:
> Hello mplayer coders,
> 
> I was looking into cache2.c. It says that it is a lockless
> cache implementation.
> 
> I see s few issues with the code. First issue is the most serious,
> since it looks like real bug.
> 
> (1) Non-atomic updated of metadata.
> 
> Main thread reads cached data using cache_read(), which uses
> off_t variables s->min_filepos, s->max_filepos etc.
> They are 64-bit variables on most systems:
> 
>     newb=s->max_filepos-s->read_filepos; // new bytes in the buffer
>     pos=s->read_filepos - s->offset;
>     if(pos<0) pos+=s->buffer_size; else
>     if(pos>=s->buffer_size) pos-=s->buffer_size;
>     if(newb>s->buffer_size-pos) newb=s->buffer_size-pos; // handle wrap...
>     if(newb>size) newb=size;
>     memcpy(buf,&s->buffer[pos],newb);
> 
> However, caching thread reads new data into circular buffer
> and updates those same variables in cache_fill():
> 
>   back2=s->buffer_size-(space+newb); // max back size
>   if(s->min_filepos<(read-back2)) s->min_filepos=read-back2;
>   len=stream_read(s->stream,&s->buffer[pos],space);
>   if(!len) s->eof=1;
>   s->max_filepos+=len;
>   if(pos+len>=s->buffer_size){
>       // wrap...
>       s->offset+=s->buffer_size;
>   }
> 
> These updates are not atomic on 32-bit architectures (i386).
> Therefore caching thread can update lower half of a value, then it
> may get rescheduled with CPU given to main thread, which then
> will use bogus value of half-updated variable.
> 
> If you don't believe me, add asm() statements as shown below:
> 
> asm("#1");
>   s->max_filepos+=len;
> asm("#1");

Maybe this works:

union { off_t o; double d; } foo = { s->max_filepos + len };
*(double *)&s->max_filepos = foo.d;

?? I'm sure there are better implementations of the same idea.

Rich




More information about the MPlayer-dev-eng mailing list