The Interlock
helper class can ease the locking of resources to prevent multiple requests to modify it.
Usage example:
using RestServer;
using RestServer.Helper;
using UnityEngine;
public class TOMover : MonoBehaviour {
public RestServer.RestServer server;
private SimpleInterlock _lock = new SimpleInterlock();
void Start() {
server.EndpointCollection.RegisterEndpoint(HttpMethod.GET, "/move", MoveHandler);
}
public void MoveHandler(RestRequest request) {
if (_lock.isRunning) {
request.SendAsyncErrorResponse(423, "Animation is still running.");
} else {
ThreadingHelper.Instance.ExecuteAsyncCoroutine(DoAnimation);
request.SendAsyncOkResponse();
}
}
public IEnumerator DoAnimation() {
using (var interlock = _lock.DoWork()) {
// do animation
}
}
}
This code example shows how to protect a unity animation or any long running process from being triggered twice by incoming requests. It handles requests, while the animation is running, gracefully.
Notes:
ReaderWriteLockSlim
is used in favor of lock
or other lock classes, so there is an easy and fast way to check if the animation is runningif (_animationRunning == newState) {
after entering the _lock.EnterWriteLock();
is mandatory and no mistake. There could be a second request that is handed out the write lock before we have aquired it and would result in starting the animation twice.using System.Collections;
using System.Threading;
using RestServer;
using UnityEngine;
public class ExampleAnimation : MonoBehaviour {
private RestServer.RestServer server;
private readonly ReaderWriterLockSlim _lock = new();
private bool _animationRunning;
void Start() {
server.EndpointCollection.RegisterEndpoint(HttpMethod.POST, "/animation/start", request => {
if (IsAnimationRunning()) {
// Quick and save check if the animation is running
request.SendAsyncErrorResponse(423, "Animation is still running");
}
// Start the animation in the main rendering thread
var animationStarted = ThreadingHelper.Instance.ExecuteSync(StartAnimation);
if (animationStarted) {
request.SendAsyncGetResponse("OK");
} else {
request.SendAsyncErrorResponse(423, "Animation is still running");
}
});
}
/// <summary>
/// Start the animation and report the result
/// </summary>
/// <returns>False if animation is still running; true if it was started with this call.</returns>
public bool StartAnimation() {
if (!MarkAnimation(true)) {
// animation already running
return false;
}
// Animation can be started
StartCoroutine(DoAnimation());
return true;
}
private IEnumerator DoAnimation() {
// Animation Step 1
yield return new WaitForSeconds(1.0f);
// Animation Step 2
yield return new WaitForSeconds(1.0f);
// Animation Finished
MarkAnimation(false /* finished */);
yield return null;
}
private bool IsAnimationRunning() {
try {
_lock.EnterReadLock();
return _animationRunning;
}
finally {
_lock.ExitReadLock();
}
}
private bool MarkAnimation(bool newState) {
try {
_lock.EnterWriteLock();
if (_animationRunning == newState) {
return false;
}
_animationRunning = newState;
}
finally {
_lock.ExitWriteLock();
}
return true;
}
}