views:

781

answers:

3

I have been optimising memory performance on a Windows Mobile application and have encountered some differences in behaviour between VirtualAlloc on Win32 and Windows CE.

Consider the following test:

// Allocate 64k of memory
BYTE * a = (BYTE *)VirtualAlloc(0, 65536, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
// Allocate a second contiguous 64k of memory
BYTE * b = (BYTE *)VirtualAlloc(a+65536, 65536, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);    

BYTE * c = a + 65528; // Set a pointer near the end of the first allocation
BOOL valid1 = !IsBadWritePtr(c, 8); // Expect TRUE
BOOL valid2 = !IsBadWritePtr(c + 8, 4088); // Expect TRUE
BOOL valid3 = !IsBadWritePtr(c, 4096); // TRUE on Win32, FALSE on WinCE

The code "allocates" 4096 of data starting at "c". On Win32 this works. I can find no mention in the VirtualAlloc documentation whether it is legal or coincidence but there are many examples of code that I have found via google that expect this behaviour.

On Windows CE 5.0/5.2 if I use the memory block at "c", in 99% of cases there are no problems, however on some (not all) Windows Mobile 6 devices, ReadFile & WriteFile will fail with error 87 (The parameter is incorrect.). I assume ReadFile is calling IsBadWritePtr or similar and return false due to this. If I perform two ReadFile calls then everything works fine. (There may of course be other API calls that will also fail.)

I am looking for a way to extend the memory returned by VirtualAlloc so that I can make the above work. Reserving a large amount of memory on Windows CE is problematic as each process only gets 32MB and due to other items being loaded it is not possible to reserve a large region of memory without causing other problems. (It is possible to reserve a larger amount of memory in the shared region but this also has other problems.)

Is there a way to get VirtualAlloc to enlarge or combine regions without reserving it up front?

I suspect it may be problematic given the following examples:

HANDLE hHeap1 = HeapCreate(0, 0, 0); // Heap defaults to 192k
BYTE * a1 = (BYTE*)HeapAlloc(hHeap1, 0, 64000); // +96 bytes from start of heap
BYTE * b1 = (BYTE*)HeapAlloc(hHeap1, 0, 64000); // +16 bytes from end of a1
BYTE * c1 = (BYTE*)HeapAlloc(hHeap1, 0, 64000); // +16 bytes from end of b1
BYTE * d1 = (BYTE*)HeapAlloc(hHeap1, 0, 64000); // +4528 bytes from end of c1

HANDLE hHeap2 = HeapCreate(0, 4*1024*1024, 4*1024*1024); // 4MB Heap
BYTE * a2 = (BYTE*)HeapAlloc(hHeap2, 0, 64000); // +96 bytes from start of heap
BYTE * b2 = (BYTE*)HeapAlloc(hHeap2, 0, 64000); // +16 bytes from end of a2
BYTE * c2 = (BYTE*)HeapAlloc(hHeap2, 0, 64000); // +16 bytes from end of b2
BYTE * d2 = (BYTE*)HeapAlloc(hHeap2, 0, 64000); // +16 bytes from end of c2
+1  A: 

I wouldn't trust IsBadWritePtr, see this: http://support.microsoft.com/kb/960154 I think it does something like tries to write to the memory, and sees if it gets a hardware exception. It handles the exception and then returns true or false. But, I've heard that some hardware will only raise an exception once for an attempt to write to one page, or something like that.

Is it not possible for you to VirtualAlloc without a MEM_COMMIT, so you just reserve the virtual memory addresses. Then when you actually want to use the memory, call VirtualAlloc again to do the commit. As you already reserved the pages, you should be able to commit them contiguously.

You say you want to do it without reserving up front. But, what's wrong with reserving up front. Reserving doesn't actually use any physical memory, it reserves a range of virtual addresses, it'll only use physical memory when you commit the pages.

Scott Langham
I only used IsBadWritePtr in the demo code to show how the region of memory is considered invalid by WinCE. I wouldn't use it in production code.The problem with using VirtualAlloc to reserve a chunk of memory is that you are limited to 32MB and that is not all reserveable. On test PDAs the most I can reserve is 18MB and once that's done no other DLLs can be loaded etc, so realistically ~12MB can be reserved in one chunk. That's why I want to know if it is possible to extend the memory committed by VirtualAlloc (if the next page is available) and be able to use memory across that boundary.
Steven
A: 

Ah, ok. In that case have you heard of the large memory area, I think that's Microsoft's workaround/fix to your problem.

Basically, if you VirtualAlloc more than 2 meg, it goes into the large memory area of the virtual memory and this lets you exceed the 32mb limit.

There's quite a good discussion of how it all works here:
http://msdn.microsoft.com/en-us/library/ms836325.aspx

I used it myself on a previous product, and it did the trick for me.

Scott Langham
I have done that as a partial fix but code outside my control doesn't like it if I pass memory addresses in the shared memory area, see my queston (It is possible to reserve a larger amount of memory in the shared region but this also has other problems.)...So although using the large memory area fixes some of my problems it complicates others, which is why I was looking to see why two contiguous VirtualAllocs don't work and if there is a way to extend the VirtualAlloc. I am currently using a combo of large memory area + other but would prefer to simplify the code if at all possible.
Steven
+1  A: 

No, it's not possible.

Scott Langham
Steven
Hmm, yes. It's a shame there isn't a better way. Any other idea I've got I'm sure would work out to be more complicated than your current workaround. It would be nice for someone to prove us wrong though.
Scott Langham