Skip to content

Commit f418a6c

Browse files
authored
Fixes #246 (#253)
Co-authored-by: smaillet <25911635+smaillet@users.noreply.github.com>
1 parent 5cf4537 commit f418a6c

1 file changed

Lines changed: 46 additions & 107 deletions

File tree

docfx/llvm/articles/InternalDetails/llvm-handles.md

Lines changed: 46 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,127 +3,66 @@ title: LLVM-C Handle Wrappers
33
---
44

55
## LLVM-C Handle wrappers
6-
7-
Handles for LLVM are just opaque pointers. They generally come in one of three forms.
6+
?Handles? for LLVM are just opaque pointers. They generally come in one of three forms.
87

98
1. Context owned
109
Where there is always a well known owner that ultimately is responsible for
1110
disposing/releasing the resource.
1211
2. Global resources
13-
Where there is no parent child ownership relationship and callers must manually release the resource
12+
Where there is no parent child ownership relationship and callers must manually release
13+
the resource.
1414
3. An unowned alias to a global resource
1515
This occurs when a child of a global resource contains a reference to the parent. In such
1616
a case the handle should be considered like an alias and not disposed.
1717

18-
The Handle implementations in Ubiquity.NET.Llvm follow consistent patterns for implementing each form of handle.
19-
20-
### Contextual handles
21-
22-
These handles are never manually released or disposed, though releasing their containers will make them
23-
invalid. The general pattern for implementing such handles is as follows:
24-
25-
``` C#
26-
using System;
27-
using System.Collections.Generic;
28-
29-
namespace Ubiquity.NET.Llvm.Native
30-
{
31-
internal struct LLVMxyzRef
32-
: IEquatable<LLVMxyzRef>
33-
{
34-
public override int GetHashCode( ) => Handle.GetHashCode( );
35-
36-
public override bool Equals( object obj )
37-
=> !( obj is null )
38-
&& ( obj is LLVMxyxRef r )
39-
&& ( r.Handle == Handle );
40-
41-
public bool Equals( LLVMxyxRef other )
42-
=> Handle == other.Handle;
43-
44-
public static bool operator ==( LLVMxyxRef lhs, LLVMxyxRef rhs )
45-
=> EqualityComparer<LLVMxyxRef>.Default.Equals( lhs, rhs );
46-
47-
public static bool operator !=( LLVMxyxRef lhs, LLVMxyxRef rhs )
48-
=> !( lhs == rhs );
49-
50-
internal LLVMxyxRef( IntPtr pointer )
51-
{
52-
Handle = pointer;
53-
}
54-
55-
private readonly IntPtr Handle;
56-
}
57-
}
58-
```
18+
The Handle implementations in Ubiquity.NET.Llvm follow consistent patterns for implementing
19+
each form of handle. All handle types are generated from the native C++ headers. Thus they are
20+
a source only NuGet package built along with the native extended C API library. The generated
21+
sources are not useful outside of the `Ubiquity.NET.Llvm.Interop` as they use classes within
22+
that as a base class. Ultimately, the handles are reduced to two forms:
23+
1) Requires caller to release them
24+
- Lifetime of the thing the handle refers to is controlled by the caller
25+
- Release is implemented by standard .NET pattern with [IDisposable](xref:System.IDisposable)
26+
2) Does NOT require any dispose
27+
- Lifetime of the thing the handle refers to is controlled by the container
28+
29+
>[!NOTE]
30+
> The use of code generation for the handles in a different repo is a bit fragile as the
31+
> generated handles are derived from and depend on support in a different consuming repository.
32+
> This is a result of the historical split of the native code libraries. The build of that,
33+
> takes a MUCH longer time AND requires distinct runners for each RID supported. While there is
34+
> thinking about how to unify these repositories that isn't done yet as the focus is on getting
35+
> the support for LLVM20.x and especially the JIT support. [It's been a long run with LLVM10 as
36+
> the only option.]
37+
38+
### Contextual handles and Aliases
39+
These handles are never manually released or disposed, though releasing their containers will
40+
make them invalid. The general pattern for implementing such handles is to use a generated
41+
struct that is marked as implementing the `IContextHandle<THandle>` interface. This interface
42+
is ONLY used during marshalling where the concreted type `THandle` is known and therefore does
43+
NOT require any boxing. The struct is essentially a strongly typed alias for an nint value.
44+
Contiguous sequences of these handles are re-interpret castable to a sequence of nint. (The
45+
interop support uses this for efficient marshalling of arrays.)
5946

