diff --git a/mps/code/arena.c b/mps/code/arena.c index 860715c5059..ec235c995a2 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -149,6 +149,7 @@ Bool ArenaCheck(Arena arena) */ CHECKL(arena->committed <= arena->commitLimit); CHECKL(arena->spareCommitted <= arena->committed); + CHECKL(0.0 <= arena->pauseTime); CHECKL(ShiftCheck(arena->zoneShift)); CHECKL(ArenaGrainSizeCheck(arena->grainSize)); @@ -199,6 +200,7 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) Bool zoned = ARENA_DEFAULT_ZONED; Size commitLimit = ARENA_DEFAULT_COMMIT_LIMIT; Size spareCommitLimit = ARENA_DEFAULT_SPARE_COMMIT_LIMIT; + double pauseTime = ARENA_DEFAULT_PAUSE_TIME; mps_arg_s arg; AVER(arena != NULL); @@ -211,6 +213,8 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) commitLimit = arg.val.size; if (ArgPick(&arg, args, MPS_KEY_SPARE_COMMIT_LIMIT)) spareCommitLimit = arg.val.size; + if (ArgPick(&arg, args, MPS_KEY_PAUSE_TIME)) + pauseTime = arg.val.d; arena->class = class; @@ -219,6 +223,7 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) arena->commitLimit = commitLimit; arena->spareCommitted = (Size)0; arena->spareCommitLimit = spareCommitLimit; + arena->pauseTime = pauseTime; arena->grainSize = grainSize; /* zoneShift is usually overridden by init */ arena->zoneShift = ARENA_ZONESHIFT; @@ -294,6 +299,7 @@ ARG_DEFINE_KEY(ARENA_SIZE, Size); ARG_DEFINE_KEY(ARENA_ZONED, Bool); ARG_DEFINE_KEY(COMMIT_LIMIT, Size); ARG_DEFINE_KEY(SPARE_COMMIT_LIMIT, Size); +ARG_DEFINE_KEY(PAUSE_TIME, double); static Res arenaFreeLandInit(Arena arena) { @@ -1242,6 +1248,20 @@ void ArenaSetSpareCommitLimit(Arena arena, Size limit) EVENT2(SpareCommitLimitSet, arena, limit); } +double ArenaPauseTime(Arena arena) +{ + AVERT(Arena, arena); + return arena->pauseTime; +} + +void ArenaSetPauseTime(Arena arena, double pauseTime) +{ + AVERT(Arena, arena); + AVER(0.0 <= pauseTime); + arena->pauseTime = pauseTime; + EVENT2(PauseTimeSet, arena, pauseTime); +} + /* Used by arenas which don't use spare committed memory */ Size ArenaNoPurgeSpare(Arena arena, Size size) { diff --git a/mps/code/arg.c b/mps/code/arg.c index 4721c415ccb..82f936b92cb 100644 --- a/mps/code/arg.c +++ b/mps/code/arg.c @@ -93,8 +93,8 @@ Bool ArgCheckRank(Arg arg) { } Bool ArgCheckdouble(Arg arg) { - /* It would be nice if we could check doubles with C89, but - it doesn't have isfinite() etc. which are in C99. */ + /* Don't call isfinite() here because it's not in C89, and because + infinity is a valid value for MPS_KEY_PAUSE_TIME. */ UNUSED(arg); return TRUE; } diff --git a/mps/code/config.h b/mps/code/config.h index 11d5a943a04..5c751ca5636 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -408,9 +408,14 @@ /* TODO: This should be proportional to the memory usage of the MPS, not * a constant. That will require design, and then some interface and - * documenation changes. */ + * documentation changes. */ #define ARENA_DEFAULT_SPARE_COMMIT_LIMIT ((Size)10uL*1024uL*1024uL) +/* ARENA_DEFAULT_PAUSE_TIME is the maximum time (in seconds) that the + * arena that operations within the arena may pause the mutator for. */ + +#define ARENA_DEFAULT_PAUSE_TIME (0.0) + #define ARENA_DEFAULT_ZONED TRUE /* ARENA_MINIMUM_COLLECTABLE_SIZE is the minimum size (in bytes) of diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 081b45de7c2..c24fdbad2e6 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -36,7 +36,7 @@ */ #define EVENT_VERSION_MAJOR ((unsigned)1) -#define EVENT_VERSION_MEDIAN ((unsigned)4) +#define EVENT_VERSION_MEDIAN ((unsigned)5) #define EVENT_VERSION_MINOR ((unsigned)0) @@ -67,7 +67,7 @@ */ #define EventNameMAX ((size_t)19) -#define EventCodeMAX ((EventCode)0x0086) +#define EventCodeMAX ((EventCode)0x0087) #define EVENT_LIST(EVENT, X) \ /* 0123456789012345678 <- don't exceed without changing EventNameMAX */ \ @@ -192,7 +192,8 @@ EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \ EVENT(X, ArenaGenZoneAdd , 0x0084, TRUE, Arena) \ EVENT(X, ArenaUseFreeZone , 0x0085, TRUE, Arena) \ - /* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */ + /* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */ \ + EVENT(X, PauseTimeSet , 0x0087, TRUE, Arena) /* Remember to update EventNameMAX and EventCodeMAX above! @@ -740,6 +741,10 @@ PARAM(X, 0, P, arena) /* the arena */ \ PARAM(X, 1, W, zoneSet) /* zones that aren't free any longer */ +#define EVENT_PauseTimeSet_PARAMS(PARAM, X) \ + PARAM(X, 0, P, arena) /* the arena */ \ + PARAM(X, 1, D, pauseTime) /* the new maximum pause time, in seconds */ + #endif /* eventdef_h */ diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 118d8ff070e..4ee3453a0fc 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -621,6 +621,8 @@ extern Size ArenaCommitLimit(Arena arena); extern Res ArenaSetCommitLimit(Arena arena, Size limit); extern Size ArenaSpareCommitLimit(Arena arena); extern void ArenaSetSpareCommitLimit(Arena arena, Size limit); +extern double ArenaPauseTime(Arena arena); +extern void ArenaSetPauseTime(Arena arena, double pauseTime); extern Size ArenaNoPurgeSpare(Arena arena, Size size); extern Res ArenaNoGrow(Arena arena, LocusPref pref, Size size); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 8b76dec162a..b6c1914de6b 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -719,6 +719,7 @@ typedef struct mps_arena_s { Size spareCommitted; /* Amount of memory in hysteresis fund */ Size spareCommitLimit; /* Limit on spareCommitted */ + double pauseTime; /* Maximum pause time, in seconds. */ Shift zoneShift; /* see also */ Size grainSize; /* */ diff --git a/mps/code/mps.h b/mps/code/mps.h index 05b2644ec8e..7526d629945 100644 --- a/mps/code/mps.h +++ b/mps/code/mps.h @@ -189,6 +189,9 @@ extern const struct mps_key_s _mps_key_COMMIT_LIMIT; extern const struct mps_key_s _mps_key_SPARE_COMMIT_LIMIT; #define MPS_KEY_SPARE_COMMIT_LIMIT (&_mps_key_SPARE_COMMIT_LIMIT) #define MPS_KEY_SPARE_COMMIT_LIMIT_FIELD size +extern const struct mps_key_s _mps_key_PAUSE_TIME; +#define MPS_KEY_PAUSE_TIME (&_mps_key_PAUSE_TIME) +#define MPS_KEY_PAUSE_TIME_FIELD d extern const struct mps_key_s _mps_key_EXTEND_BY; #define MPS_KEY_EXTEND_BY (&_mps_key_EXTEND_BY) @@ -457,6 +460,9 @@ extern mps_res_t mps_arena_commit_limit_set(mps_arena_t, size_t); extern void mps_arena_spare_commit_limit_set(mps_arena_t, size_t); extern size_t mps_arena_spare_commit_limit(mps_arena_t); +extern double mps_arena_pause_time(mps_arena_t); +extern void mps_arena_pause_time_set(mps_arena_t, double); + extern mps_bool_t mps_arena_has_addr(mps_arena_t, mps_addr_t); extern mps_bool_t mps_addr_pool(mps_pool_t *, mps_arena_t, mps_addr_t); extern mps_bool_t mps_addr_fmt(mps_fmt_t *, mps_arena_t, mps_addr_t); diff --git a/mps/code/mpsi.c b/mps/code/mpsi.c index a6e24f0c63a..785500a4252 100644 --- a/mps/code/mpsi.c +++ b/mps/code/mpsi.c @@ -217,6 +217,25 @@ size_t mps_arena_spare_commit_limit(mps_arena_t arena) return limit; } +double mps_arena_pause_time(mps_arena_t arena) +{ + double pause_time; + + ArenaEnter(arena); + pause_time = ArenaPauseTime(arena); + ArenaLeave(arena); + + return pause_time; +} + +void mps_arena_pause_time_set(mps_arena_t arena, double pause_time) +{ + ArenaEnter(arena); + ArenaSetPauseTime(arena, pause_time); + ArenaLeave(arena); +} + + void mps_arena_clamp(mps_arena_t arena) { ArenaEnter(arena); diff --git a/mps/design/arena.txt b/mps/design/arena.txt index 2a94bb05b39..1d7f412dd86 100644 --- a/mps/design/arena.txt +++ b/mps/design/arena.txt @@ -447,6 +447,17 @@ than the amount of spare committed memory (stored in ``spareCommitExceeded`` is called. +Pause time control +.................. + +_`.pause-time`: The generic arena structure contains the field +``pauseTime`` for the maximum time any operation in the arena may take +before returning to the mutator. This value is used by +``PolicyPollAgain()`` to decide whether to do another unit of tracing +work. The MPS interface provides getter (``mps_arena_pause_time()``) +and setter (``mps_arena_pause_time_set()``) functions. + + Locks ..... diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index f95aaba331e..2e12f215135 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -12,6 +12,14 @@ Release 1.115.0 New features ............ +#. The MPS now provides control over the maximum time that operations + within an arena may pause the :term:`client program` for. This can + be specified by the new function :c:func:`mps_arena_pause_time_set` + or by passing the new keyword argument + :c:macro:`MPS_KEY_PAUSE_TIME` to :c:func:`mps_arena_create_k`. The + current value can be retrieved by the new function + :c:func:`mps_arena_pause_time`. + #. When creating an :ref:`pool-amc` pool, :c:func:`mps_pool_create_k` accepts the new keyword argument :c:macro:`MPS_KEY_EXTEND_BY`, specifying the minimum size of the memory segments that the pool @@ -41,6 +49,7 @@ New features :c:func:`mps_root_create_area_tagged` for areas of memory that can be scanned by area scanning functions. + Interface changes ................. diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index f98de4ba56d..d778681cadf 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -139,7 +139,7 @@ Client arenas * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`) is its size. - It also accepts two optional keyword arguments: + It also accepts three optional keyword arguments: * :c:macro:`MPS_KEY_COMMIT_LIMIT` (type :c:type:`size_t`) is the maximum amount of memory, in :term:`bytes (1)`, that the MPS @@ -154,6 +154,11 @@ Client arenas granularity reduces overheads, but increases :term:`fragmentation` and :term:`retention`. + * :c:macro:`MPS_KEY_PAUSE_TIME` (type :c:type:`double`, default + 0.0) is the maximum time, in seconds, that operations within the + arena may pause the :term:`client program` for. See + :c:func:`mps_arena_pause_time_set` for details. + For example:: MPS_ARGS_BEGIN(args) { @@ -213,7 +218,7 @@ Virtual memory arenas more efficient. When creating a virtual memory arena, :c:func:`mps_arena_create_k` - accepts four optional :term:`keyword arguments` on all platforms: + accepts five optional :term:`keyword arguments` on all platforms: * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`, default 256 :term:`megabytes`) is the initial amount of virtual address @@ -262,7 +267,12 @@ Virtual memory arenas :term:`bytes (1)`. See :c:func:`mps_arena_spare_commit_limit` for details. - A fifth optional :term:`keyword argument` may be passed, but it + * :c:macro:`MPS_KEY_PAUSE_TIME` (type :c:type:`double`, default + 0.0) is the maximum time, in seconds, that operations within the + arena may pause the :term:`client program` for. See + :c:func:`mps_arena_pause_time_set` for details. + + A sixth optional :term:`keyword argument` may be passed, but it only has any effect on the Windows operating system: * :c:macro:`MPS_KEY_VMW3_TOP_DOWN` (type :c:type:`mps_bool_t`, @@ -424,6 +434,59 @@ Arena properties :c:func:`mps_arena_commit_limit`. +.. c:function:: double mps_arena_pause_time(mps_arena_t arena) + + Return the maximum time, in seconds, that operations within the + arena may pause the :term:`client program` for. + + ``arena`` is the arena. + + See :c:func:`mps_arena_pause_time_set` for details. + + +.. c:function:: void mps_arena_pause_time_set(mps_arena_t arena, double pause_time) + + Set the maximum time, in seconds, that operations within an arena + may pause the :term:`client program` for. + + ``arena`` is the arena. + + ``pause_time`` is the new maximum pause time, in seconds. It must + be non-negative. + + The pause time may be set to zero, in which case the MPS returns + as soon as it can do so. The consequence is that the MPS will need + to take more slices of time in order to make :term:`garbage + collection` progress. This value is suitable for interactive + applications where latency needs to be minimized, but where there + is plenty of CPU time available. + + The pause time may be set to infinity, in which case the MPS + completes all outstanding :term:`garbage collection` work before + returning from an operation. The consequence is that the MPS will + be able to save on the overheads due to :term:`incremental + collection`, leading to lower total time spent in collection. This + value is suitable for non-interactive applications where total + time is important. + + The MPS makes a best effort to return to the :term:`client + program` from any operation on the arena within the maximum pause + time, but does not guarantee to do so. This is for three reasons: + + 1. many operations in the MPS necessarily take some minimum amount + time that's logarithmic in the amount of :term:`memory (2)` + being managed (so if you set the maximum pause time to zero, + then every operation will exceed it); + + 2. some operations in the MPS call functions in the :term:`client + program` (for example, the :term:`format methods`), and the MPS + has no control over how long these functions take; + + 3. none of the operating systems supported by the MPS provide + real-time guarantees (for example, the process may have to wait + for :term:`memory (2)` to be :term:`paged in`). + + .. c:function:: size_t mps_arena_reserved(mps_arena_t arena) Return the total :term:`address space` reserved by an diff --git a/mps/manual/source/topic/keyword.rst b/mps/manual/source/topic/keyword.rst index ddbfbfd57ac..396abb92cd8 100644 --- a/mps/manual/source/topic/keyword.rst +++ b/mps/manual/source/topic/keyword.rst @@ -115,6 +115,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. :c:macro:`MPS_KEY_MVFF_SLOT_HIGH` :c:type:`mps_bool_t` ``b`` :c:func:`mps_class_mvff` :c:macro:`MPS_KEY_MVT_FRAG_LIMIT` :c:type:`mps_word_t` ``count`` :c:func:`mps_class_mvt` :c:macro:`MPS_KEY_MVT_RESERVE_DEPTH` :c:type:`mps_word_t` ``count`` :c:func:`mps_class_mvt` + :c:macro:`MPS_KEY_PAUSE_TIME` :c:type:`double` ``size`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` :c:type:`mps_pool_debug_option_s` ``*pool_debug_options`` :c:func:`mps_class_ams_debug`, :c:func:`mps_class_mv_debug`, :c:func:`mps_class_mvff_debug` :c:macro:`MPS_KEY_RANK` :c:type:`mps_rank_t` ``rank`` :c:func:`mps_class_ams`, :c:func:`mps_class_awl`, :c:func:`mps_class_snc` :c:macro:`MPS_KEY_SPARE` :c:type:`double` ``d`` :c:func:`mps_class_mvff`