Skip to content

Commit

Permalink
Save progress, as I am deleting the progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Perksey committed Sep 12, 2024
1 parent a76c488 commit 9cf86ba
Show file tree
Hide file tree
Showing 8 changed files with 624 additions and 64 deletions.
2 changes: 1 addition & 1 deletion generator.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"InputSourceRoot": "eng/submodules/terrafx.interop.windows/sources/Interop/Windows",
"InputTestRoot": "eng/submodules/terrafx.interop.windows/tests/Interop/Windows",
"OutputSourceRoot": "sources/Windows",
"OutputSourceRoot": "sources/Win32/Win32",
"OutputTestRoot": "tests/Windows",
"DefaultLicenseHeader": "eng/silktouch/header.txt",
"Solution": "Silk.NET.sln",
Expand Down
195 changes: 195 additions & 0 deletions sources/Core/Core/Abstractions/BreakneckLock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Sourced from https://github.com/john-h-k/SpinLockSlim under the MIT license

// MIT License
//
// Copyright (c) 2019 John Kelly
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System.Diagnostics;
using System.Runtime.CompilerServices;

// ReSharper disable RedundantAssignment

namespace Silk.NET.Core;

/// <summary>
/// Provided a lightweight spin lock for synchronization in high performance
/// scenarios with a low hold time
/// </summary>
/// <remarks>
/// This lock is very performant, but it is very dangerous (hence breakneck).
/// It's recommended to use the framework-provided locks where possible.
/// </remarks>
public struct BreakneckLock
{
private static int True => 1;
private static int False => 0;

private const MethodImplOptions MaxOpt = (MethodImplOptions)768;

private volatile int _acquired; // either 1 or 0

/// <summary>
/// Creates a new <see cref="BreakneckLock"/>
/// </summary>
/// <returns>A new <see cref="BreakneckLock"/></returns>
[MethodImpl(MaxOpt)]
public static BreakneckLock Create() => new();

/// <summary>
/// Returns <c>true</c> if the lock is acquired, else <c>false</c>
/// </summary>
#pragma warning disable 420 // Unsafe.As<,> doesn't read the reference so the lack of volatility is not an issue, but we do need to treat the returned reference as volatile
public bool IsAcquired => Volatile.Read(ref Unsafe.As<int, bool>(ref _acquired));
#pragma warning restore 420

/// <summary>
/// Enter the lock. If this method returns, <paramref name="taken"/>
/// will be <c>true</c>. If an exception occurs, <paramref name="taken"/> will indicate
/// whether the lock was taken and needs to be released using <see cref="Exit()"/>.
/// This method may never exit
/// </summary>
/// <param name="taken">A reference to a bool that indicates whether the lock is taken. Must
/// be <c>false</c> when passed, else the internal state or return state may be corrupted.
/// If the method returns, this is guaranteed to be <c>true</c></param>
[MethodImpl(MaxOpt)]
public void Enter(ref bool taken)
{
// while acquired == 1, loop, then when it == 0, exit and set it to 1
while (!TryAcquire())
{
// NOP
}

taken = true;
}

/// <summary>
/// Enter the lock if it not acquired, else, do not. <paramref name="taken"/> will be
/// <c>true</c> if the lock was taken, else <c>false</c>. If <paramref name="taken"/> is
/// <c>true</c>, <see cref="Exit()"/> must be called to release it, else, it must not be called
/// </summary>
/// <param name="taken">A reference to a bool that indicates whether the lock is taken. Must
/// be <c>false</c> when passed, else the internal state or return state may be corrupted</param>
[MethodImpl(MaxOpt)]
public void TryEnter(ref bool taken)
{
taken = TryAcquire();
}

/// <summary>
/// Try to safely enter the lock a certain number of times (<paramref name="iterations"/>).
/// <paramref name="taken"/> will be <c>true</c> if the lock was taken, else <c>false</c>.
/// If <paramref name="taken"/> is <c>true</c>, <see cref="Exit()"/> must be called to release
/// it, else, it must not be called
/// </summary>
/// <param name="taken">A reference to a bool that indicates whether the lock is taken. Must
/// be <c>false</c> when passed, else the internal state or return state may be corrupted</param>
/// <param name="iterations">The number of attempts to acquire the lock before returning
/// without the lock</param>
[MethodImpl(MaxOpt)]
public void TryEnter(ref bool taken, uint iterations)
{
// if it acquired == 0, change it to 1 and return true, else return false
while (!TryAcquire())
{
if (unchecked(iterations--) == 0) // postfix decrement, so no issue if iterations == 0 at first
{
return;
}
}

taken = true;
}

/// <summary>
/// Try to safely enter the lock for a certain <see cref="TimeSpan"/> (<paramref name="timeout"/>).
/// <paramref name="taken"/> will be <c>true</c> if the lock was taken, else <c>false</c>.
/// If <paramref name="taken"/> is <c>true</c>, <see cref="Exit()"/> must be called to release
/// it, else, it must not be called
/// </summary>
/// <param name="taken">A reference to a bool that indicates whether the lock is taken. Must
/// be <c>false</c> when passed, else the internal state or return state may be corrupted</param>
/// <param name="timeout">The <see cref="TimeSpan"/> to attempt to acquire the lock for before
/// returning without the lock. A negative <see cref="TimeSpan"/>will cause undefined behaviour</param>
[MethodImpl(MaxOpt)]
public void TryEnter(ref bool taken, TimeSpan timeout)
{
long start = Stopwatch.GetTimestamp();
long end = unchecked((long)timeout.TotalMilliseconds * Stopwatch.Frequency + start);

// if it acquired == 0, change it to 1 and return true, else return false
while (!TryAcquire())
{
if (Stopwatch.GetTimestamp() >= end)
{
return;
}
}

taken = true;
}

/// <summary>
/// Exit the lock. This method is dangerous and must be called only once the caller is sure they have
/// ownership of the lock.
/// </summary>
[MethodImpl(MaxOpt)]
public void Exit()
{
// release the lock - int32 write will always be atomic
_acquired = False;
}

/// <summary>
/// Exit the lock with an optional post-release memory barrier. This method is dangerous and must be called only
/// once the caller is sure they have ownership of the lock.
/// </summary>
/// <param name="insertMemBarrier">Whether a memory barrier should be inserted after the release</param>
[MethodImpl(MaxOpt)]
public void Exit(bool insertMemBarrier)
{
Exit();

if (insertMemBarrier)
Thread.MemoryBarrier();
}

/// <summary>
/// Exit the lock with a post-release memory barrier. This method is dangerous and must be called only once the
/// caller is sure they have ownership of the lock.
/// </summary>
[MethodImpl(MaxOpt)]
public void ExitWithBarrier()
{
Exit();
Thread.MemoryBarrier();
}

[MethodImpl(MaxOpt)]
private bool TryAcquire()
{
// if it acquired == 0, change it to 1 and return true, else return false
return Interlocked.CompareExchange(ref _acquired, True, False) == False;
}
}
51 changes: 3 additions & 48 deletions sources/SilkTouch/SilkTouch/Mods/Common/IMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,8 @@ namespace Silk.NET.SilkTouch.Mods;
public interface IMod
{
/// <summary>
/// Runs before SilkTouch does anything with the given job name and job configuration.
/// Initializes the mod.
/// </summary>
/// <param name="key">The job name (corresponds to the configuration key for mod configs).</param>
/// <param name="config">The job config.</param>
/// <returns>An asynchronous task.</returns>
Task BeforeJobAsync(string key, SilkTouchConfiguration config) => Task.CompletedTask;

/// <summary>
/// Runs before SilkTouch invokes ClangSharp with the given parsed response files. Gives each mod an opportunity to
/// modify the generator configuration.
/// </summary>
/// <param name="key">The job name (corresponds to the configuration key for mod configs).</param>
/// <param name="rsps">The read response files.</param>
/// <returns>
/// The modified response files to be passed into either the next mod or ClangSharp if this is the last mod.
/// </returns>
Task<List<ResponseFile>> BeforeScrapeAsync(string key, List<ResponseFile> rsps) =>
Task.FromResult(rsps);

/// <summary>
/// Runs after SilkTouch has invoked ClangSharp which generated the given syntax nodes. Gives each mod an
/// opportunity to mutate the syntax tree.
/// </summary>
/// <param name="key">The job name (corresponds to the configuration key for mod configs).</param>
/// <param name="syntax">The generated output from ClangSharp (or the previous mod).</param>
/// <returns>
/// The modified syntax nodes to be either passed to the next mod or output from the generator if this is the last
/// mod.
/// </returns>
Task<GeneratedSyntax> AfterScrapeAsync(string key, GeneratedSyntax syntax) =>
Task.FromResult(syntax);

/// <summary>
/// Runs before SilkTouch is going to output the MSBuild workspace. The generated documents have already been added,
/// so this gives the opportunity for the mod to modify the workspace further.
/// </summary>
/// <param name="key">The job name (corresponds to the configuration key for mod configs).</param>
/// <param name="workspace">The generated output from scraping.</param>
/// <returns>The modified MSBuild solution either to be output or passed to the next mod if applicable.</returns>
Task<GeneratorWorkspace> BeforeOutputAsync(string key, GeneratorWorkspace workspace) =>
Task.FromResult(workspace);

/// <summary>
/// Runs after all generation activities have completed. Gives each mod an opportunity to clean up its state for
/// this job.
/// </summary>
/// <param name="key">The job name (corresponds to the configuration key for mod configs).</param>
/// <returns>An asynchronous task.</returns>
Task AfterJobAsync(string key) => Task.CompletedTask;
/// <param name="context"></param>
void Initialize(IModContext context);
}
15 changes: 0 additions & 15 deletions sources/SilkTouch/SilkTouch/Mods/Common/IModConfigBinder.cs

