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:
Matthew Parkinson
2021-10-20 09:31:45 +01:00
committed by Matthew Parkinson
parent c1062e629e
commit 20a114cb62
13 changed files with 343 additions and 72 deletions

View File

@@ -8,6 +8,7 @@
#include "pool.h"
#include "remotecache.h"
#include "sizeclasstable.h"
#include "ticker.h"
namespace snmalloc
{
@@ -96,6 +97,11 @@ namespace snmalloc
*/
LocalCache* attached_cache;
/**
* Ticker to query the clock regularly at a lower cost.
*/
Ticker<typename SharedStateHandle::Pal> ticker;
/**
* The message queue needs to be accessible from other threads
*
@@ -405,6 +411,7 @@ namespace snmalloc
std::cout << "Slab is woken up" << std::endl;
#endif
ticker.check_tick();
return;
}
@@ -419,6 +426,7 @@ namespace snmalloc
{
dealloc_local_slabs(sizeclass);
}
ticker.check_tick();
}
/**
@@ -698,7 +706,8 @@ namespace snmalloc
sl.insert(meta);
}
return finish_alloc<zero_mem, SharedStateHandle>(p, sizeclass);
auto r = finish_alloc<zero_mem, SharedStateHandle>(p, sizeclass);
return ticker.check_tick(r);
}
return small_alloc_slow<zero_mem>(sizeclass, fast_free_list, rsize);
}
@@ -766,7 +775,8 @@ namespace snmalloc
alloc_classes[sizeclass].available.insert(meta);
}
return finish_alloc<zero_mem, SharedStateHandle>(p, sizeclass);
auto r = finish_alloc<zero_mem, SharedStateHandle>(p, sizeclass);
return ticker.check_tick(r);
}
/**

97
src/mem/ticker.h Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#include "../ds/defines.h"
#include <cstdint>
namespace snmalloc
{
/**
* This class will attempt to call the PAL every 50ms to check the time.
* If the caller of check_tick, does so more frequently, it will attempt
* to back-off to only query the time, every n calls to check_tick, where
* `n` adapts to the current frequency of calling.
*
* The aim is to reduce the time spent querying the time as this might be
* an expensive operation if time has been virtualised.
*/
template<typename PAL>
class Ticker
{
/**
* Calls to check_tick required before the time is next queried
*/
uint64_t count_down = 1;
/**
* Number of ticks next time we check the time.
* That is,
* counted - count_down
* Is how many ticks, since last_epoch_ms was updated.
*/
uint64_t counted = 1;
/**
* Last time we queried the clock.
*/
uint64_t last_query_ms = 0;
/**
* Slow path that actually queries clock and sets up
* how many calls for the next time we hit the slow path.
*/
template<typename T = void*>
SNMALLOC_SLOW_PATH T check_tick_slow(T p = nullptr)
{
uint64_t now_ms = PAL::time_in_ms();
// Set up clock.
if (last_query_ms == 0)
{
last_query_ms = now_ms;
count_down = 1;
counted = 1;
return p;
}
uint64_t duration_ms = now_ms - last_query_ms;
last_query_ms = now_ms;
// Check is below clock resolution
if (duration_ms == 0)
{
// Exponential back off
count_down = counted;
counted *= 2;
return p;
}
constexpr size_t deadline_in_ms = 50;
// Estimate number of ticks to get to the new deadline, based on the
// current interval
auto new_deadline_in_ticks =
((1 + counted) * deadline_in_ms) / duration_ms;
counted = new_deadline_in_ticks;
count_down = new_deadline_in_ticks;
return p;
}
public:
template<typename T = void*>
SNMALLOC_FAST_PATH T check_tick(T p = nullptr)
{
// Check before decrement, so that later calcations can use
// count_down == 0 for check on the next call.
// This is used if the ticks are way below the frequency of
// heart beat.
if (--count_down == 0)
{
return check_tick_slow(p);
}
return p;
}
};
} // namespace snmalloc