mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat!: add cooperative execution-time limits across engine, VM, and binding (#539)
- Introduce ExecutionTimer/ExecutionTimerConfig to allow limiting evaluating time. - To amortize time checking costs, checking interval can be configured via the notion of work units - A global fallback time limit can be set to universally limit all evaluation in addition to engine level limit setting. - Implement limnits in interpreter and RVM. In RVM, also handle suspend/resume so that time during pause is not counted. - Add engine-level APIs to set/clear per-engine timer configuration and apply global fallback defaults. - Surface execution-time limits through FFI and C# bindings - Add C# tests and example usage to validate engine overrides, global fallback behavior, and compiled policy enforcement. - Expand docs for execution-time limit - Add interpreter YAML cases and VM unit tests for time-limit behavior and deterministic time sources. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
e68e852ee3
commit
394625d4bc
@@ -203,5 +203,14 @@ namespace Regorus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UseHandle(Action<IntPtr> action)
|
||||
{
|
||||
UseHandle<object?>(handlePtr =>
|
||||
{
|
||||
action(handlePtr);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Regorus.Internal;
|
||||
|
||||
|
||||
@@ -24,6 +26,17 @@ namespace Regorus
|
||||
_handle = RegorusEngineHandle.Create();
|
||||
}
|
||||
|
||||
public static void SetFallbackExecutionTimerConfig(ExecutionTimerConfig config)
|
||||
{
|
||||
var nativeConfig = config.ToNative();
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_set_fallback_execution_timer_config(nativeConfig));
|
||||
}
|
||||
|
||||
public static void ClearFallbackExecutionTimerConfig()
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_clear_fallback_execution_timer_config());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
@@ -87,6 +100,32 @@ namespace Regorus
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void SetExecutionTimerConfig(ExecutionTimerConfig config)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var nativeConfig = config.ToNative();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var localConfig = nativeConfig;
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_set_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr, &localConfig));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearExecutionTimerConfig()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
UseHandle(enginePtr =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
CheckAndDropResult(Regorus.Internal.API.regorus_engine_clear_execution_timer_config((Regorus.Internal.RegorusEngine*)enginePtr));
|
||||
}
|
||||
});
|
||||
}
|
||||
public string? AddPolicy(string path, string rego)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
@@ -355,7 +394,21 @@ namespace Regorus
|
||||
});
|
||||
}
|
||||
|
||||
string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||
private static string? StringFromUtf8(IntPtr ptr)
|
||||
{
|
||||
|
||||
#if NETSTANDARD2_1
|
||||
return Marshal.PtrToStringUTF8(ptr);
|
||||
#else
|
||||
int len = 0;
|
||||
while (Marshal.ReadByte(ptr, len) != 0) { ++len; }
|
||||
byte[] buffer = new byte[len];
|
||||
Marshal.Copy(ptr, buffer, 0, buffer.Length);
|
||||
return Encoding.UTF8.GetString(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string? CheckAndDropResult(Regorus.Internal.RegorusResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Regorus
|
||||
{
|
||||
/// <summary>
|
||||
/// Managed representation of the execution timer configuration used by the engine.
|
||||
/// </summary>
|
||||
public readonly struct ExecutionTimerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExecutionTimerConfig"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="limit">Maximum wall-clock duration allowed for evaluation. Must be non-negative.</param>
|
||||
/// <param name="checkInterval">Number of work units between timer checks. Must be non-zero.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="limit"/> is negative or <paramref name="checkInterval"/> is zero.</exception>
|
||||
public ExecutionTimerConfig(TimeSpan limit, uint checkInterval)
|
||||
{
|
||||
if (limit < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(limit), "Execution timer limit must be non-negative.");
|
||||
}
|
||||
|
||||
if (checkInterval == 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(checkInterval), "Execution timer check interval must be non-zero.");
|
||||
}
|
||||
|
||||
Limit = limit;
|
||||
CheckInterval = checkInterval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum wall-clock duration allowed for an evaluation.
|
||||
/// </summary>
|
||||
public TimeSpan Limit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of work units between timer checks.
|
||||
/// </summary>
|
||||
public uint CheckInterval { get; }
|
||||
|
||||
internal Regorus.Internal.RegorusExecutionTimerConfig ToNative()
|
||||
{
|
||||
if (Limit < TimeSpan.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Execution timer limit must be non-negative.");
|
||||
}
|
||||
|
||||
ulong ticks = checked((ulong)Limit.Ticks);
|
||||
ulong limitNanoseconds = checked(ticks * 100UL);
|
||||
|
||||
return new Regorus.Internal.RegorusExecutionTimerConfig
|
||||
{
|
||||
limit_ns = limitNanoseconds,
|
||||
check_interval = CheckInterval,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace Regorus.Internal
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_compile_with_entrypoint", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_compile_with_entrypoint(RegorusEngine* engine, byte* rule);
|
||||
|
||||
#if REGORUS_FFI_TEST_HOOKS
|
||||
#if REGORUS_FFI_TEST_HOOKS
|
||||
/// <summary>
|
||||
/// Trigger a panic inside the engine for testing purposes.
|
||||
/// </summary>
|
||||
@@ -269,7 +269,35 @@ namespace Regorus.Internal
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_test_reset_poison", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern void regorus_engine_test_reset_poison();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Configure the execution timer for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_set_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_set_execution_timer_config(RegorusEngine* engine, RegorusExecutionTimerConfig* config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the execution timer configuration for a specific engine instance.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_engine_clear_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_engine_clear_execution_timer_config(RegorusEngine* engine);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Execution Timer Global Methods
|
||||
|
||||
/// <summary>
|
||||
/// Set the process-wide fallback execution timer configuration.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_set_fallback_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_set_fallback_execution_timer_config(RegorusExecutionTimerConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// Clear the process-wide fallback execution timer configuration.
|
||||
/// </summary>
|
||||
[DllImport(LibraryName, EntryPoint = "regorus_clear_fallback_execution_timer_config", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern RegorusResult regorus_clear_fallback_execution_timer_config();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -579,6 +607,16 @@ namespace Regorus.Internal
|
||||
public byte* error_message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FFI representation of the execution timer configuration.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RegorusExecutionTimerConfig
|
||||
{
|
||||
public ulong limit_ns;
|
||||
public uint check_interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for regorus::Engine.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user