When searching for something else in the header and documentation files, I recently discovered the following function prototype in <objc/objc-auto.h> :
void *objc_memmove_collectable(void *dst, const void *src, size_t size);
It is in the middle of a group of functions following the comment that says: "Write down barriers. Used by the compiler." These functions are used to inform the garbage collector that the memory it manages has been changed, so it can scan links and not accidentally return memory that must be rooted in a strong link that it does not know about. I know that this use of __strong should almost always force the compiler to insert the correct function, but I also heard Apple engineers say that there are extremely rare cases when you have to call such functions directly. (I believe that when a file is not compiled as Objective-C, for example, C code that is written to GC-managed memory, but I'm not sure.)
Unfortunately, Apple Objective-C Runtime Release Notes for Mac OS X v10.5 contains the following information about this feature: (last under the first heading)
"When copying bulk memory to the garbage collector, you should use the objc_memmove_collectable(void *dst, const void *src, size_t size) API objc_memmove_collectable(void *dst, const void *src, size_t size) ."
The function seems focused on moving items from non-GC memory to GC memory, and email message archives seem to suggest that the goal is to trigger only one write barrier for a large copy of the block. (For example, 1000 people record with a write barrier for each of them, compared to 1 mass copy with one write barrier for the entire memory area that is written to.) This is one instance when it should be used, but the documents donβt say anything about when it should not (or is not necessary).
For example, I have a block of memory that I allocated NSAllocateCollectable() and NSScannedOption , and I use it as a dynamically expanding circular buffer. If the buffer fills up, I double its size using NSReallocateCollectable() and NSScannedOption . The part that is wrapped (between the first slot in the array and the last object in the buffer) is copied / moved to the beginning of the second half of the array. Then I bzero() slots in which the data was copied to avoid over-rooting of the moved objects. (See Lines 460-467 in this file . Yes, the code works the same way as it is - it is fully tested, and I did not see any crashes since I added the __strong attribute some time ago.)
Questions:
- When is it necessary to use
objc_memmove_collectable() instead of memmove() or memcpy() ? - For example, what if the source and destination are GC-managed memory? (My memory is declared as
__strong id *array; so I assume the compiler inserts a barrier.) - If this is not necessary, will it help / hinder the work of the GC? (For example, does it hold a lock or helps the GC avoid manual scans?) Is it good / white?
Edit: Since memcpy and memmove do not interact with the GC at all, I was wondering why I did not see any failures from the memory collected from under me. My best guess at this point is that since bzero also does not tell the GC anything, the collector will not know about zeroed memory and moved data until the next time it scans the entire memory block. If the collector still considers null references as roots and does not yet count new memory cells, this explains why the values ββare not received prematurely. Does this sound right?