|
| 1 | +# Mono code guidelines |
| 2 | + |
| 3 | +This document is meant to capture guidelines for contributing code to |
| 4 | +the [src/mono/mono/](../../src/mono/mono), |
| 5 | +[src/native/public/mono](../../src/native/public/mono) areas of the |
| 6 | +dotnet/runtime repo. |
| 7 | + |
| 8 | +In general this guide does not apply to: |
| 9 | + |
| 10 | +1. Shared native code in [src/native](../../src/native) |
| 11 | +2. The Mono-specific C# code in [src/mono](../../src/mono) in System.Private.CoreLib or elsewhere |
| 12 | + |
| 13 | +## Code style |
| 14 | + |
| 15 | +Mono is written in C. |
| 16 | + |
| 17 | +We follow the [Mono Coding guidelines](https://www.mono-project.com/community/contributing/coding-guidelines/) for the C code - in particular: |
| 18 | + |
| 19 | +* tabs, not spaces |
| 20 | +* space between a function name and the open parenthesis |
| 21 | +* braces on the same line as `if`, `for`, `while` etc |
| 22 | + |
| 23 | +## Naming |
| 24 | + |
| 25 | +Mono reserves the following prefixes for symbols: `mono_`, `monovm_`, `m_`, `monoeg_` (used to remap [`eglib`](../../src/mono/mono/eglib) |
| 26 | +functions which in source code have a `g_` prefix). |
| 27 | + |
| 28 | +All non-`static` symbols should use one of these prefixes. We generally use `mono_` for most |
| 29 | +functions. `m_` is used for some inline accessor functions and macros. `g_` (`mono_eg_`) is used |
| 30 | +for `eglib` functions. |
| 31 | + |
| 32 | +Types in a single C file can use any name. Types in a header should use a `Mono` (or sometimes |
| 33 | +`mono_`) prefix. |
| 34 | + |
| 35 | +Public API symbols and types *must* be prefixed. |
| 36 | + |
| 37 | +For types Mono generally uses `typedef struct _MonoWhatever { ... } MonoWhatever`. Opaque types may |
| 38 | +define `typedef struct _MonoWhatever MonoWhatever` in a client header and define `struct |
| 39 | +_MonoWhatever {...}` in an implementation header. Occasionally we break `#include` cycles by adding |
| 40 | +forward declarations for some types. |
| 41 | + |
| 42 | +## Macros |
| 43 | + |
| 44 | +Mono derives from an autotools-style project so we generally write `HOST_XYZ` for the machine on |
| 45 | +which the runtime is executing (as opposed to the machine on which the runtime was compiled), and |
| 46 | +`TARGET_XYZ` for the machine that will be targeted by the JIT and AOT compilers. In the case of AOT |
| 47 | +compilation host and target might be different: the host might be Windows, and the target might be |
| 48 | +Browser WebAssembly, for example. |
| 49 | + |
| 50 | +Macros generally use a `MONO_` prefix. Macros in public API headers *must* use the prefix. |
| 51 | + |
| 52 | +## Types |
| 53 | + |
| 54 | +Prefer the standard C sized types `int32_t`, `intptr_t`, etc over the eglib types `gint32`, `gsize` etc. |
| 55 | + |
| 56 | +One exception is `gboolean` is prefered over C `bool`. |
| 57 | + |
| 58 | +There are actually three boolean types to keep in mind: |
| 59 | + |
| 60 | +* `gboolean` used internally in the runtime |
| 61 | +* `MonoBoolean` used as an interop type with C# bool in internal calls |
| 62 | +* `mono_bool` used by the public C API - generally new code shouldn't use it except when adding a new public API function. |
| 63 | + |
| 64 | +## Utility and platform abstraction functions |
| 65 | + |
| 66 | +Mono generally tries to fill in POSIX-like abstractions on platforms that lack them (for example, Windows). |
| 67 | +This is in contrast to the CoreCLR PAL which generally tries to add Windows API abstractions on top |
| 68 | +of POSIX. |
| 69 | + |
| 70 | +If an existing `eglib` utility function is available, it should be used. New ones can be added. |
| 71 | + |
| 72 | +Other platform specific code is in `src/mono/utils` |
| 73 | + |
| 74 | +## Directory dependencies |
| 75 | + |
| 76 | +For the code in `src/mono/mono`: |
| 77 | + |
| 78 | +* `eglib` should not depend on other code from `src/mono` |
| 79 | + |
| 80 | +* `utils` can depend on `eglib` and 3rd party code from `src/native/external` |
| 81 | + |
| 82 | +* `sgen` depends on `eglib` and `utils` |
| 83 | + |
| 84 | +* `metadata` depends on all of the above (but note that some Boehm-GC files in this directory should not depend on `sgen`) |
| 85 | + |
| 86 | +* `mini` depends on all of the above |
| 87 | + |
| 88 | +* `mini/interp` depends on all of the above |
| 89 | + |
| 90 | +* `components` can use functions from all of the above provided they're marked with |
| 91 | + `MONO_COMPONENT_API`, see [../design/mono/components.md](../design/mono/components.md) |
| 92 | + |
| 93 | +The main distinction between `metadata` and `utils` is that utils code should not assume that it is |
| 94 | +part of an executing .NET runtime - anything that loads types, creates objects, etc does not belong |
| 95 | +in utils but in metadata. |
| 96 | + |
| 97 | +The `mini` directory contains execution engines. If the execution engine has to provide some |
| 98 | +functionality to `metadata` it generally does so by installing some callback that is invoked by |
| 99 | +`metadata`. For example, `mini` knows how to unwind exceptions and perform stack walks - while |
| 100 | +`metadata` decides *when* to unwind or perform a stackwalk. To coordinate, `metadata` exposes an |
| 101 | +API to install hooks and `mini` provides the implementations. |
| 102 | + |
| 103 | +## Error handling |
| 104 | + |
| 105 | +New code should prefer to use the `MonoError` functions. |
| 106 | + |
| 107 | +* A non-public-API function should take a `MonoError *` argument. |
| 108 | +* In case of an error the function should call one of the `mono_error_set_` functions from [../../src/mono/mono/utils/mono-error-internals.h](../../src/mono/mono/utils/mono-error-internals.h) |
| 109 | +* Inside the runtime check whether there was an error by calling `is_ok (error)`, `mono_error_assert_ok (error)`, `goto_if_nok` or `return_if_nok`/`return_val_if_nok` |
| 110 | +* If there is an error and you're handling it, call `mono_error_cleanup (error)` to dispose of the resources. |
| 111 | +* `MonoError*` is generally one-shot: after it's been cleaned up it needs to be re-inited with `mono_error_init_reuse`, but this is discouraged. |
| 112 | +* Instead if you intend to deal with an error, use `ERROR_DECL (local_error)` then call the |
| 113 | + possibly-failing function and pass it `local_error`, then call `mono_error_assert_ok |
| 114 | + (local_error)` if it "can't fail" or `mono_error_cleanup (local_error)` if you want to ignore the |
| 115 | + failure. |
| 116 | + |
| 117 | +## Managed Exceptions |
| 118 | + |
| 119 | +New code should generally not deal with `MonoException*`, use `MonoError*` instead. |
| 120 | + |
| 121 | +New code should avoid using `mono_error_set_pending_exception` - it affects a thread-local flag in |
| 122 | +a way that is not obvious to the caller of your function and may trample existing in-flight |
| 123 | +exceptions and make your code fail in unexpected ways. |
| 124 | + |
| 125 | +There are two circumstances when you might need to call `mono_error_set_pending_exception`: |
| 126 | + |
| 127 | +1. You're working with a public Mono API that sets a pending exception |
| 128 | +2. You're implementing an icall, but can't use `HANDLES()` (see the [internal calls](#internal-calls) section below) |
| 129 | + |
| 130 | +## Internal calls |
| 131 | + |
| 132 | +Prefer P/Invokes or QCalls over internal calls. That is, if your function only takes arguments that |
| 133 | +are not managed objects, and does not need to interact with the runtime, it is better to define it |
| 134 | +outside the runtime. |
| 135 | + |
| 136 | +Internal calls generally have at least one argument that is a managed object, or may throw a managed exception. |
| 137 | + |
| 138 | +Internal calls are declared in [`icall-def.h`](../../src/mono/mono/metadata/icall-def.h) See the comment in the header for details. |
| 139 | + |
| 140 | +There are two styles of internal calls: `NOHANDLES` and `HANDLES`. (This is a simplification as there |
| 141 | +are also JIT internal calls added by the execution engines) |
| 142 | + |
| 143 | +The difference is that `HANDLES` icalls receive references to managed objects wrapped in a handle |
| 144 | +that attempts to keep the object alive for the duration of the internal call even if the thread |
| 145 | +blocks or is suspended, while `NOHANDLES` functions don't. Additionally `HANDLES` functions get a |
| 146 | +`MonoError*` argument from the managed-to-native interop layer that will be converted to a managed |
| 147 | +exception when the function returns. `NOHANDLES` functions generally have to call |
| 148 | +`mono_error_set_pending_exception` themselves. |
| 149 | + |
| 150 | +## Suspend Safety |
| 151 | + |
| 152 | +See [Cooperative Suspend at mono-project.com](https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/) and the [mono thread state machine design document](../design/mono/mono-thread-state-machine.md) |
| 153 | + |
| 154 | +In general runtime functions that may be called from the public Mono API should call |
| 155 | +`MONO_ENTER_GC_UNSAFE`/`MONO_EXIT_GC_UNSAFE` if they call any other runtime API. Internal calls are |
| 156 | +already in GC Unsafe on entry, but QCalls and P/Invokes aren't. |
| 157 | + |
| 158 | +When calling a blocking native API, wrap the call in `MONO_ENTER_GC_SAFE`/`MONO_EXIT_GC_SAFE`. |
| 159 | + |
| 160 | +Prefer `mono_coop_` synchronization primitives (`MonoCoopMutex`, `MonoCoopCond`, |
| 161 | +`MonoCoopSemaphore`, etc) over `mono_os_` primitives (`mono_mutex_t`, `mono_cond_t`). The former |
| 162 | +will automatically go into GC Safe mode before doing operations that may block. The latter should |
| 163 | +only be used for low-level leaf locks that may need to be shared with non-runtime code. You're |
| 164 | +responsible for switching to GC Safe mode when doing blocking operations in that case. |
| 165 | + |
| 166 | +## GC Memory Safety |
| 167 | + |
| 168 | +We have explored many different policies on how to safely access managed memory from the runtime. The existing code is not uniform. |
| 169 | + |
| 170 | +This is the current policy (but check with a team member as this document may need to be updated): |
| 171 | + |
| 172 | +1. It is never ok to access a managed object from GC Safe code. |
| 173 | +2. Mono's GC scans the managed heap precisely. Mono does not allow heap objects to contain pointers into the interior of other managed obejcts. |
| 174 | +3. Mono's GC scans the native stack conservatively: any value that looks like a pointer into the GC |
| 175 | + heap, will cause the target object to be pinned and not collected. Interior pointers from the |
| 176 | + stack into the middle of a managed object are allowed and will pin the object. |
| 177 | + |
| 178 | +In general one of the following should be used to ensure that an object is kept alive, particularly |
| 179 | +across a call back into managed from native, or across a call that may trigger a GC (effectively |
| 180 | +nearly any runtime internal API, due to assembly loading potentially triggering managed callbacks): |
| 181 | + |
| 182 | +* The object is pinned in managed code using `fixed` (common with strings) and the native code gets a `gunichar2*` |
| 183 | +* a `ref` local is passed into native code and the native code gets a `MonoObject * volatile *` (the `volatile` is important) |
| 184 | +* a `Span<T>` is passed into native code and the native code gets a `MonoSpanOfObjects*` |
| 185 | +* an icall is declared with `HANDLES()` and a `MonoObjectHandle` (or a more specific type such as `MonoReflectionTypeHandle` is passed in). |
| 186 | +* a GCHandle is passed in |
| 187 | + |
| 188 | +Generally only functions on the boundary between managed and native should use one of the above |
| 189 | +mechanisms (that is, it's enough that an object is pinned once). Callees can take a `MonoObject*` |
| 190 | +argument and assume that it was pinned by the caller. |
| 191 | + |
| 192 | +In cases where an object is created in native code, it should be kept alive: |
| 193 | + |
| 194 | +1. By assigning into a `MonoObject * volatile *` (ie: use `out` or `ref` arguments in C#) |
| 195 | +2. By creating a local handle using `MONO_HANDLE_NEW` or `MONO_HANDLE_PIN` (the function should then use `HANDLE_FUNCTION_ENTER`/`HANDLE_FUNCTION_RETURN` to set up and tear down a handle frame) |
| 196 | +3. By creating a GCHandle |
| 197 | +4. By assigning to a field of another managed object |
| 198 | + |
| 199 | +In all cases, if there is any intervening internal API call or a call to managed code, the object |
| 200 | +must be kept alive before the call using one of the above methods. |
| 201 | + |
| 202 | +### Write barriers |
| 203 | + |
| 204 | +When writing a managed object to a field of another managed object, use one of the |
| 205 | +`mono_gc_wbarrier_` functions (for example, `mono_gc_wbarrier_generic_store`). It is ok to call the write |
| 206 | +barrier functions if the destination is not in the managed heap (in which case they will just do a normal write) |
| 207 | + |
| 208 | +## Assertions |
| 209 | + |
| 210 | +Mono code should use `g_assert`, `mono_error_assert_ok`, `g_assertf`, `g_assert_not_reached` etc. |
| 211 | + |
| 212 | +Unlike CoreCLR, Mono assertions are always included in the runtime - both in Debug and in Release builds. |
| 213 | + |
| 214 | +New code should try not to rely on the side-effects of assert conditions. (That is, one day we may want |
| 215 | +to turn off assertions in Release builds.) |
| 216 | + |
| 217 | +## Mono Public API |
| 218 | + |
| 219 | +Mono maintains a public API for projects that embed the Mono runtime in order to provide the ability |
| 220 | +to execute .NET code in the context of another application or framework. The current Mono API is |
| 221 | +`mono-2.0`. We strive to maintain binary ABI and API stability for our embedders. |
| 222 | + |
| 223 | +The public API headers are defined in |
| 224 | +[`../../src/native/public/mono`](../../src/native/public/mono). Great care should be taken when |
| 225 | +modifying any of the functions declared in these headers. In particular breaking the ABI by |
| 226 | +removing these functions or changing their arguments is not allowed. Semantic changes should be |
| 227 | +avoided, or implemented in a way that is least disruptive to embedders (for example the runtime does |
| 228 | +not support multiple appdomains anymore, but `mono_domain_get` continues to work). |
| 229 | + |
| 230 | +### Hidden API functions |
| 231 | + |
| 232 | +In practice certain functions have been tagged with `MONO_API` even if they are not declared in the |
| 233 | +public headers. These symbols have been used by projects that embed Mono despite not being in the |
| 234 | +public headers. They should be treated with the same care as the real public API. |
| 235 | + |
| 236 | +### Unstable API functions |
| 237 | + |
| 238 | +Functions declared in `mono-private-unstable.h` headers do not need to maintain API/ABI stability. |
| 239 | +They are generally new APIs that have been added since .NET 5 but that have not been stabilized yet. |
| 240 | + |
| 241 | +As a matter of courtesy, notify the .NET macios and .NET Android teams if changing the behavior of these functions. |
| 242 | + |
| 243 | +### WASM |
| 244 | + |
| 245 | +The WASM and WASI runtimes in [`src/mono/wasm/runtime`](../../src/mono/wasm/runtime) and |
| 246 | +`src/mono/wasi` are effectively external API clients. When possible they should use existing `MONO_API` functions. |
| 247 | + |
| 248 | +As a matter of expedience, the wasm project has sometimes taken advantage of static linking by |
| 249 | +adding declarations of internal Mono functions in `src/mono/wasm/runtime/driver.c` and directly |
| 250 | +calling Mono internals. |
| 251 | + |
| 252 | +In general new code should not do this. When modifying existing code, mysterious WASM failures may |
| 253 | +be attributed to symbol signature mismatches between WASM and the Mono runtime. |
0 commit comments