Add a timer to the PAL
This adds a way to periodically pool the PAL to see if any timers have expired. Timers can be used to periodically provide callbacks to the rest of snmalloc.
This commit is contained in:
committed by
Matthew Parkinson
parent
c1062e629e
commit
20a114cb62
@@ -3,7 +3,7 @@
|
||||
#ifdef __cpp_concepts
|
||||
# include "../ds/concept.h"
|
||||
# include "pal_consts.h"
|
||||
|
||||
# include "pal_ds.h"
|
||||
# include <utility>
|
||||
|
||||
namespace snmalloc
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../ds/defines.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
@@ -85,70 +86,7 @@ namespace snmalloc
|
||||
/**
|
||||
* Default Tag ID for the Apple class
|
||||
*/
|
||||
static const uint8_t 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 = nullptr;
|
||||
|
||||
void (*pal_notify)(PalNotificationObject* self);
|
||||
|
||||
PalNotificationObject(void (*pal_notify)(PalNotificationObject* self))
|
||||
: pal_notify(pal_notify)
|
||||
{}
|
||||
};
|
||||
|
||||
/***
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
static const int PALAnonDefaultID = 241;
|
||||
|
||||
/**
|
||||
* Query whether the PAL supports a specific feature.
|
||||
|
||||
163
src/pal/pal_ds.h
Normal file
163
src/pal/pal_ds.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ds/defines.h"
|
||||
#include "../ds/helpers.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
template<typename T>
|
||||
class PalList
|
||||
{
|
||||
/**
|
||||
* List of callbacks to notify
|
||||
*/
|
||||
std::atomic<T*> elements{nullptr};
|
||||
|
||||
static_assert(
|
||||
std::is_same<decltype(T::pal_next), std::atomic<T*>>::value,
|
||||
"Required pal_next type.");
|
||||
|
||||
public:
|
||||
/**
|
||||
* Add an element to the list
|
||||
*/
|
||||
void add(T* element)
|
||||
{
|
||||
auto prev = &elements;
|
||||
auto curr = prev->load();
|
||||
do
|
||||
{
|
||||
while (curr != nullptr)
|
||||
{
|
||||
prev = &(curr->pal_next);
|
||||
curr = prev->load();
|
||||
}
|
||||
} while (!prev->compare_exchange_weak(curr, element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies function to all the elements of the list
|
||||
*/
|
||||
void apply_all(function_ref<void(T*)> func)
|
||||
{
|
||||
T* curr = elements;
|
||||
while (curr != nullptr)
|
||||
{
|
||||
func(curr);
|
||||
curr = curr->pal_next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = nullptr;
|
||||
|
||||
void (*pal_notify)(PalNotificationObject* self);
|
||||
|
||||
PalNotificationObject(void (*pal_notify)(PalNotificationObject* self))
|
||||
: pal_notify(pal_notify)
|
||||
{}
|
||||
};
|
||||
|
||||
/***
|
||||
* Wrapper for managing notifications for PAL events
|
||||
*/
|
||||
class PalNotifier
|
||||
{
|
||||
/**
|
||||
* List of callbacks to notify
|
||||
*/
|
||||
PalList<PalNotificationObject> callbacks;
|
||||
|
||||
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)
|
||||
{
|
||||
callbacks.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the pal_notify of all the registered objects.
|
||||
*/
|
||||
void notify_all()
|
||||
{
|
||||
callbacks.apply_all([](auto curr) { curr->pal_notify(curr); });
|
||||
}
|
||||
};
|
||||
|
||||
class PalTimerObject
|
||||
{
|
||||
friend class PalTimer;
|
||||
template<typename T>
|
||||
friend class PalList;
|
||||
|
||||
std::atomic<PalTimerObject*> pal_next;
|
||||
|
||||
void (*pal_notify)(PalTimerObject* self);
|
||||
|
||||
uint64_t last_run = 0;
|
||||
uint64_t repeat;
|
||||
|
||||
public:
|
||||
PalTimerObject(void (*pal_notify)(PalTimerObject* self), uint64_t repeat)
|
||||
: pal_notify(pal_notify), repeat(repeat)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple mechanism for handling timers.
|
||||
*
|
||||
* Note: This is really designed for a very small number of timers,
|
||||
* and this design should be changed if that is no longer the case.
|
||||
*/
|
||||
class PalTimer
|
||||
{
|
||||
/**
|
||||
* List of callbacks to notify
|
||||
*/
|
||||
PalList<PalTimerObject> timers;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Register a callback to be called every repeat milliseconds.
|
||||
*/
|
||||
void register_timer(PalTimerObject* timer)
|
||||
{
|
||||
timers.add(timer);
|
||||
}
|
||||
|
||||
void check(uint64_t time_ms)
|
||||
{
|
||||
static std::atomic_flag lock = ATOMIC_FLAG_INIT;
|
||||
|
||||
// Depulicate calls into here, and make single threaded.
|
||||
if (lock.test_and_set())
|
||||
{
|
||||
timers.apply_all([time_ms](PalTimerObject* curr) {
|
||||
if (
|
||||
(curr->last_run == 0) ||
|
||||
((time_ms - curr->last_run) > curr->repeat))
|
||||
{
|
||||
curr->last_run = time_ms;
|
||||
curr->pal_notify(curr);
|
||||
}
|
||||
});
|
||||
lock.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../aal/aal.h"
|
||||
#include "pal_concept.h"
|
||||
#include "pal_consts.h"
|
||||
#include "pal_timer_default.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -27,7 +28,7 @@ namespace snmalloc
|
||||
* ever use.
|
||||
*/
|
||||
template<SNMALLOC_CONCEPT(PALNoAllocBase) BasePAL>
|
||||
struct PALNoAlloc
|
||||
struct PALNoAlloc : public PalTimerDefaultImpl<BasePAL>
|
||||
{
|
||||
/**
|
||||
* Bitmap of PalFeatures flags indicating the optional features that this
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ds/bits.h"
|
||||
#include "pal_timer_default.h"
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
// Can be extended
|
||||
// Will require a reserve method in subclasses.
|
||||
template<class State>
|
||||
class PALPlainMixin : public State
|
||||
class PALPlainMixin : public State, public PalTimerDefaultImpl<State>
|
||||
{
|
||||
public:
|
||||
// Notify platform that we will not be using these pages
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# include <iostream>
|
||||
#endif
|
||||
#include "../ds/address.h"
|
||||
#include "pal_timer_default.h"
|
||||
#if defined(SNMALLOC_BACKTRACE_HEADER)
|
||||
# include SNMALLOC_BACKTRACE_HEADER
|
||||
#endif
|
||||
@@ -39,7 +40,7 @@ namespace snmalloc
|
||||
* version.
|
||||
*/
|
||||
template<class OS>
|
||||
class PALPOSIX
|
||||
class PALPOSIX : public PalTimerDefaultImpl<PALPOSIX<OS>>
|
||||
{
|
||||
/**
|
||||
* Helper class to access the `default_mmap_flags` field of `OS` if one
|
||||
@@ -348,5 +349,19 @@ namespace snmalloc
|
||||
}
|
||||
error("Entropy requested on platform that does not provide entropy");
|
||||
}
|
||||
|
||||
static uint64_t internal_time_in_ms()
|
||||
{
|
||||
auto hold = KeepErrno();
|
||||
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
|
||||
{
|
||||
error("Failed to get time");
|
||||
}
|
||||
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * 1000) +
|
||||
(static_cast<uint64_t>(ts.tv_nsec) / 1000000);
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
|
||||
32
src/pal/pal_timer_default.h
Normal file
32
src/pal/pal_timer_default.h
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pal_consts.h"
|
||||
#include "pal_ds.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
template<typename PalTime>
|
||||
class PalTimerDefaultImpl
|
||||
{
|
||||
inline static PalTimer timers{};
|
||||
|
||||
public:
|
||||
static uint64_t time_in_ms()
|
||||
{
|
||||
auto time = PalTime::internal_time_in_ms();
|
||||
|
||||
// Process timers
|
||||
timers.check(time);
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
static void register_timer(PalTimerObject* timer)
|
||||
{
|
||||
timers.register_timer(timer);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../ds/address.h"
|
||||
#include "../ds/bits.h"
|
||||
#include "pal_timer_default.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef _MSC_VER
|
||||
@@ -22,9 +23,11 @@
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# include <chrono>
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
class PALWindows
|
||||
class PALWindows : public PalTimerDefaultImpl<PALWindows>
|
||||
{
|
||||
/**
|
||||
* A flag indicating that we have tried to register for low-memory
|
||||
@@ -206,6 +209,14 @@ namespace snmalloc
|
||||
error("Failed to get entropy.");
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint64_t internal_time_in_ms()
|
||||
{
|
||||
return static_cast<uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user