6047
### Global Handles
61-
Global handles require the caller to explicitly release the resources. In Ubiquity.NET.Llvm these
62-
are managed with the .NET SafeHandles types through an Ubiquity.NET.Llvm specific derived type
63-
LlvmObject. Since these types are derived from a SafeHandle they are properly cleaned
64-
up by the runtime without the need to make the containing type implement IDisposable,
65-
though there may be other reasons to make a type Disposable. Generally, types should
66-
avoid IDisposable unless they really need to perform some special cleanup early or in
67-
a particular ordered sequence but such cases are rare.
68-
69-
All resource handles in Ubiquity.NET.Llvm requiring explicit release are handled consistently
70-
using the following basic pattern:
71-
72-
``` C#
73-
using System;
74-
using System.Runtime.InteropServices;
75-
using System.Security;
76-
77-
using static Ubiquity.NET.Llvm.Native.NativeMethods;
78-
79-
namespace Ubiquity.NET.Llvm.Native
80-
{
81-
[SecurityCritical]
82-
internal class LLVMxyzRef
83-
: LlvmObjectRef
84-
{
85-
public LLVMxyzRef( IntPtr handle, bool owner )
86-
: base( owner )
87-
{
88-
SetHandle( handle );
89-
}
90-
91-
[SecurityCritical]
92-
protected override bool ReleaseHandle( )
93-
{
94-
LLVMDisposeXyz( handle );
95-
return true;
96-
}
48+
Global handles require the caller to explicitly release the resources. In
49+
Ubiquity.NET.Llvm.Interop these are managed with the .NET [SafeHandle](xref:System.Runtime.InteropServices.SafeHandle)
50+
types through an LLVM interop specific derived type `GlobalHandleBase`. Since these types are
51+
derived from a `SafeHandle` they are cleaned up with the standard .NET [IDisposable](xref:System.IDisposable).
9752

98-
private LLVMxyzRef( )
99-
: base( true )
100-
{
101-
}
53+
All resource handles in `Ubiquity.NET.Llvm,Interop` requiring explicit release are handled consistently
54+
using the generated handle types as a distinct type derived from `GlobalHandleBase`
10255

103-
[DllImport( LibraryPath, CallingConvention = CallingConvention.Cdecl )]
104-
private static extern void LLVMDisposeXyz( IntPtr @xyz );
105-
}
106-
}
107-
```
56+
Global handles that also have an alias include a declaration of the alias type and allow
57+
conversion to the unowned form of the handle.
10858

10959
### Global Alias handles
110-
Global alias handles are a specialized form of global handles where they do not
111-
participate in ownership control/release. These are commonly used when a child
112-
of a global container exposes a property that references the parent container.
113-
In such cases the reference retrieved from the child shouldn't be used to destroy
114-
the parent when no longer used.
60+
Global alias handles are a specialized form of global handles where they do not participate in
61+
ownership control/release. These are commonly used when a child of a global container exposes
62+
a property that references the parent container. In such cases the reference retrieved from the
63+
child shouldn't be used to destroy the parent when no longer used.
11564

116-
In Ubiquity.NET.Llvm this is represented as a distinct handle type derived from the global
117-
handle as follows:
65+
In Ubiquity.NET.Llvm.Interop this is represented as an unowned context handle, that is alias
66+
handles are the same as a context handle. There is no way to convert from an unowned alias to
67+
an owned global handle (The other way around is allowed and supported)
11868

119-
``` C#
120-
// xyz alias
121-
internal class LLVMxyzAlias
122-
: LLVMxyzRef
123-
{
124-
private LLVMxyzAlias()
125-
: base( IntPtr.Zero, false )
126-
{
127-
}
128-
}
129-
```

0 commit comments

Comments
 (0)