Hacker News new | ask | show | jobs
by divs1210 1258 days ago
Interesting.

Why do you say so?

1 comments

Predictable finalisation. To build say, an operating system, you need to know exactly when memory is freed. If you don’t know this at a fundamental level, how can you schedule work? This IMO is fundamentally why no one has built a modern OS in C# alone.
Hence why system programming languages with GC also have other ways to manage resources (including memory), just because a GC is available doesn't mean it has to be used for everything.

See the whole linage of Mesa/Cedar, Modula-2+, Modula-3, Oberon, Midori,... OSes.

C#, like those languages, allows for manual memory management, unsafe code, stack allocation. MSIL was designed to also support languages like C++, and since C# 7, many of those capabilities are no longer only available to C++/CLI, rather being incremently exposed to C# as well.

Besides being a GC algorithm anyway, RC has performance issues, which can only be solved by using multiple optimization techniques from tracing GC, like background threads for cascading deletion of complex graphs, deferred counter manipulation, lock free data structures,...

Project Oberon source code,

http://www.projectoberon.com/

Active Oberon source code (the latest language OS from the Oberon tree still being actively developed),

https://gitlab.inf.ethz.ch/felixf/oberon

It would be great if Microsoft some day releases Midori source code.

> It would be great if Microsoft some day releases Midori source code.

Waiting for this since years. But it won't happen, I guess, as it didn't happen until now.

Adding features to languages by anyone but the language designer doesn't work. It's unsupported, it affects too many other parts of the stack and leads to incredibly hard to spot bugs. You, or any project, can't do this.

Also: doing this means as soon as you start using predictable finalisation to close DB connections, or anything, your code stops being C# code. It can no longer be used except in a highly nontrivial environment. It depends, in an undeclared manner, on a nonstandard runtime.

So for open source code, or anything you're hoping to reuse: please don't do this.

If you want this, switch to C++, Swift or Rust, or some other language that has it built in.

That is what using declarations and IDisposable are for.
Exactly. Idiomatic C# code does not depend on finalizers to release resources. Finalizers are typically only used as a last resort to prevent handle leaks, it is only memory deallocation that is nondeterministic.
And even then, SafeHandle is a much better option.

Memory deallocation can be done as deterministic as malloc/free via the native heap APIs.

Zero stdlib comes with no GC and seems to be viable for bare metal, so pretty much what you're describing.

Although you don't need to remove GC for everything. IDisposable already covers explicit destruction and the memory mapping can be handled with native functions where necessary. (I don't know if that's possible in bflat, I'm talking in general - but pinvoke should be enough)

Also for hard real time systems (which do not necessarily include operating systems). You need to have a deterministic computation time each cycle and never go over the time budget allocated. With a GC that isn't reference-counted it's nondeterministic. It could also be nondeterministic with reference counting if run e.g. into a loop that allocates a lot of objects and then they are freed, but you can measure and control and fix this behavior more easily than when trying to work against a non-resource-counting GC.

Like others said here, it could certainly be done in C#, but you have to fight the language on some level. Events/delegates, strings and almost all collection classes are heap based and therefore can trigger the GC. It's difficult to do useful stuff without them.

But you can do what you'd do in typical hard real-time applications anyway anyway, that is, preallocate everything you think you'll need. The APIs are there.

The most problematic data type is the string, it's almost impossible to do anything in C# without it, and it's in the heap, therefore can trigger the GC. Also it's immutable, so almost all useful operations on it produce another string(s). See Joe Duffy's old blog post here [0].

However Using the new Span<char> or ReadOnlySpan<char> which are stack based can alleviate that. Almost all string actions can be done on them. There's also a library called ZString which can be useful [1].

Another problem is the jit, which in "regular" dotnet works lazily, only on the first call to a function or even to string literals, so again you don't get deterministic execution. It is very quick but calling a new function chain in succession can add up. However using an AOT compilation like bflat does eliminates that problem.

Even without using AOT, you can approximate it by using reflection and calling RuntimeHelpers.PrepareMethod() on all methods in all referenced assemblies (except the system ones which should already be precompiled).

[0] https://github.com/joeduffy/joeduffy.github.io/blob/master/_...

[1] https://github.com/Cysharp/ZString

Depends on the effort,

https://www.ptc.com/en/products/developer-tools/perc

https://www.aicas.com/wp/products-services/jamaicavm/

It just happens that .NET never went down this path.