Make Lazy Decomit asynchronous
On platforms that support low-memory notifications register callbacks that perform lazy decommit. This allows idle processes to return memory to the OS. Without incurring the cost of constantly committing and decommitting memory. Code review and CI changes * Fixed test to use a template to make constexpr magic work * Factored out basic notification mechanism so can be reused on other platforms.
This commit is contained in:
@@ -10,8 +10,8 @@ namespace snmalloc
|
||||
{
|
||||
/**
|
||||
* This PAL supports low memory notifications. It must implement a
|
||||
* `low_memory_epoch` method that returns a `uint64_t` of the number of
|
||||
* times that a low-memory notification has been raised and an
|
||||
* `register_for_low_memory_callback` method that allows callbacks to be
|
||||
* registered when the platform detects low-memory and an
|
||||
* `expensive_low_memory_check()` method that returns a `bool` indicating
|
||||
* whether low memory conditions are still in effect.
|
||||
*/
|
||||
@@ -53,4 +53,63 @@ namespace snmalloc
|
||||
* Default Tag ID for the Apple class
|
||||
*/
|
||||
static const int PALAnonDefaultID = 241;
|
||||
|
||||
/**
|
||||
* This struct is used to represent callbacks for notification from the
|
||||
* platform. It contains a next pointer as client is responsible for
|
||||
* allocation as we cannot assume an allocator at this point.
|
||||
**/
|
||||
struct PalNotificationObject
|
||||
{
|
||||
std::atomic<PalNotificationObject*> pal_next;
|
||||
|
||||
void (*pal_notify)(PalNotificationObject* self);
|
||||
};
|
||||
|
||||
/***
|
||||
* Wrapper for managing notifications for PAL events
|
||||
**/
|
||||
class PalNotifier
|
||||
{
|
||||
/**
|
||||
* List of callbacks to notify
|
||||
**/
|
||||
std::atomic<PalNotificationObject*> callbacks = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Register a callback object to be notified
|
||||
*
|
||||
* The object should never be deallocated by the client after calling
|
||||
* this.
|
||||
**/
|
||||
void register_notification(PalNotificationObject* callback)
|
||||
{
|
||||
callback->pal_next = nullptr;
|
||||
|
||||
auto prev = &callbacks;
|
||||
auto curr = prev->load();
|
||||
do
|
||||
{
|
||||
while (curr != nullptr)
|
||||
{
|
||||
prev = &(curr->pal_next);
|
||||
curr = prev->load();
|
||||
}
|
||||
} while (!prev->compare_exchange_weak(curr, callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the pal_notify of all the registered objects.
|
||||
**/
|
||||
void notify_all()
|
||||
{
|
||||
PalNotificationObject* curr = callbacks;
|
||||
while (curr != nullptr)
|
||||
{
|
||||
curr->pal_notify(curr);
|
||||
curr = curr->pal_next;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
|
||||
@@ -20,23 +20,25 @@ namespace snmalloc
|
||||
{
|
||||
class PALWindows
|
||||
{
|
||||
/**
|
||||
* The number of times that the memory pressure notification has fired.
|
||||
*/
|
||||
static inline std::atomic<uint64_t> pressure_epoch;
|
||||
/**
|
||||
* A flag indicating that we have tried to register for low-memory
|
||||
* notifications.
|
||||
*/
|
||||
static inline std::atomic<bool> registered_for_notifications;
|
||||
static inline HANDLE lowMemoryObject;
|
||||
|
||||
/**
|
||||
* List of callbacks for low-memory notification
|
||||
**/
|
||||
static inline PalNotifier low_memory_callbacks;
|
||||
|
||||
/**
|
||||
* Callback, used when the system delivers a low-memory notification. This
|
||||
* simply increments an atomic counter each time the notification is raised.
|
||||
* calls all the handlers registered with the PAL.
|
||||
*/
|
||||
static void CALLBACK low_memory(_In_ PVOID, _In_ BOOLEAN)
|
||||
{
|
||||
pressure_epoch++;
|
||||
low_memory_callbacks.notify_all();
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -76,17 +78,6 @@ namespace snmalloc
|
||||
# endif
|
||||
;
|
||||
|
||||
/**
|
||||
* Counter values for the number of times that a low-pressure notification
|
||||
* has been delivered. Callers should compare this with a previous value
|
||||
* to see if the low memory state has been triggered since they last
|
||||
* checked.
|
||||
*/
|
||||
uint64_t low_memory_epoch()
|
||||
{
|
||||
return pressure_epoch.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the low memory state is still in effect. This is an
|
||||
* expensive operation and should not be on any fast paths.
|
||||
@@ -98,6 +89,17 @@ namespace snmalloc
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register callback object for low-memory notifications.
|
||||
* Client is responsible for allocation, and ensuring the object is live
|
||||
* for the duration of the program.
|
||||
**/
|
||||
static void
|
||||
register_for_low_memory_callback(PalNotificationObject* callback)
|
||||
{
|
||||
low_memory_callbacks.register_notification(callback);
|
||||
}
|
||||
|
||||
static void error(const char* const str)
|
||||
{
|
||||
puts(str);
|
||||
|
||||
Reference in New Issue
Block a user