# Thread Registry

Most of the Demos have the property named `c` when registering a new <mark style="color:blue;">`PerigeeApplication`</mark>. The `c` simply stands for config. Let's take a look at how you can access and control the managed threads within an application.&#x20;

```csharp
PerigeeApplication.ApplicationNoInit("Demo", (c) => //"c" here is the registry
{     

});
```

## Properties

### 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:

```csharp
//Use method
c.GetCancellationToken()

//OR go directly to the source
c.CTS.Token;
```

{% hint style="danger" %}
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.&#x20;
{% endhint %}

### 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

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:

{% content-ref url="../blueprints/perigee-with-.net-hosting" %}
[perigee-with-.net-hosting](https://docs.perigee.software/blueprints/perigee-with-.net-hosting)
{% endcontent-ref %}

### 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:

{% content-ref url="../getting-started/hello-configuration" %}
[hello-configuration](https://docs.perigee.software/getting-started/hello-configuration)
{% endcontent-ref %}

## Configuration Methods

### IConfiguration

#### Configure

To override the `IConfiguration`, call **`ConfigureConfiguration()`**. This builds the IConfiguration with the default options and whatever you've added to the builder.

```csharp
PerigeeApplication.ApplicationNoInit("Configuration", (c) => {

    c.ConfigureConfiguration((cb, env) => {
        cb.AddKeyPerFile("keyDirectory");
    });

});
```

#### Reload Configuration Event&#x20;

```csharp
PerigeeApplication.ApplicationNoInit("Configuration", (c) => {

    c.ConfigUpdatedEvent((config) => {
        c.GetLogger<_PerigeeStartup>().LogInformation("Config updated");
    });

});
```

#### Get a configuration

```csharp
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" ]
  },
 
 */
```

### IServiceProvider

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

#### Configure and get service

```csharp
PerigeeApplication.ApplicationNoInit("Configuration", (c) => {

    c.ConfigureServices((sc, env) => {
        sc.AddSingleton((isp) => new HelloConfig());
    });

    var hc = c.ServiceProvider.GetRequiredService<HelloConfig>();

});
```

## Windows Startup

### Registering

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.

```csharp
c.RegisterStartup();
```

## Thread Methods

### GetThreads

Get all threads from the management system

#### Example:

```csharp
c.GetThreads();
```

### RestartAllThreads

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.

#### Example:

```csharp
c.RestartAllThreads();
```

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

#### Example:

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

### StartOrRestart

Starts or restarts a thread with the specified name.

#### Example:

```csharp
c.StartOrRestart("MyThread");
```

### Start

Starts a thread with the specified name.

#### Example:

```csharp
c.Start("MyThread");
```

### Stop

**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!**

#### Example:

```csharp
c.Stop("MyThread");
```

### QueueStop

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

#### Example:

```csharp
c.QueueStop("MyThread", true);
```

### IsRunning

Checks whether a thread with the specified name is running.

#### Example:

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

### StartIfNotRunning

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

#### Example:

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

### GetThread

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

#### Example:

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

### ContainsThreadByName

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

#### Example:

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

### ExecuteCRONNow

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

#### Example:

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

### Add a custom managed thread

The best example of this is shown here:

{% content-ref url="../perigee-and-beyond/extending-threads" %}
[extending-threads](https://docs.perigee.software/perigee-and-beyond/extending-threads)
{% endcontent-ref %}

### Link to Configuration

You can link managed threads to configuration values in one of two ways:

1. Use the handy **`.LinkToConfig()`** method immediately following the thread
2. Use the **`.LinkConfigToThread()`** method somewhere later

```csharp
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) => { 

            l.LogInformation("Running"); 

        }, 
        started: false).LinkToConfig("HelloConfig:Enabled");

    //You may also provide thread linking later:
    c.LinkConfigToThread("HelloConfig:Enabled", "MyThread");
    

});
```

{% hint style="warning" %}
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".`
{% endhint %}

## Agent Methods

Agents are explained fully over here:

{% content-ref url="event-sources/scheduled-logic/sync-agent" %}
[sync-agent](https://docs.perigee.software/core-modules/event-sources/scheduled-logic/sync-agent)
{% endcontent-ref %}

### Get agents

To get an agent after it's been added to the system, call **`GetAgent()`**.

```csharp
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:

```csharp
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.&#x20;
  * If **TRUE**, it will wait for the current execution to finish and then trigger another exeucution.&#x20;
  * If **FALSE**, it will not trigger another execution, but return.&#x20;
* An optional wait time before giving up. If the wait time is not supplied, it will wait indefinitely.

#### Example:

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