[GSOC 2023] .NET Port

I once has an internship where my assigned task was to make a SOAP request by downloading a NuGet package and using that for the task.
Naturally I thought the assignment was to make the request myself.
Also SOAP is terrible.

Anyhow I decided against IT as a Job because of that! : D

1 Like

downloading a NuGet package and using that for the task.

There’s a NuGet package for almost programming task you can think of. There’s even one for reading Haiku HPKG files.

We would like to try to support other languages in Genio with the full spectrum of features: build and run, syntax highlighting and LSP support (via omnisharp maybe?).
.NET and C# and Python would be our first choices.
So yes, please make the binaries available so we can experiment a bit.

4 Likes

For anyone interested, I will release my binary builds here.

The .NET runtime is already released as a build artifact. I will set up build scripts for the .NET SDK as well as the NuGet feeds later today. After that I will provide further installation instructions.

HaikuPorts releases are currently impossible because:

  • The current branch is using a prerelease version of .NET 8.
  • Many PRs are waiting to get merged upstream.
  • No official documentation (at least, not among the ones I know) on how to create packages that have themselves as a build prerequisite. Probably creating an initial dummy package that downloads pre-built binaries and then use that one to bootstrap another “revision” of the recipe might work?
  • Unconventional installation layout. All of .NET lives in one directory, and it should be writable so that additional workloads or other optional components can be installed through NuGet, instead of being divided into /bin, /lib, etc. like other UNIX applications.
4 Likes

You can look at hos this is done for Rust and Haskell.

For Rust, haikuports does not currently builds from source, but just repackages existing binaries.

For Haskell, the first version was cross compiled, and then that version was used to build the next one, and so on.

That will not really fit with the way haiku packages work. Living in one directory is no problem (just put it in system/develop/tools/dotnet for example), but you’ll probably need some things to be in a packaged directory and some to be in a non-packaged one.

According to the documentation, NuGet has “user” and “computer” levels, just like other package managers. Usually in Haiku, the “computer” level is read-only and managed by packages, and the “user” level will be in a non-packaged directory (at least I think that’s what we do for Python and Perl).

This is actually solvable, as .NET provides a way to install workloads using the native installation method of the platform. For example, on Windows, workloads are installed as .msi files.

When workloads are actually needed, we can make .hpkg files for each workload and write some kind of provider backend for Haiku packages.

1 Like

Detailed installation instructions have been provided here:

The instructions assume an audience with some familiarity to both the .NET and Haiku ecosystems.

A blog with the technical details behind this will come soon.

5 Likes

I have provided a dotnet-install.sh-like script that installs the latest .NET builds on Haiku.
The script currently installs Debug builds, not Release builds as the latter might have some mysterious hidden bugs.

Make sure you install .NET dependencies first:

pkgman install -y gmp krb5 libiconv llvm12_libunwind mpfr
pkgman install -y jq # Required for the dotnet-install script.

Then run the installer script:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/trungnt2910/dotnet-builds/HEAD/dotnet-install.sh)" -- --install-dir=/path/to/where/you/want/to/install/dotnet

For veteran users of .NET, no, this script does not work like the Microsoft’s official script and currently still lacks many options.

7 Likes

image

This is my HpkgReader.Gtk, a demo application for my personal HPKG reader implementation written 100% in C#.

The application runs on Haiku using GtkSharp bindings for Gtk3.

27 Likes

It seems like .NET uses edge-triggered polling with EPOLLET, or the equivalent EV_CLEAR for platforms with kqueue.

This means after an event has been read by the user, it will not be returned by the polling function a second time, even when there is more data to be read from/more data can be write to the file descriptor. The event will only be re-triggered when another write/read actually occurs on the other end.

This property cannot be satisfied by the current poll workaround. For poll, it is not possible to distinguish between two different events in two calls and the same unchanged event in two calls. Therefore, .NET tries to loop again and again and handles the same event over and over, starving CPU resources and preventing other threads from running.

1 Like

Not much activity last week because I had exams.

I am also waiting for an epoll/kqueue implementation because of the problem mentioned above.

