Ask or search…

Thread Registry

Most of the Demos have the property named c when registering a new PerigeeApplication. The c simply stands for config. Let's take a look at how you can access and control the managed threads within an application.
PerigeeApplication.ApplicationNoInit("Demo", (c) => //"c" here is the registry


The Cancellation Token

There is a single "global" token that all other tokens are joined to. This is a great place to listen for application shutdown as it's cancelled the moment a SIG_ABORT or CONTROL-C event is received.
To get the globally registered cancellation token, it's available as such:
//Use method
//OR go directly to the source
If you were to cancel this token you will shut off the entire application and trigger a graceful shutdown event. Only do this if you want the whole application to quit.

Names, configurations

AppName - is available for the registered application name, if used for logging or custom logic.
Args[] - is available if passed into the constructor, and is the application start parameters.
Configuration - is the IConfiguration that was loaded and built after it's Configuration phase.
ServiceProvider - The IServiceProvider after service build configuration
Event_ConfigurationUpdated - Is the event handler for subscribing to hot-reloads. You may also use the helper method c.ConfigUpdatedEvent((nconfig) => {}).
RegisteredTaskCount - The number of registered threads


Hosted application task is respected and shut down alongside Perigee.
HostTask - When hosting an API application, supply the host task and it is stored here. The demo for this functionality can be seen here:

Secure properties

SecureValueKey - The secure key
SecureValueIV - The secure IV
SecureValueSource - The secure source, either args, environment, or direct.
To see the demo for this, check out:

Configuration Methods



To override the IConfiguration, call ConfigureConfiguration(). This builds the IConfiguration with the default options and whatever you've added to the builder.
PerigeeApplication.ApplicationNoInit("Configuration", (c) => {
c.ConfigureConfiguration((cb, env) => {

Reload Configuration Event

PerigeeApplication.ApplicationNoInit("Configuration", (c) => {
c.ConfigUpdatedEvent((config) => {
c.GetLogger<_PerigeeStartup>().LogInformation("Config updated");

Get a configuration

PerigeeApplication.ApplicationNoInit("Configuration", (c) => {
//Get a value from the config by path
var Name = c.GetValue<string>("HelloConfig:Name");
//Get a class of the configuration section
HelloConfig hc = c.GetConfigurationAs<HelloConfig>("HelloConfig");
public class HelloConfig
public string Name { get; set; }
public int Year { get; set; }
public List<string> Tags { get; set; }
"HelloConfig": {
"Name": "HAL 9000",
"Year": 2001,
"Tags": [ "Heuristic", "Algorithmic" ]


If you need to configure the service provider, do so with the ConfigureServices() callback.

Configure and get service

PerigeeApplication.ApplicationNoInit("Configuration", (c) => {
c.ConfigureServices((sc, env) => {
sc.AddSingleton((isp) => new HelloConfig());
var hc = c.ServiceProvider.GetRequiredService<HelloConfig>();

Windows Startup


If you want to register an autostart for the windows platform, it does so by adding values to the registry. The app MUST be run with administrative privileges the first time this key is set.

Thread Methods


Get all threads from the management system




Restarts all threads in the system. This will start stopped threads, it's intended to perform a full reboot of the application and all of the threads, regardless of their current state.


You could use the RunTime property to only restart threads that haven't been restarted in a certain period of time:


c.GetThreads().Where(f => f.RunTime > TimeSpan.FromSeconds(30)).ToList().ForEach(f =>
l.LogInformation("Restarting thread {n}", f.Name);


Starts or restarts a thread with the specified name.




Starts a thread with the specified name.




NOTE We highly recommend using QueueStop() over Stop(). The reason is that Stop is not thread safe, and may cause issues. It exists only for niche, same thread operation. Use with caution!




Queues the stop of a thread, waiting for the task to complete.


c.QueueStop("MyThread", true);


Checks whether a thread with the specified name is running.


bool isRunning = c.IsRunning("MyThread");


Starts a thread with the specified name if it is not already running.


bool started = c.StartIfNotRunning("MyThread");


Gets a thread with the specified name, if it exists. Use with caution.


ManagedThread myThread = c.GetThread("MyThread");


Checks if the collection contains a thread with the specified name.


bool result = c.ContainsThreadByName("threadName");


Executes a CRON thread immediately by its name. Optionally, you can provide a callback to be invoked when the CRON operation is completed and the number of seconds to wait for a start operation


bool result = c.ExecuteCRONNow("threadName", () => Console.WriteLine("CRON completed"), 3000);

Add a custom managed thread

The best example of this is shown here:
You can link managed threads to configuration values in one of two ways:
  1. 1.
    Use the handy .LinkToConfig() method immediately following the thread
  2. 2.
    Use the .LinkConfigToThread() method somewhere later
PerigeeApplication.ApplicationNoInit("Configuration", (c) => {
//Add a CRON thread that is not started by default. Then link it to the HelloConfig:Enabled boolean
c.AddCRON("MyThread", "0 0 1 1 *",
(ct, l) => {
started: false).LinkToConfig("HelloConfig:Enabled");
//You may also provide thread linking later:
c.LinkConfigToThread("HelloConfig:Enabled", "MyThread");
Because configurations are loaded, hot-reloaded, and maintained internally by the configuration system, please also use the started: false flag on the thread itself. This will prevent the thread from starting while the configuration value is "false".

Agent Methods

Agents are explained fully over here:

Get agents

To get an agent after it's been added to the system, call GetAgent().
PerigeeApplication.ApplicationNoInit("Agent", (c) => {
//Add an agent to run every 2 hours, at most 4 times a day
c.AddAgent("MyAgent", "Agent1", "Main", new SyncAgentSourceMemory("AgentDemo.json", c.GetCancellationToken()), AgentRunCondition.RunIfPastDue, (c) => c.SetSyncLimitPerDay(4).SetSync(TimeSpan.FromHours(2)),
(ct, l, exec) =>
return exec.Complete(DateTimeOffset.UtcNow.AddHours(1));
}, null);
//Get the agent
var agent = c.GetAgent("MyAgent");
c.ConfigureAgent("MyAgent", (c) => c.SetSync(TimeSpan.FromSeconds(20)));
//(MyAgent) Agent1(Main) Next Check in 18 seconds

Configure agents

To reconfigure an agent after it's been added to the system:
PerigeeApplication.ApplicationNoInit("Agent", (c) => {
//Add an agent to run every 2 hours, at most 4 times a day
c.AddAgent("MyAgent", "Agent1", "Main", new SyncAgentSourceMemory("AgentDemo.json", c.GetCancellationToken()), AgentRunCondition.RunIfPastDue, (c) => c.SetSyncLimitPerDay(4).SetSync(TimeSpan.FromHours(2)),
(ct, l, exec) =>
return exec.Complete(DateTimeOffset.UtcNow.AddHours(1));
}, null);
//Call configure, changing the timespan check to 20 seconds.
c.ConfigureAgent("MyAgent", (c) => c.SetSync(TimeSpan.FromSeconds(20)));
//You'll see the log of the next check event:
// (MyAgent) Agent1(Main) Next Check in 18 seconds

Queue Remote Refresh

Queues an agent for remote refresh. The parameters are:
  • The agent name.
  • A boolean to say whether or not to queue if the current agent is executing.
    • If TRUE, it will wait for the current execution to finish and then trigger another exeucution.
    • If FALSE, it will not trigger another execution, but return.
  • An optional wait time before giving up. If the wait time is not supplied, it will wait indefinitely.


c.Agent_QueueRemoteRefresh("AgentName", true, TimeSpan.FromSeconds(30));