Skip to content

Commit afc2e86

Browse files
committed
Refactor ReaderWriterLockLight to native-backed interop
Major overhaul of ReaderWriterLockLight in Hexa.NET.Utilities: - Replaced C# lock-free implementation with native-backed version using new C API (ReaderWriterLock.h) and auto-generated C# bindings. - Added Generator project to auto-generate C# interop from C headers via HexaGen. - Updated native library (HexaUtils) to support Windows, Linux, macOS, Android (x64/arm64/x86); .csproj and NuGet packaging updated for new runtimes/ layout. - Updated hexa-workflows.json for new native binary locations. - ReaderWriterLockLight struct now wraps native lock and delegates all operations to native code via generated interop. - Improved fairness and documentation in native ReaderWriterLock. - Enhanced XML docs for WaitOnAddressHelper.cs. - Updated solution and project files for new structure and dependencies. - Replaced all old native binaries with new, platform-specific versions. - Added auto-generated interop files to C# codebase. - Improves maintainability, cross-platform support, and interop best practices.
1 parent e194288 commit afc2e86

27 files changed

Lines changed: 732 additions & 56 deletions

Generator/Generator.csproj

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net10.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="HexaGen" Version="1.1.23.5" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<None Update="generator.json">
16+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
17+
</None>
18+
<None Update="include\common.h">
19+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
20+
</None>
21+
<None Update="include\ReaderWriterLock.h">
22+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
23+
</None>
24+
<None Update="include\utils.h">
25+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
26+
</None>
27+
</ItemGroup>
28+
29+
</Project>

Generator/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// See https://aka.ms/new-console-template for more information
2+
using HexaGen;
3+
using HexaGen.Patching;
4+
5+
var files = Directory.GetFiles("include", "*.h", SearchOption.AllDirectories).ToList();
6+
GeneratorBuilder.Create<CsCodeGenerator>("generator.json")
7+
.WithPrePatch(new NamingPatch(["HexaUtils"], NamingPatchOptions.CaseInsensitive))
8+
.Generate("include/utils.h", "../../../../Hexa.NET.Utilities/Native/", files);

Generator/generator.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"Namespace": "Hexa.NET.Utilities.Native",
3+
"ApiName": "HexaUtils",
4+
"LibName": "HexaUtils",
5+
"GenerateConstructorsForStructs": true,
6+
"GeneratePlaceholderComments": false,
7+
"GenerateMetadata": false,
8+
"WrapPointersAsHandle": true,
9+
"ImportType": "FunctionTable",
10+
"TypeMappings": {
11+
},
12+
"Defines": [ "HEXA_UTILS_BUILD_SHARED=1" ]
13+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#ifndef HEXA_UTILS_ReaderWriterLock_H
2+
#define HEXA_UTILS_ReaderWriterLock_H
3+
4+
#include "common.h"
5+
#include <stdio.h>
6+
7+
/**
8+
* @brief A lock-free reader-writer lock.
9+
*
10+
* Allows multiple concurrent readers or a single exclusive writer.
11+
* Writers are given fairness: once a writer is waiting, new readers
12+
* will block until the writer has acquired and released the lock.
13+
*
14+
* Must be initialized with ReaderWriterLock_Init() before use.
15+
*/
16+
typedef struct ReaderWriterLock_t
17+
{
18+
size_t storage;
19+
} ReaderWriterLock;
20+
21+
/**
22+
* @brief Initializes a ReaderWriterLock.
23+
* @param cLock Pointer to the lock to initialize.
24+
*/
25+
HEXA_UTILS_API(void) ReaderWriterLock_Init(ReaderWriterLock* cLock);
26+
27+
/**
28+
* @brief Acquires a read lock, blocking until it is available.
29+
*
30+
* Blocks if a writer currently holds or is waiting for the lock.
31+
*
32+
* @param cLock Pointer to the lock.
33+
* @return 1 on success, -1 if the reader count would overflow.
34+
*/
35+
HEXA_UTILS_API(int) ReaderWriterLock_LockRead(ReaderWriterLock* cLock);
36+
37+
/**
38+
* @brief Attempts to acquire a read lock without blocking.
39+
*
40+
* @param cLock Pointer to the lock.
41+
* @return 1 if the read lock was acquired, 0 if a writer holds or is waiting
42+
* for the lock, -1 if the reader count would overflow.
43+
*/
44+
HEXA_UTILS_API(int) ReaderWriterLock_TryLockRead(ReaderWriterLock* cLock);
45+
46+
/**
47+
* @brief Releases a previously acquired read lock.
48+
* @param cLock Pointer to the lock.
49+
*/
50+
HEXA_UTILS_API(void) ReaderWriterLock_UnlockRead(ReaderWriterLock* cLock);
51+
52+
/**
53+
* @brief Acquires a write lock, blocking until all readers and other writers
54+
* have released the lock.
55+
* @param cLock Pointer to the lock.
56+
*/
57+
HEXA_UTILS_API(void) ReaderWriterLock_LockWrite(ReaderWriterLock* cLock);
58+
59+
/**
60+
* @brief Attempts to acquire a write lock without blocking.
61+
*
62+
* @param cLock Pointer to the lock.
63+
* @param preserveWriterFairness If true, the call blocks until all active
64+
* readers drain before returning success, preventing writer starvation.
65+
* If false, the call returns 0 immediately when readers are active.
66+
* @return 1 if the write lock was acquired, 0 if another writer already holds
67+
* the lock (or readers are active and @p preserveWriterFairness is false).
68+
*/
69+
HEXA_UTILS_API(int) ReaderWriterLock_TryLockWrite(ReaderWriterLock* cLock, bool preserveWriterFairness);
70+
71+
/**
72+
* @brief Releases a previously acquired write lock.
73+
* @param cLock Pointer to the lock.
74+
*/
75+
HEXA_UTILS_API(void) ReaderWriterLock_UnlockWrite(ReaderWriterLock* cLock);
76+
77+
#endif

