Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows ring buffer without copying

On Ring Buffer's Wikipedia entry, there's example code showing a hack for UNIX systems whereby the adjacent virtual memory to a piece of memory is mapped to the same phbysical memory, thus implementing a ring buffer without the need for any memcpy, etc. I was wondering if there's a way to so something similar in Windows?

Thanks, Fraser

like image 515
Robert Fraser Avatar asked Jun 19 '09 08:06

Robert Fraser


2 Answers

I didn't really follow all the details of the example in wikipedia. With that in mind, you map memory in Windows using CreateFileMapping and MapViewOfFile, however MapViewOfFile does not allow you to specify a base address for the mapping. MapViewOfFileEx can be used to specify a base address so maybe you could use a similar technique.

I don't have any way of telling if this would actually work:

// determine valid buffer size
SYSTEM_INFO info;
GetSystemInfo(&info);

// note that the base address must be a multiple of the allocation granularity
DWORD bufferSize=info.dwAllocationGranularity;

HANDLE hMapFile = CreateFileMapping(
             INVALID_HANDLE_VALUE,
             NULL,
             PAGE_READWRITE,
             0,
             bufferSize*2,
             L"Mapping");

BYTE *pBuf = (BYTE*)MapViewOfFile(hMapFile,
                    FILE_MAP_ALL_ACCESS,
                    0,                   
                    0,                   
                    bufferSize);
MapViewOfFileEx(hMapFile,
                    FILE_MAP_ALL_ACCESS,
                    0,                   
                    0,                   
                    bufferSize,
                    pBuf+bufferSize);
like image 187
1800 INFORMATION Avatar answered Sep 20 '22 05:09

1800 INFORMATION


Oh hey, this is the topic which worried me a lot lately. I needed posix-optimised ring buffer on Windows, mostly because of its random-access interface, but never had any idea on how to implement it. Now, the code proposed by @1800 INFORMATION works sometimes, sometimes it doesn't, but the idea is great anyway.

The thing is, MapViewOfFileEx sometimes fails with ERROR_INVALID_ADDRESS meaning that it cannot map the view to pBuf+bufferSize. This is because the MapViewOfFile called before selects a free address space of bufferSize length (starting from pBuf), but it doesn't garantee this address space to be bufferSize*2 long. And why would we need bufferSize*2 virtual memory? Because our ring buffer needs to wrap. This is what the second mapping view is for. When the read or write pointer leaves the first view, it enters the second view (because they are contigous in memory), but actually it starts over at the same mapping.

UINT_PTR addr;
HANDLE hMapFile;
LPVOID address, address2;

hMapFile = CreateFileMapping (    // create a mapping backed by a pagefile
    INVALID_HANDLE_VALUE,
    NULL,
    PAGE_EXECUTE_READWRITE,
    0,
    bufferSize*2,
    "Local\\mapping" );
if(hMapFile == NULL) 
    FAIL(CreateFileMapping);

address = MapViewOfFile (    // find a free bufferSize*2 address space
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0,                   
    0,                   
    bufferSize*2 );
if(address==NULL) 
    FAIL(MapViewOfFile);
UnmapViewOfFile(address);
// found it. hopefully it'll remain free while we map to it

addr = ((UINT_PTR)address);
address = MapViewOfFileEx (
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0,                   
    0,                   
    bufferSize, 
    (LPVOID)addr );

addr = ((UINT_PTR)address) + bufferSize;        
address2 = MapViewOfFileEx (
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0,                   
    0,                   
    bufferSize,
    (LPVOID)addr);  

if(address2==NULL)      
    FAIL(MapViewOfFileEx);

// when you're done with your ring buffer, call UnmapViewOfFile for 
// address and address2 and CloseHandle(hMapFile)
like image 44
Artem Martynovich Avatar answered Sep 19 '22 05:09

Artem Martynovich