You never need to call the
HeapLock andHeapUnlock functions under normal operation.Assuming the heap is serialized
(which is the default),
all the standard heap functions like
HeapAllocate and HeapFreewill automatically serialize.
In fact, the way they serialize is by calling
the¹
HeapLock andHeapUnlock functions!Nearly all heap operations complete in a single call.
If your heap is serialized, this means that the heap operation
takes the heap lock,
does its work,
and then releases the heap lock and returns.
If all heap operations were like this,
then there would be no need for
HeapLock orHeapUnlock.Unfortunately, there is also the
HeapWalk function,which does a little bit of work, and then returns
with a partial result.
The design for
HeapWalk is thatthe application calls the function repeatedly
until it either gets all the results it wants, or gets bored and gives up.
But wait, what if the heap changes while the application is
trying to walk through it?
To prevent that from happening,
the program can call
HeapLock before starting the enumeration,and
HeapUnlock when it is done.During the time the heap is locked,
other threads which attempt to call a
HeapXxx function with that same heapwill block until the heap is unlocked.
The ability to lock the heap creates a lot of potential for craziness,
because the heap is a high-traffic area.
As a result, it is very important that any code which calls
HeapLock do very little while the lock is held.Take the lock, do your thing, and get out quickly.
But wait, there's more.
Holding the heap lock blocks all other threads from
allocating or freeing memory.
This puts the heap lock very low in your lock hierarchy.
Therefore,
while you hold the heap lock,
you cannot block on synchronization objects whose owners might
try to access the heap you just locked.
Consider the following:
// Code in italics is wrong.Sending a message is a big deal.
void BadIdea()
{
HeapLock(GetProcessHeap());
SendMessage(...);
HeapUnlock(GetProcessHeap());
}
The thread that is the target of the message
may be waiting for the heap lock,
and now you've created a deadlock.
You won't proceed until that thread processes the message,
but that thread can't process the message until you unlock the heap.
Bonus chatter
Note that if you call
HeapLock or HeapUnlockon a heap that was created without serialization
(
HEAP_NO_SERIALIZATION),then the results are undefined.
That's because passing the
HEAP_NO_SERIALIZATION flag means"Hey, Heap Manager,
don't bother locking this heap.
I will take responsibility for ensuring that only one thread
operates on this heap at a time."
If you later call
HeapLock on a no-serialization heap,the heap manager will say,
"Wha?
You said that you would take care of serialization, not me!"
It's like ordering a car and saying,
"Don't bother installing door locks.
I will take responsibility for ensuring the safety of the car.
(Say, by never letting the car leave a secured facility.)"
And then a month later, calling OnStar and saying,
"Hi, can you remotely lock my car for me? Thanks."
Dude, you explicitly opted out of door locks.
(Amazingly, I encountered one developer who thought that
calling
HeapLock on a no-serialization heapwould cause other heap operations on the heap to be blocked,
even if they passed the
HEAP_NO_SERIALIZATION flag to those operations.Um, no, the
HeapLock function cannot lock ano-serialization heap because a no-serialization heap doesn't have
lock in the first place, at your request.)
Nitpicker's corner
¹ s/the/the functional equivalents of/
