String.Create with Spans

One new C# feature with .NET 9 is the ability to define allows ref struct as a generic type constraint. It sounds contradictory to refer to this as a constraint, because it allows more types to be used with the given method. However, it can also limit how those generic type parameters maybe used in methods. Allowing ref structs allows ref struct types to be used as generic type parameters. The most common ref struct types are the built-in Span<T> and ReadOnlySpan<T> but this constraint allows for any custom ref structs as well. In C# ref struct types are special as they must live on the stack, and must not escape to the managed heap.

The C# compiler makes sure that ref struct types are used correctly, and in this sense, it poses as a constraint. For example, in the code snippet below MyType compiles without the constraint:

public class MyType<T> // where T : allows ref struct
{
    private T _field;
    public T MyCreate(Func<T> myfunc)
    {
        var value = myfunc();
        _field = value;
        return value;
    }
}

However, if we uncomment the where T : allows ref struct constraint, the compiler issues the following error for _field: Field or auto-implemented property cannot be of type 'T' unless it is an instance member of a ref struct.

Find out more


Testing Locks is Difficult

I have recently worked with multithreaded code using shared state, that required mutual exclusion. In .NET there are numerous different types to accomplish mutual exclusion, the C# language even provides a lock keyword. Depending on the use-case one can use Monitor, lock on reference types, SemaphoneSlim, ReaderWriterLockSlim, Interlocked, SpinLock concurrent collections, or the .NET 9 introduced Lock type. This latter one also works with the lock language keyword, and it brings much of the underlying native implementation to the managed space. This results in some minor performance improvements compared to locking on any other reference type.

Achieving the above improvement seems straightforward: change the object's type that is used within a lock keyword. However, lock might be still slower to other types of locks as the 'best' locking solution depends on many factors:

  • number of threads accessing the lock

  • the frequence of the threads accessing the lock

  • duration inside the lock

  • contention

  • CPU, architecture, OS, etc.

  • memory model

  • etc.

In general, certain locks might fit better for certain uses cases (for example, when using collection, async code, granular locks etc.), which drives the decisions on the lock that suits the best for an application. To prove that one locking solution is more performant than another is only possible by performance measuring an actual application on a production-like load. This also means that the same code might perform differently from application to application.

Find out more


Struct Equality and SkipLocalsInit

I have recently learned an interesting behavior of the .NET runtime on equality from a post on Mastodon. The post points out that RuntimeHelpers.Equals returns whether the input arguments' underlying memory are equal, which may not be true always, even when the values are equal.

Under the hood RuntimeHelpers.Equals uses memcmp to compare the memory representation of two objects.

Padding

In C# structs may have paddings in the memory representation. For example, the following struct will have a padding on x64, so that the value is aligned to 8 bytes:

Find out more


Cross Compile on Windows 11 to Linux ARM64 (Raspberry)

The quickest and easiest way to cross compile NativeAot on Windows to a Raspberry Pi is using PublishAotCross nuget package.

Set <PublishAot> to true and install nuget package PublishAotCross.

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <PublishAot>true</PublishAot>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="PublishAotCross" Version="1.0.0" />
  </ItemGroup>
  
</Project>

Package requires zig, download and extract it to any folder.

Find out more


Running ASP.NET Core 9 on a Raspberry Pi Zero 2W

This is a step by step for setting up a Raspberry Pi Zero 2W with a default ASP.NET Core 9 application (in 9 steps).

  1. Install the Raspberry Pi Imager tool.

  2. Install Raspberry Pi OS Lite (64bit) on the SD Card. Configure settings Wi-Fi/user/SSH settings during the installation.

  3. SSH into the raspberry using ssh <user>@raspberrypi.local or ssh <ip> -l <user> command.

  4. Install .NET runtime:

  • Follow documentation outlined by dotnet iot. Here I am using the Framework-Dependent approach.

  • curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel STS

  • Set PATH: echo 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrc then echo 'export PATH=$PATH:$HOME/.dotnet' >> ~/.bashrc and source ~/.bashrc

  • Run dotnet --version to validate that installation succeeded.

  1. Create a folder mkdir app on the Raspberry Pi for the application.

  2. Publish the web application in Release, Portable, .NET 9, Framework-Dependent mode on any dev machine.

  3. Copy the published application to the Raspberry Pi using scp: scp -r D:\repos\..\WebApplication\bin\Release\net9.0\publish\* <user>@raspberrypi:/home/<user>/app executed on the dev machine.

  4. To run locally the service on port 80 (443 would require a certificate), execute the following command: sudo /home/<user>/.dotnet/dotnet /home/<user>/app/WebApplication.dll --urls http://+:80, where sudo is required for port 80 (using port 5000 can bind without elevated privileges).

  5. Setup the application to start after boot using systemd ref:

Find out more