Generator/include/common.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef HEXA_UTILS_COMMON_H
2+
#define HEXA_UTILS_COMMON_H
3+
4+
#include <stdint.h>
5+
/* Calling convention */
6+
7+
#if defined(_WIN32) || defined(_WIN64)
8+
#define HEXA_UTILS_CALL __cdecl
9+
#elif defined(__GNUC__) || defined(__clang__)
10+
#define HEXA_UTILS_CALL __attribute__((__cdecl__))
11+
#else
12+
#define HEXA_UTILS_CALL
13+
#endif
14+
15+
/* API export/import */
16+
#if defined(_WIN32) || defined(_WIN64)
17+
#ifdef HEXA_UTILS_BUILD_SHARED
18+
#define HEXA_UTILS_EXPORT
19+
#else
20+
#define HEXA_UTILS_EXPORT
21+
#endif
22+
#elif defined(__GNUC__) || defined(__clang__)
23+
#ifdef HEXA_UTILS_BUILD_SHARED
24+
#define HEXA_UTILS_EXPORT __attribute__((visibility("default")))
25+
#else
26+
#define HEXA_UTILS_EXPORT
27+
#endif
28+
#else
29+
#define HEXA_UTILS_EXPORT
30+
#endif
31+
32+
#if defined __cplusplus
33+
#define HEXA_UTILS_EXTERN extern "C"
34+
#else
35+
#include <stdarg.h>
36+
#include <stdbool.h>
37+
#define HEXA_UTILS_EXTERN extern
38+
#endif
39+
40+
#define HEXA_UTILS_API(type) HEXA_UTILS_EXTERN HEXA_UTILS_EXPORT type HEXA_UTILS_CALL
41+
#define HEXA_UTILS_API_INTERNAL(type) HEXA_UTILS_EXTERN type HEXA_UTILS_CALL
42+
43+
#endif

Generator/include/utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef HEXA_UTILS_H
2+
#define HEXA_UTILS_H
3+
4+
#include "ReaderWriterLock.h"
5+
6+
#endif