This file was deleted.

76 changes: 76 additions & 0 deletions sources/SilkTouch/SilkTouch/Mods/Common/IModContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;

namespace Silk.NET.SilkTouch.Mods;

/// <summary>
/// An interface into the mod pipeline for mod execution.
/// </summary>
public interface IModContext
{
/// <summary>
/// The current job key.
/// </summary>
string JobKey { get; }

/// <summary>
/// Denotes this mod as producing artifacts of the given type.
/// </summary>
/// <param name="allowedModes">The modes allowed to be used for this artifact.</param>
/// <typeparam name="T">The type of the artifacts.</typeparam>
/// <remarks>
/// This method should be used if <see cref="AddArtifacts{T}"/> is not called prior to the enumeration of artifacts
/// added using <see cref="AddArtifacts{T}"/>. <see cref="AddArtifacts{T}"/> does enact the behaviour of this
/// function implicitly provided that it is called before <see cref="IMod.Initialize"/> exits.
/// </remarks>
void ProducesArtifacts<T>(
ModArtifactAccess allowedModes =
ModArtifactAccess.Exclusive | ModArtifactAccess.Shared | ModArtifactAccess.Take
);

/// <summary>
/// Denotes this mod as receiving artifacts of the given type.
/// </summary>
/// <param name="accessMode">The mode with which the artifacts shall be accessed.</param>
/// <typeparam name="T">The type of the artifacts.</typeparam>
/// <remarks>
/// This method should be used if <see cref="GetArtifacts{T}"/> is not called prior to the enumeration of artifacts
/// added using <see cref="AddArtifacts{T}"/>. <see cref="GetArtifacts{T}"/> does enact the behaviour of this
/// function implicitly provided that it is called before <see cref="IMod.Initialize"/> exits.
/// </remarks>
void ReceivesArtifacts<T>(ModArtifactAccess accessMode);

/// <summary>
/// Gets the artifacts of the given type from previous mods in the pipeline.
/// </summary>
/// <typeparam name="T">The type of the artifacts.</typeparam>
/// <returns>The artifacts.</returns>
/// <remarks>
/// This should be called within <see cref="IMod.Initialize"/>, but should be enumerated as part of the evaluation
/// of <see cref="AddArtifacts{T}"/>. Calling this and using the result either in a LINQ method chain or async
/// generator (i.e. <c>yield</c>) should be sufficient. If this is not achievable, use
/// <see cref="ReceivesArtifacts{T}"/>.
/// </remarks>
IAsyncEnumerable<T> GetArtifacts<T>(ModArtifactAccess accessMode);

/// <summary>
/// Adds the given artifacts to the pipeline.
/// </summary>
/// <param name="artifacts">The artifacts</param>
/// <param name="allowedModes">
/// The allowable modes with which the artifacts can be accessed. This is used to determine the concurrent flow of
/// artifacts through the pipeline. Generally this is expected to be consistent for any given artifact type, and
/// should reflect whether the artifact is stateful or not (i.e. stateful/mutable artifacts should use
/// <see cref="ModArtifactAccess.Exclusive"/> or <see cref="ModArtifactAccess.Take"/>, whereas immutable artifacts
/// should use <see cref="ModArtifactAccess.Shared"/> or <see cref="ModArtifactAccess.Take"/>). This is simply used
/// for validation of calls to <see cref="GetArtifacts{T}"/>/<see cref="ReceivesArtifacts{T}"/>.
/// </param>
/// <typeparam name="T">The type of the artifacts.</typeparam>
void AddArtifacts<T>(
IAsyncEnumerable<T> artifacts,
ModArtifactAccess allowedModes =
ModArtifactAccess.Exclusive | ModArtifactAccess.Shared | ModArtifactAccess.Take
);
}
12 changes: 12 additions & 0 deletions sources/SilkTouch/SilkTouch/Mods/Common/IModLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;

namespace Silk.NET.SilkTouch.Mods;

public interface IModLoader
{
bool TryLoadMod(string identifier, [NotNullWhen(true)] out IMod? mod);
}
Loading

0 comments on commit 9cf86ba

Please sign in to comment.