Skip to content
c80k edited this page Jun 19, 2019 · 7 revisions

Using the Generator Backend

The generator backend (capnp-csharp) is designed to be used in conjunction with the capnp tool (from the original Cap'n Proto tool suite). Technically it reads a Cap'n Proto-encoded code generation request from standard input and produces according C# files.

Deploy the VS capnp-csharp project to a standalone executable (refer to Microsoft instructions on how to deploy .NET Core applications) somewhere locally on your machine. From there have two options:

  • Either copy all files from the deployment target folder together with all files from the Cap'n Proto tool suite into a single location. This will provide you the most convenience because from now on you may invoke the tools like this:
capnp compile -ocsharp myschema.capnp
  • Alternatively, you may keep all executables at the locations where they currently are. Going with this option invoke them like this (path/to/capnp-csharp is the path to the executable inside your deployment target folder):
capnp compile -o"path/to/capnp-csharp" myschema.capnp

There are currently no C#-specific schema attributes. The backend does interpret the namespace attribute (see c++.capnp).

Beware of Resources: Dispose!

There is a major difference between C++ and C#: Whereas C++ takes you in responsibility of explicitly managing the memory for your objects, C# comes with a garbage collector (GC). A GC is a great thing because you usually don't need to worry about freeing allocated memory. But it is not a universal solution. Some resources need explicit management. So do Cap'n Proto capabilities. Once you obtained a capability from some other party, you should Dispose it when you no longer need it. As soon as you Dispose it, the Capnp.Net.Runtime will send a 'Release' message to the other side, indicating, that the capability is no longer needed. "Why not just add a Finalizer to the internal Proxy implementation and send the message once the GC collects it?", you might wonder. Well, actually there already is a Finalizer implementation doing exactly this. But this mechanism is just a last line of defense. You should explicitly Dispose any capability. Really. Maybe you've heard GC acts non-deterministically, so you cannot know when the Finalizer gets called. Why should you care? Because non-determinstally can also mean "never for (nearly) the entire lifetime of your applicaton". This might be the case if memory pressure is quite low, and GC decides that it is just not worth the effort of freeing a few bytes occupied by the Proxy object. Of course, the GC cannot know that there is much more behind the scenes: Image the other side is a small embedded device, with the capability implementation consuming lots of resources, awaiting urgently your release. Here we have the final answer: The GC just cannot make a qualified decision when it's time to release a capability because it will only ever consider memory usage of the local process.