Hexa.NET.Utilities.sln

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 17
4-
VisualStudioVersion = 17.10.35027.167
3+
# Visual Studio Version 18
4+
VisualStudioVersion = 18.3.11520.95 d18.3
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hexa.NET.Utilities", "Hexa.NET.Utilities\Hexa.NET.Utilities.csproj", "{055E63D3-810C-4289-97D4-E27BCDB86548}"
77
EndProject
@@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hexa.NET.Utilities.Tests",
1111
EndProject
1212
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hexa.NET.Unsafe.Analyzers", "Hexa.NET.Unsafe.Analyzers\Hexa.NET.Unsafe.Analyzers.csproj", "{5F79B930-69FB-4395-AAC5-5A776D94F2AE}"
1313
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generator", "Generator\Generator.csproj", "{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}"
15+
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1618
Debug|Any CPU = Debug|Any CPU
@@ -51,6 +53,14 @@ Global
5153
{5F79B930-69FB-4395-AAC5-5A776D94F2AE}.Release|Any CPU.Build.0 = Release|Any CPU
5254
{5F79B930-69FB-4395-AAC5-5A776D94F2AE}.Release|x86.ActiveCfg = Release|Any CPU
5355
{5F79B930-69FB-4395-AAC5-5A776D94F2AE}.Release|x86.Build.0 = Release|Any CPU
56+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Debug|Any CPU.Build.0 = Debug|Any CPU
58+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Debug|x86.ActiveCfg = Debug|Any CPU
59+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Debug|x86.Build.0 = Debug|Any CPU
60+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Release|x86.ActiveCfg = Release|Any CPU
63+
{5A76BB29-57FE-4B59-A883-C1FC95EBAF88}.Release|x86.Build.0 = Release|Any CPU
5464
EndGlobalSection
5565
GlobalSection(SolutionProperties) = preSolution
5666
HideSolutionNode = FALSE

Hexa.NET.Utilities/Hexa.NET.Utilities.csproj

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,65 @@
4444
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
4545
</ItemGroup>
4646

47+
<ItemGroup>
48+
<None Include="native\android-arm64\*.so">
49+
<Link>runtimes\android-arm64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
50+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
51+
<PackagePath>runtimes/android-arm64/native</PackagePath>
52+
<Pack>true</Pack>
53+
</None>
54+
<None Include="native\android-x64\*.so">
55+
<Link>runtimes\android-x64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
56+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
57+
<PackagePath>runtimes/android-x64/native</PackagePath>
58+
<Pack>true</Pack>
59+
</None>
60+
<None Include="native\osx-arm64\*.dylib">
61+
<Link>runtimes\osx-arm64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
62+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
63+
<PackagePath>runtimes/osx-arm64/native</PackagePath>
64+
<Pack>true</Pack>
65+
</None>
66+
<None Include="native\osx-x64\*.dylib">
67+
<Link>runtimes\osx-x64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
68+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
69+
<PackagePath>runtimes/osx-x64/native</PackagePath>
70+
<Pack>true</Pack>
71+
</None>
72+
<None Include="native\linux-arm64\*.so">
73+
<Link>runtimes\linux-arm64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
74+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
75+
<PackagePath>runtimes/linux-arm64/native</PackagePath>
76+
<Pack>true</Pack>
77+
</None>
78+
<None Include="native\linux-x64\*.so">
79+
<Link>runtimes\linux-x64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
80+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
81+
<PackagePath>runtimes/linux-x64/native</PackagePath>
82+
<Pack>true</Pack>
83+
</None>
84+
<None Include="native\win-arm64\*.dll">
85+
<Link>runtimes\win-arm64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
86+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
87+
<PackagePath>runtimes/win-arm64/native</PackagePath>
88+
<Pack>true</Pack>
89+
</None>
90+
<None Include="native\win-x64\*.dll">
91+
<Link>runtimes\win-x64\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
92+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
93+
<PackagePath>runtimes/win-x64/native</PackagePath>
94+
<Pack>true</Pack>
95+
</None>
96+
<None Include="native\win-x86\*.dll">
97+
<Link>runtimes\win-x86\native\%(RecursiveDir)%(Filename)%(Extension)</Link>
98+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
99+
<PackagePath>runtimes/win-x86/native</PackagePath>
100+
<Pack>true</Pack>
101+
</None>
102+
</ItemGroup>
103+
104+
<ItemGroup>
105+
<PackageReference Include="HexaGen.Runtime" Version="1.1.24" />
106+
</ItemGroup>
107+
47108
</Project>

Hexa.NET.Utilities/HexaUtils.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Hexa.NET.Utilities.Native
2+
{
3+
public static partial class HexaUtils
4+
{
5+
static HexaUtils()
6+
{
7+
InitApi();
8+
}
9+
10+
public static string GetLibraryName()
11+
{
12+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
13+
{
14+
return "HexaUtils";
15+
}
16+
17+
return "libHexaUtils";
18+
}
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
// ------------------------------------------------------------------------------
9+
10+
using System;
11+
using HexaGen.Runtime;
12+
13+
namespace Hexa.NET.Utilities.Native
14+
{
15+
public unsafe partial class HexaUtils
16+
{
17+
public const int HEXA_UTILS_BUILD_SHARED = 1;
18+
19+
}
20+
}

0 commit comments

Comments
 (0)