In the last week, I decided to pay the virtual memory technical debt by implementing _kern_remap_memory. While the code will need a bit more reviewing, it has been tested with scenarios similar to what should be used by .NET. I will update the .NET port to use this new syscall when it has been fully accepted into mainstream Haiku and exposed as a public API in libroot.so.

11 Likes

This is how some portions of .NET should look like if all my patches are accepted.

Currently, I am exposing the _kern_remap_memory system call as a special mode of mmap named MAP_REMAP. When this flag is specified, the last two arguments become the PID of the source memory (0 for using the same team), and the address of the cloned memory.

The commit uses this new functionality in a variety of ways, from committing memory in previously overcommitting area, remapping memory ranges with different protections (the doublemapper), and reading the memory of another process.

Using the new syscall, the bug that makes dotnet crash at fork unless COMPlus_EnableWriteXorExecute=0 is set disappears. The double mapper works flawlessly.

The mentioned commit also removes a hack related to datagram UNIX domain sockets, as I have implemented that in this patch.

These changes will not be visible in the dotnet-builds repository until the Haiku patches required get merged (and that will take quite a long time since they are both size XL on Gerrit).

With these two patches, and hopefully kqueue being implemented soon, most problems of the port are solved (at least until I get an SDK stable enough to bootstrap itself and run library tests). The remaining issues either have functionally equivalent workarounds, are obscure and not supported on many other platforms anyway (some process/thread attributes), or are way outside of my area of knowledge (IPV6 support).

9 Likes

That’s not really the problem.

Large but well-designed and well-documented patches are not a problem to review. But to achieve that it usually means spending some time to discuss things (for example in a bugtracker ticket) before implementing it “right” on the first try. Otherwise, the discussion will happen after the code is already written, and changes will be needed (that’s totally fine, sometimes it’s very useful to have a working prototype code to experiment with and see the limitations of an approach and what can be improved on it).

5 Likes

Is there any way to implement the Linux extension mremap instead? (though, notably, it seems more like our resize_area than this… maybe with a special flag to keep the old mapping?)

It is so similar that HyClone uses this to implement resize_area.

mremap is some kind of an opposite to the _kern_remap_memory. mremap moves the mapping and resizes the it, but all of the mapping’s properties (protections and things like that) are left unmodified. The old mapping are always removed (MREMAP_DONTUNMAP does not keep the old pages but replaces them with zero-filled ones instead).

_kern_remap_memory cannot resize ranges but can change properties. It looks more similar to Darwin’s vm_remap.

Along with mremap, I have also considered exposing this syscall independently as remap_memory, but there do not seem to be any public non-area VM syscall on Haiku.

Do any of the BSDs have an equivalent to the Darwin API? It would be preferable if we just added something in libbsd or libgnu instead of adding our own private API. But we can do that if necessary.

Ultimately, whatever we do, it shouldn’t involve syscalls being invoked directly by third-party applications. That will just create compatibility problems. At least with syscall wrapper functions, we can modify the syscall without having to rebuild other programs.

FreeBSD seems to also have a mremap, but it is a bit different from the Linux version.

This version also does not allow changing any attributes but does allow duplicating the mapping using MAP_REMAPDUP. It also allows resizing the mapping.

it shouldn’t involve syscalls being invoked directly by third-party applications

Currently, as seen in the dotnet commit, I am using mmap with a special MAP_REMAP flag as a wrapper for _kern_remap_memory. For example, to commit a range of memory:

    bool success = mmap(address, size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_FIXED | MAP_REMAP, 0, (off_t)address) == address;

This looks like exactly what we want, then. So, let’s go with it for this API?

No. Like I explain above:

  • It does not have all the features we want:
    • It cannot change the newly remapped memory’s protection and other attributes.
    • It works on one process only.
  • It does things that _kern_remap_memory cannot do:
    • _kern_remap_memory as in the current patch cannot preserve memory protection of the target mappings. They are all set to one specified protection.
    • _kern_remap_memory cannot resize mappings.

If needed, I can probably change _kern_remap_memory a bit to fit mremap. But mremap is not good enough to be the one and only public interface for this syscall.

What will use this feature of _kern_remap_memory? It won’t be possible without superuser permissions, right?

The manual page says it can have different mappings, so that means mprotect can be run. But yes, I guess that’s a difference, if remap_memory does it all at once.