Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog
All notable changes to this project will be documented in this file using the standards as defined at [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0).

### Version 1.0.0 *(2024-01-26)*
Improvements:
- Added `MainThreadDispatcher` class.
- Added `TaskExtensionTest` class.
- Structured project as a UPM compatible package.
7 changes: 7 additions & 0 deletions CHANGELOG.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions Chartboost.CSharp.Threading.Unity.nuspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>Chartboost.CSharp.Threading.Unity</id>
<version>1.0.0</version>
<title>Chartboost Threading Utilities for Unity</title>
<description>Reusable Threading Utilities for Chartboost's Unity Projects</description>
<authors>Chartboost</authors>
<owners>Chartboost</owners>
<license type="file">LICENSE.md</license>
<readme>README.md</readme>
<copyright>Copyright 2024</copyright>
<tags>Chartboost, Ads, Mediation, Unity, cs</tags>
<repository type="git" url="https://github.com/ChartBoost/chartboost-unity-threading"/>
<dependencies>
<dependency id="Chartboost.CSharp.Utilities.Unity" version="1.0.0" />
</dependencies>
</metadata>
</package>
7 changes: 7 additions & 0 deletions Chartboost.CSharp.Threading.Unity.nuspec.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright 2023 Chartboost Inc.

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.
7 changes: 7 additions & 0 deletions LICENSE.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,66 @@
# chartboost-unity-threading
# Chartboost MainThreadDispatcher Plugin for Unity
A way of dispatching functions to the main thread in Unity projects. Useful for functions that Unity limits to the main thread from different threads.

# Installation
This package is meant to be a dependency for other Chartboost Packages;however, if you wish to use it by itself, it can be installed through UPM & NuGet as follows:

```json
"dependencies": {
"com.chartboost.unity.threading": "1.0.0",
...
},
"scopedRegistries": [
{
"name": "NpmJS",
"url": "https://registry.npmjs.org",
"scopes": [
"com.chartboost"
]
}
]
```

# Usage

## Simple Actions
Utilize the following methods to execute calls on the main thread:

```csharp
void TestAction(object state){
//Execute logic on main thread
Debug.Log("This is called in the main thread")
}

// Synchronous; blocks until the callback completes
MainThreadDispatcher.Send(TestAction);

// Asynchronous; send and forget
MainThreadDispatcher.Post(TestAction)
```

## Tasks
Taks can be utilized in Unity. However, if they contain code that must run on the Unity main thread, the Task too should also be run in the main thread. Use the following:

```csharp
MainThreadDispatcher.MainThreadTask(async () =>{
// Mostly useful when calling task initially from outside of the Unity environment
await myTask();
});
```

## Task Continuations
Task continuations are useful when trying to call asynchronous code from a synchronous environment. The following examples represent the same logic.

```csharp
private void MySyncrhonousMethod(){
MyAsynchornousTask().ContinueWithOnMainThread(taskContinuationResultTask => {
// perform any continuation logic here.
Debug.Log("My task finished!")
});
}

private async void MyAsyncrhonousMethod(){
var taskResult = awat MyAsynchronousTask();
Debug.Log("My task finished!")
}
```
7 changes: 7 additions & 0 deletions README.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Runtime.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions Runtime/AwaitableAndroidJavaProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine;

namespace Chartboost
{
public class AwaitableAndroidJavaProxy<TResult> : AndroidJavaProxy
{
public TaskAwaiter<TResult> GetAwaiter()
{
if (_taskCompletionSource != null)
return _taskCompletionSource.Task.GetAwaiter();

_taskCompletionSource = new TaskCompletionSource<TResult>();

if (_isComplete)
_setResult();
else
DidComplete += result => _setResult();

return _taskCompletionSource.Task.GetAwaiter();
}

protected AwaitableAndroidJavaProxy(string nativeInterface) : base(nativeInterface) { }

protected void _complete(TResult result)
{
if (_isComplete)
return;

_result = result;
var toComplete = DidComplete;
DidComplete = null;
_isComplete = true;
toComplete?.Invoke(_result);
}

private void _setResult()
{
try
{
_taskCompletionSource.TrySetResult(_result);
}
catch (ObjectDisposedException e)
{
Debug.Log(e.Message);
}
}

private TaskCompletionSource<TResult> _taskCompletionSource;
private event Action<TResult> DidComplete;
private TResult _result;
private bool _isComplete;
}
}
3 changes: 3 additions & 0 deletions Runtime/AwaitableAndroidJavaProxy.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions Runtime/AwaitableProxies.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;

namespace Chartboost
{
/// <summary>
/// Utility class utilized to resolve awaitable proxies for C based wrappers.
/// </summary>
public static class AwaitableProxies
{
private static readonly Dictionary<int, ILater> WaitingProxies = new Dictionary<int, ILater>();

public static (Later<TResult> proxy, int hashCode) SetupProxy<TResult>()
{
var proxy = new Later<TResult>();
var hashCode = proxy.GetHashCode();
WaitingProxies[hashCode] = proxy;
return (proxy, hashCode);
}

public static void ResolveCallbackProxy<TResponse>(int hashCode, TResponse response) {
if (!WaitingProxies.ContainsKey(hashCode))
return;
if (WaitingProxies[hashCode] is Later<TResponse> later) later.Complete(response);
WaitingProxies.Remove(hashCode);
}
}
}
11 changes: 11 additions & 0 deletions Runtime/AwaitableProxies.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions Runtime/Chartboost.Threading.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Chartboost.Threading",
"rootNamespace": "Chartboost",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [
""
],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
7 changes: 7 additions & 0 deletions Runtime/Chartboost.Threading.asmdef.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions Runtime/Later.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Chartboost
{
/// <summary>For referencing <see cref="ILater{TResult}"/> generically</summary>
public interface ILater { }

/// <summary>Read-only interface for a standard <see cref="Later{TResult}"/></summary>
public interface ILater<TResult> : ILater
{
event Action<TResult> OnComplete;

TaskAwaiter<TResult> GetAwaiter();
}

/// <summary>Basic Later for passing a single type to callbacks and awaiters</summary>
public class Later<TResult> : BaseLater<TResult>
{
public void Complete(TResult result) => _complete(result);
}

/// <summary>Separated implementation so the derivations can offer different methods for completion</summary>
public abstract class BaseLater<TResult> : ILater<TResult>
{
public event Action<TResult> OnComplete
{
remove => DidComplete -= value;
add
{
if (_isComplete)
DidComplete += value;
else
value?.Invoke(_result);
}
}

public TaskAwaiter<TResult> GetAwaiter()
{
if (_completionSource != null)
return _completionSource.Task.GetAwaiter();

_completionSource = new TaskCompletionSource<TResult>();

if (_isComplete)
_completionSource.TrySetResult(_result);
else
DidComplete += result => _completionSource.TrySetResult(result);

return _completionSource.Task.GetAwaiter();
}

protected void _complete(TResult result)
{
if (_isComplete)
return;

_result = result;

var toComplete = DidComplete;
DidComplete = null;

_isComplete = true;
toComplete?.Invoke(_result);
}

private TaskCompletionSource<TResult> _completionSource;
private event Action<TResult> DidComplete;
private bool _isComplete;
private TResult _result;
}
}
3 changes: 3 additions & 0 deletions Runtime/Later.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading