Skip to content

Commit

Permalink
Add GetLooper API
Browse files Browse the repository at this point in the history
  • Loading branch information
mayuki committed Aug 4, 2023
1 parent 554ebfa commit 334d937
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 24 deletions.
4 changes: 2 additions & 2 deletions src/LogicLooper/ILogicLooper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ public interface ILogicLooper : IDisposable
Task RegisterActionAsync<TState>(LogicLooperActionWithStateDelegate<TState> loopAction, TState state);

/// <summary>
/// [Experimental] Registers a loop-frame action to the looper and returns <see cref="Task"/> to wait for completion.
/// [Experimental] Registers an async-aware loop-frame action to the looper and returns <see cref="Task"/> to wait for completion.
/// An asynchronous action is executed across multiple frames, differ from the synchronous version.
/// </summary>
/// <param name="loopAction"></param>
/// <returns></returns>
Task RegisterActionAsync(LogicLooperAsyncActionDelegate loopAction);

/// <summary>
/// [Experimental] Registers a loop-frame action with state object to the looper and returns <see cref="Task"/> to wait for completion.
/// [Experimental] Registers an async-aware loop-frame action with state object to the looper and returns <see cref="Task"/> to wait for completion.
/// An asynchronous action is executed across multiple frames, differ from the synchronous version.
/// </summary>
/// <param name="loopAction"></param>
Expand Down
6 changes: 6 additions & 0 deletions src/LogicLooper/ILogicLooperPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,10 @@ public interface ILogicLooperPool : IDisposable
/// <param name="shutdownDelay"></param>
/// <returns></returns>
Task ShutdownAsync(TimeSpan shutdownDelay);

/// <summary>
/// Gets a <see cref="ILogicLooper"/> instance from the pool. This is useful when you want to explicitly register multiple actions on the same loop thread.
/// </summary>
/// <returns></returns>
ILogicLooper GetLooper();
}
3 changes: 3 additions & 0 deletions src/LogicLooper/Internal/NotInitializedLogicLooperPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ public Task RegisterActionAsync<TState>(LogicLooperAsyncActionWithStateDelegate<
public Task ShutdownAsync(TimeSpan shutdownDelay)
=> throw new InvalidOperationException("LogicLooper.Shared is not initialized yet.");

public ILogicLooper GetLooper()
=> throw new InvalidOperationException("LogicLooper.Shared is not initialized yet.");

public void Dispose() { }
}
12 changes: 8 additions & 4 deletions src/LogicLooper/LogicLooperPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,30 @@ public LogicLooperPool(TimeSpan targetFrameTime, int looperCount, ILogicLooperPo

/// <inheritdoc />
public Task RegisterActionAsync(LogicLooperActionDelegate loopAction)
=> _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction);
=> GetLooper().RegisterActionAsync(loopAction);

/// <inheritdoc />
public Task RegisterActionAsync<TState>(LogicLooperActionWithStateDelegate<TState> loopAction, TState state)
=> _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction, state);
=> GetLooper().RegisterActionAsync(loopAction, state);

/// <inheritdoc />
public Task RegisterActionAsync(LogicLooperAsyncActionDelegate loopAction)
=> _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction);
=> GetLooper().RegisterActionAsync(loopAction);

/// <inheritdoc />
public Task RegisterActionAsync<TState>(LogicLooperAsyncActionWithStateDelegate<TState> loopAction, TState state)
=> _balancer.GetPooledLooper(_loopers).RegisterActionAsync(loopAction, state);
=> GetLooper().RegisterActionAsync(loopAction, state);

/// <inheritdoc />
public async Task ShutdownAsync(TimeSpan shutdownDelay)
{
await Task.WhenAll(_loopers.Select(x => x.ShutdownAsync(shutdownDelay)));
}

/// <inheritdoc />
public ILogicLooper GetLooper()
=> _balancer.GetPooledLooper(_loopers);

public void Dispose()
{
foreach (var looper in _loopers)
Expand Down
28 changes: 10 additions & 18 deletions src/LogicLooper/ManualLogicLooperPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,37 +43,29 @@ public void TickWhile(Func<bool> predicate)

/// <inheritdoc />
public void Dispose()
{
Loopers[0].Dispose();
}
=> Loopers[0].Dispose();

/// <inheritdoc />
public Task RegisterActionAsync(LogicLooperActionDelegate loopAction)
{
return Loopers[0].RegisterActionAsync(loopAction);
}
=> Loopers[0].RegisterActionAsync(loopAction);

/// <inheritdoc />
public Task RegisterActionAsync<TState>(LogicLooperActionWithStateDelegate<TState> loopAction, TState state)
{
return Loopers[0].RegisterActionAsync(loopAction, state);
}
=> Loopers[0].RegisterActionAsync(loopAction, state);

/// <inheritdoc />
public Task RegisterActionAsync(LogicLooperAsyncActionDelegate loopAction)
{
return Loopers[0].RegisterActionAsync(loopAction);
}
=> Loopers[0].RegisterActionAsync(loopAction);

/// <inheritdoc />
public Task RegisterActionAsync<TState>(LogicLooperAsyncActionWithStateDelegate<TState> loopAction, TState state)
{
return Loopers[0].RegisterActionAsync(loopAction, state);
}
=> Loopers[0].RegisterActionAsync(loopAction, state);

/// <inheritdoc />
public Task ShutdownAsync(TimeSpan shutdownDelay)
{
return Loopers[0].ShutdownAsync(shutdownDelay);
}
=> Loopers[0].ShutdownAsync(shutdownDelay);

/// <inheritdoc />
public ILogicLooper GetLooper()
=> Loopers[0];
}
20 changes: 20 additions & 0 deletions test/LogicLooper.Test/LogicLooperPoolTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,24 @@ public void RegisterActionAsync()

executedCount.Should().Be(actionCount * loopCount);
}

[Fact]
public void GetLooper()
{
using var pool = new LogicLooperPool(60, 4, new FakeSequentialLogicLooperPoolBalancer());
pool.GetLooper().Should().Be(pool.Loopers[0]);
pool.GetLooper().Should().Be(pool.Loopers[1]);
pool.GetLooper().Should().Be(pool.Loopers[2]);
pool.GetLooper().Should().Be(pool.Loopers[3]);
pool.GetLooper().Should().Be(pool.Loopers[0]);
}

class FakeSequentialLogicLooperPoolBalancer : ILogicLooperPoolBalancer
{
private int _count;
public Cysharp.Threading.LogicLooper GetPooledLooper(Cysharp.Threading.LogicLooper[] pooledLoopers)
{
return pooledLoopers[_count++ % pooledLoopers.Length];
}
}
}
9 changes: 9 additions & 0 deletions test/LogicLooper.Test/ManualLogicLooperPoolTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,13 @@ public void RegisterActionAsync()
pool.Loopers[0].ApproximatelyRunningActions.Should().Be(0);
t1.IsCompletedSuccessfully.Should().BeTrue();
}

[Fact]
public void GetLooper()
{
var pool = new ManualLogicLooperPool(60.0);
var looper = pool.GetLooper();

looper.Should().Be(pool.FakeLooper);
}
}

0 comments on commit 334d937

Please sign in to comment.