Mozilla 2/Memory/OOM API: Difference between revisions
m (reserve_cur() --> reserve_current()) |
(Revise based on bug #427109 comments #19 and #26) |
||
Line 27: | Line 27: | ||
== Memory reserve API == | == Memory reserve API == | ||
The allocator | <pre>/* | ||
* The allocator maintains a memory reserve that is used to satisfy allocation | |||
* requests when no additional memory can be acquired from the operating | |||
* system. Under normal operating conditions, the reserve size is at least | |||
* reserve_min bytes. If the reserve is depleted or insufficient to satisfy an | |||
* allocation request, then condition notifications are sent to one or more of | |||
* the registered callback functions: | |||
* | |||
* RESERVE_CND_LOW: The reserve had to be used to satisfy an allocation | |||
* request, which dropped the reserve size below the | |||
* minimum. The callee should try to free memory in order | |||
* to restore the reserve. | |||
* | |||
* RESERVE_CND_CRIT: The reserve was not large enough to satisfy a pending | |||
* allocation request. The callee must free adequate memory | |||
* in order to prevent application failure. | |||
* | |||
* RESERVE_CND_FAIL: An allocation request could not be satisfied, despite all | |||
* attempts. The allocator is about to terminate the | |||
* application. | |||
* | |||
* The order in which the callback functions are called is only loosely | |||
* specified: in the absence of interposing callback | |||
* registrations/unregistrations or condition notification mask modifications, | |||
* enabled callbacks will be called in an arbitrary round-robin order. | |||
* | |||
* Condition notifications are sent to callbacks only while conditions exist. | |||
* For example, just before the allocator sends a RESERVE_CND_LOW condition | |||
* notification to a callback, the reserve is in fact depleted. However, due | |||
* to allocator concurrency, the reserve may have been restored by the time the | |||
* callback function executes. Furthermore, if the reserve is restored at some | |||
* point during the delivery of condition notifications to callbacks, no | |||
* further deliveries will occur, since the condition no longer exists. | |||
* | |||
* Callback functions can freely call back into the allocator (i.e. the | |||
* allocator releases all internal resources before calling each callback | |||
* function), though use of the x*() functions is discouraged, since recursive | |||
* callbacks are likely to result (which places extra burden on the application | |||
* to avoid deadlocking). | |||
*/ | |||
/* Memory reserve condition types. */ | |||
typedef enum { | |||
RESERVE_CND_NONE = 0x0, /* No condition. */ | |||
RESERVE_CND_LOW = 0x1, | |||
RESERVE_CND_CRIT = 0x2, | |||
RESERVE_CND_FAIL = 0x4, | |||
RESERVE_CND_ALL = 0x7 /* Union of all conditions. */ | |||
} reserve_cnd_t; | |||
/* | |||
* Reserve condition notification callback function type definition. | |||
* | |||
* Inputs: | |||
* ctx: Opaque application data, as passed to reserve_cb_register(). | |||
* cnd: Condition type being delivered. | |||
* size: Allocation request size for the allocation that caused the condition. | |||
* | |||
* Output: | |||
* ret: If false, block further callbacks to the callee for the condition type | |||
* currently being delivered. | |||
*/ | |||
typedef bool reserve_cb_t(void *ctx, reserve_cnd_t cnd, size_t size); | |||
/* | |||
* Register a callback function. | |||
* | |||
* Inputs: | |||
* cb: Callback function pointer. | |||
* ctx: Opaque application data, passed to cb(). | |||
* mask: Mask of condition types to deliver to the callback function. To | |||
* receive callbacks for a condition type, include it in the mask. | |||
*/ | |||
void reserve_cb_register(reserve_cb_t *cb, void *ctx, reserve_cnd_t mask); | |||
/* Unregister a callback function. */ | |||
void reserve_cb_unregister(reserve_cb_t *cb); | |||
/* | |||
* Get the mask of condition types that are currently enabled for a callback | |||
* function. | |||
* | |||
* Input: | |||
* cb: Callback function pointer. | |||
* | |||
* Output: | |||
* ret: Mask of condition types that may be delivered to cb. | |||
*/ | |||
reserve_cnd_t reserve_cb_getmask(reserve_cb_t *cb); | |||
/* | |||
* Set the mask of conditions that may be delivered to a callback. | |||
* | |||
* Inputs: | |||
* cb: Callback function pointer. | |||
* mask: Mask of condition types to deliver to the callback function. To | |||
* receive callbacks for a condition type, include it in the mask. | |||
* | |||
* Output: | |||
* ret: Previous mask of condition types that were to be delivered to the | |||
* callback function. | |||
*/ | |||
reserve_cnd_t reserve_cb_setmask(reserve_cb_t *cb, reserve_cnd_t mask); | |||
/* | |||
* Enable delivery of condition types that are included in mask. | |||
* | |||
* Inputs: | |||
* cb: Callback function pointer. | |||
* mask: Mask of condition types to merge into the current set of enabled | |||
* condition types. | |||
* | |||
* Output: | |||
* ret: Previous mask of condition types that were to be delivered to the | |||
* callback function. | |||
*/ | |||
reserve_cnd_t reserve_cb_enable(reserve_cb_t *cb, reserve_cnd_t mask); | |||
/* | |||
* Disable delivery of condition types that are included in mask. | |||
* | |||
* Inputs: | |||
* cb: Callback function pointer. | |||
* mask: Mask of condition types to remove from current set of enabled | |||
* condition types. | |||
* | |||
* Output: | |||
* ret: Previous mask of condition types that were to be delivered to the | |||
* callback function. | |||
*/ | |||
reserve_cnd_t reserve_cb_disable(reserve_cb_t *cb, reserve_cnd_t mask); | |||
/* | |||
* Get the current reserve size. | |||
* | |||
* ret: Current reserve size. | |||
*/ | |||
size_t reserve_cur_get(void); | |||
/* | |||
* Get the minimum acceptable reserve size. If the reserve drops below this | |||
* value, the RESERVE_CND_LOW condition notification is sent to the callbacks. | |||
* | |||
* ret: Minimum acceptable reserve size. | |||
*/ | |||
size_t reserve_min_get(void); | |||
/* | |||
* Set the minimum acceptable reserve size. | |||
* | |||
* min: Reserve threshold. | |||
* ret: False if the reserve was successfully resized; true otherwise. Note | |||
* that failure to resize the reserve also results in a RESERVE_CND_LOW | |||
* condition. | |||
*/ | |||
bool reserve_min_set(size_t min); | |||
</pre> | |||
== Implementation (non-)details == | == Implementation (non-)details == |
Revision as of 04:39, 22 May 2008
Rationale
Memory allocation is an integral part of any non-trivial application, and in principle any allocation can fail due to being Out Of Memory (OOM). The traditional approach to OOM handling is to check for failure at every allocation point in the application and execute recovery code. Unfortunately, such recovery can be quite complex, depending on the surrounding code. Even worse, actual OOM conditions are rare, so the recovery code is very difficult to adequately test.
We need to improve our OOM handling approach so that it is possible to reliably and efficiently run with constrained memory resources. This is particularly important when running on mobile platforms.
Standard can-fail API
The standard memory allocation functions can fail due to an OOM condition. If failure occurs, each function sets the return value accordingly, and it is up to the caller to deal appropriately with the error.
void *malloc(size_t size);
void *valloc(size_t size); /* Legacy compatibility function. */
void *calloc(size_t num, size_t size);
void *realloc(void *ptr, size_t size);
int posix_memalign(void **memptr, size_t alignment, size_t size);
void *memalign(size_t alignment, size_t size);
New no-fail API
The no-fail functions will either succeed or terminate the application before returning. See later sections for related information on internal mechanisms that are irrelevant to the caller of these functions. There is a long history of using xmalloc(), xrealloc(), etc. as no-fail wrappers around the standard allocation functions, which motivates the function naming. xvalloc() and xposix_memalign() are omitted since xmemalign() suffices.
void *xmalloc(size_t size);
void *xcalloc(size_t num, size_t size);
void *xrealloc(void *ptr, size_t size);
void *xmemalign(size_t alignment, size_t size);
Memory reserve API
/* * The allocator maintains a memory reserve that is used to satisfy allocation * requests when no additional memory can be acquired from the operating * system. Under normal operating conditions, the reserve size is at least * reserve_min bytes. If the reserve is depleted or insufficient to satisfy an * allocation request, then condition notifications are sent to one or more of * the registered callback functions: * * RESERVE_CND_LOW: The reserve had to be used to satisfy an allocation * request, which dropped the reserve size below the * minimum. The callee should try to free memory in order * to restore the reserve. * * RESERVE_CND_CRIT: The reserve was not large enough to satisfy a pending * allocation request. The callee must free adequate memory * in order to prevent application failure. * * RESERVE_CND_FAIL: An allocation request could not be satisfied, despite all * attempts. The allocator is about to terminate the * application. * * The order in which the callback functions are called is only loosely * specified: in the absence of interposing callback * registrations/unregistrations or condition notification mask modifications, * enabled callbacks will be called in an arbitrary round-robin order. * * Condition notifications are sent to callbacks only while conditions exist. * For example, just before the allocator sends a RESERVE_CND_LOW condition * notification to a callback, the reserve is in fact depleted. However, due * to allocator concurrency, the reserve may have been restored by the time the * callback function executes. Furthermore, if the reserve is restored at some * point during the delivery of condition notifications to callbacks, no * further deliveries will occur, since the condition no longer exists. * * Callback functions can freely call back into the allocator (i.e. the * allocator releases all internal resources before calling each callback * function), though use of the x*() functions is discouraged, since recursive * callbacks are likely to result (which places extra burden on the application * to avoid deadlocking). */ /* Memory reserve condition types. */ typedef enum { RESERVE_CND_NONE = 0x0, /* No condition. */ RESERVE_CND_LOW = 0x1, RESERVE_CND_CRIT = 0x2, RESERVE_CND_FAIL = 0x4, RESERVE_CND_ALL = 0x7 /* Union of all conditions. */ } reserve_cnd_t; /* * Reserve condition notification callback function type definition. * * Inputs: * ctx: Opaque application data, as passed to reserve_cb_register(). * cnd: Condition type being delivered. * size: Allocation request size for the allocation that caused the condition. * * Output: * ret: If false, block further callbacks to the callee for the condition type * currently being delivered. */ typedef bool reserve_cb_t(void *ctx, reserve_cnd_t cnd, size_t size); /* * Register a callback function. * * Inputs: * cb: Callback function pointer. * ctx: Opaque application data, passed to cb(). * mask: Mask of condition types to deliver to the callback function. To * receive callbacks for a condition type, include it in the mask. */ void reserve_cb_register(reserve_cb_t *cb, void *ctx, reserve_cnd_t mask); /* Unregister a callback function. */ void reserve_cb_unregister(reserve_cb_t *cb); /* * Get the mask of condition types that are currently enabled for a callback * function. * * Input: * cb: Callback function pointer. * * Output: * ret: Mask of condition types that may be delivered to cb. */ reserve_cnd_t reserve_cb_getmask(reserve_cb_t *cb); /* * Set the mask of conditions that may be delivered to a callback. * * Inputs: * cb: Callback function pointer. * mask: Mask of condition types to deliver to the callback function. To * receive callbacks for a condition type, include it in the mask. * * Output: * ret: Previous mask of condition types that were to be delivered to the * callback function. */ reserve_cnd_t reserve_cb_setmask(reserve_cb_t *cb, reserve_cnd_t mask); /* * Enable delivery of condition types that are included in mask. * * Inputs: * cb: Callback function pointer. * mask: Mask of condition types to merge into the current set of enabled * condition types. * * Output: * ret: Previous mask of condition types that were to be delivered to the * callback function. */ reserve_cnd_t reserve_cb_enable(reserve_cb_t *cb, reserve_cnd_t mask); /* * Disable delivery of condition types that are included in mask. * * Inputs: * cb: Callback function pointer. * mask: Mask of condition types to remove from current set of enabled * condition types. * * Output: * ret: Previous mask of condition types that were to be delivered to the * callback function. */ reserve_cnd_t reserve_cb_disable(reserve_cb_t *cb, reserve_cnd_t mask); /* * Get the current reserve size. * * ret: Current reserve size. */ size_t reserve_cur_get(void); /* * Get the minimum acceptable reserve size. If the reserve drops below this * value, the RESERVE_CND_LOW condition notification is sent to the callbacks. * * ret: Minimum acceptable reserve size. */ size_t reserve_min_get(void); /* * Set the minimum acceptable reserve size. * * min: Reserve threshold. * ret: False if the reserve was successfully resized; true otherwise. Note * that failure to resize the reserve also results in a RESERVE_CND_LOW * condition. */ bool reserve_min_set(size_t min);
Implementation (non-)details
Internally, the allocator may manage the reserve as non-contiguous chunks of memory. This means that, for example, even with a reserve of 10MiB, the allocator may fail to allocate a 2MiB object. Similarly, even if the application makes 10MiB of non-contiguous memory available, the reserve may not increase at all, due to memory fragmentation.
If the reserve threshold is not a multiple of the allocator's internal chunk size, the actual threshold may effectively be rounded up to the nearest multiple of the chunk size. This only matters in that it limits the effective granularity with which the reserve can be configured. The chunk size for jemalloc can be queried via jemalloc_stats()[1].