SlideShare une entreprise Scribd logo
1  sur  105
BEHIND MODERN
CONCURRENCY PRIMITIVES
INTRODUCTION
Bartosz Sypytkowski
▪ @Horusiath
▪ b.sypytkowski@gmail.com
▪ bartoszsypytkowski.com
 Concurrency in user vs. kernel space
 Thread pools
 Schedulers
 Coroutines
 State machines
AGENDA
WHY DO WE USE THIS?
ASYNC/AWAIT
Task.Run(async () =>
{
await using var conn = new SqlConnection(ConnectionString);
await conn.OpenAsync();
var user = await conn.QueryFirstAsync<User>(
"SELECT * FROM Users WHERE Id = @id",
new { id = 100 });
Console.WriteLine($"Received user {user.FirstName} {user.LastName}");
});
INSTEAD OF THIS?
THREAD API
new Thread(() =>
{
var conn = new SqlConnection(ConnectionString);
conn.Open();
var user = conn.QueryFirst<User>(
"SELECT * FROM Users WHERE Id = @id",
new { id = 100 });
Console.WriteLine($"Received user {user.FirstName} {user.LastName}");
}).Start();
Thread Pool*
Managed
Thread
Managed
Thread
OS
Thread
THREADS TASKS
User space
Kernel space
User space
Kernel space
Managed
Thread
OS
Thread
OS
Thread
Scheduler
OS
Thread
OS
Thread
OS
Thread
Task Task Task Task Task Task
Task Task Task Task Task Task
Task Task Task Task Task Task
THREAD POOLS
THREAD
POOL
BASICS
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Job Queue
PINNING THREAD TO CPU
var threads = Process.GetCurrentProcess().Threads;
var cpuCount = Environment.ProcessorCount;
Console.WriteLine($"Using {threads.Count} threads on {cpuCount} cores.");
// => Using 10 threads on 4 cores.
for (int i = 0; i < threads.Count; i++)
{
var pthread = threads[i];
var cpuMask = (1L << (i % cpuCount));
pthread.IdealProcessor = i % cpuCount; // set preferred thread(s)
pthread.ProcessorAffinity = (IntPtr)cpuMask; // set thread affinity mask
}
NOTE: it’s not always possible (iOS) or respected.
SHARING DATA IN MULTI-CPU ARCHITECTURE
HARDWARE
ARCHITECTURE
101 CPU 1
L1 cache
L1 cache
L3 cache
CPU 2
L1 cache
L1 cache
RAM
CPU L1 cache: 1ns
CPU L2 cache: 4-7ns
CPU L2 cache: 100ns
HARDWARE
ARCHITECTURE
NUMA CPU 1
CPU 2
L1-2
cache
CPU 3
CPU 4
L1-2
cache
CPU 5
CPU 6
L1-2
cache
CPU 7
CPU 8
L1-2
cache
THREAD
POOL
MULTIPLE
QUEUES
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
THREAD
POOL
WORK STEALING
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Job
Job Job Job
Job
Job
Job Job Job Job
THREAD
POOL
WORK STEALING
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Job
Job Job Job
Job
Job
? Job Job Job
Empty
Queue
THREAD
POOL
WORK STEALING
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Job
Job Job Job
Job
Job
? Job Job Job
THREAD
POOL
WORK STEALING
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Job Job Job
Job
Job
Job Job Job
Job
COMPAR
ING
VECTOR
CLOCKS
AFFINITY
BASED
THREAD POOL
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
S1
S1
S1
S1
S2
S2
S3
S2
S3
S3
S4
S5
S4
S4
S5
Always map entity to the same thread / core
AFFINITY THREAD POOL: PERFORMANCE
Source: https://scalac.io/improving-akka-dispatchers/
THREAD-PER-CORE
ARCHITECTURE
WORKING WITH I/O
SCENARIO #1
READING A FILE
WHY IS THIS
CODE BAD?
static async Task ProcessFiles(FileInfo[] files)
{
var tasks = new Task[files.Length];
for (int i = 0; i < files.Length; i++)
tasks[i] = ProcessFile(files[i]);
await Task.WhenAll(tasks);
}
static async Task ProcessFile(FileInfo file)
{
using var stream = file.OpenRead();
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
Process(text);
}
WHY IS THIS
CODE BAD?
static async Task ProcessFiles(FileInfo[] files)
{
var tasks = new Task[files.Length];
for (int i = 0; i < files.Length; i++)
tasks[i] = ProcessFile(files[i]);
await Task.WhenAll(tasks);
}
static async Task ProcessFile(FileInfo file)
{
using var stream = file.OpenRead();
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
Process(text);
}
Blocking the thread belonging
to a thread pool shared with
other tasks.
I/O
1. Just use async I/O API…
WHAT CAN WE DO
ABOUT IT?
I/O
1. Just use async I/O API…
2. … but what when it’s not possible?
WHAT CAN WE DO
ABOUT IT?
I/O
1. Just use async I/O API…
2. … but what when it’s not possible?
WHAT CAN WE DO
ABOUT IT?
internal static class LmdbMethods
{
[DllImport("lmdb", CallingConvention = CallingConvention.Cdecl)]
public static extern int mdb_dbi_open(IntPtr txn, string name, DatabaseOpenFlags flags, out uint db);
[DllImport("lmdb", CallingConvention = CallingConvention.Cdecl)]
public static extern int mdb_cursor_get(IntPtr cursor, ref ValueStructure key, ref ValueStructure
data, CursorOperation op);
[DllImport("lmdb", CallingConvention = CallingConvention.Cdecl)]
public static extern int mdb_cursor_put(IntPtr cursor, ref ValueStructure key, ref ValueStructure
value, CursorPutOptions flags);
// other methods
}
THREAD
POOL
I/O THREADS
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Managed
Thread
I/O
Threads
Managed
Thread
Managed
Thread
THREAD
POOL
I/O THREADS
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Managed
Thread
I/O
Threads
Managed
Thread
Managed
Thread
J1
THREAD
POOL
I/O THREADS
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Managed
Thread
I/O
Threads
Managed
Thread
Managed
Thread
J1
THREAD
POOL
I/O THREADS
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Managed
Thread
I/O
Threads
Managed
Thread
Managed
Thread
J1
THREAD
POOL
I/O THREADS
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Managed
Thread
I/O
Threads
Managed
Thread
Managed
Thread
J2
GOROUTINES & I/O
DEALING WITH KERNEL CALLS
COMPAR
ING
VECTOR
CLOCKS
GOROUTINES
I/O CALLS
CPU 1
Agent
Runtime
Thread
CPU 2
Agent
Runtime
Thread
CPU 3
Agent
Runtime
Thread
CPU 4
Agent
Runtime
Thread
COMPAR
ING
VECTOR
CLOCKS
GOROUTINES
I/O CALLS
CPU 1
Agent
Runtime
Thread
CPU 2
Agent
Runtime
Thread
CPU 3
Agent
Runtime
Thread
CPU 4
Agent
Runtime
Thread
sys
call
COMPAR
ING
VECTOR
CLOCKS
GOROUTINES
I/O CALLS
CPU 1
Agent
CPU 2
Agent
Runtime
Thread
CPU 3
Agent
Runtime
Thread
CPU 4
Agent
Runtime
Thread
Runtime
Thread
sys
call
COMPAR
ING
VECTOR
CLOCKS
GOROUTINES
I/O CALLS
CPU 1
Agent
CPU 2
Agent
Runtime
Thread
CPU 3
Agent
Runtime
Thread
CPU 4
Agent
Runtime
Thread
Runtime
Thread
sys
call
Runtime
Thread
SCHEDULERS
Preemptive vs. Cooperative
Scheduler depends on coroutines
to return control to scheduler.
PREEMPTIVE
Scheduler is in charge of
execution. Coroutines can be
preempted.
COOPERATIVE
SCENARIO #2
CREATE AN EXCEL FILE
COMPAR
ING
VECTOR
CLOCKS
BUILDING
AN EXCEL
FILE
app.get('/valuations.xlsx', async (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
var valuations = await getValuations(req.params.id);
for (let valuation in valuations) {
worksheet.addRow(valuation);
}
await workbook.xlsx.write(res);
});
COMPAR
ING
VECTOR
CLOCKS
BUILDING
AN EXCEL
FILE
app.get('/valuations.xlsx', async (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
var valuations = await getValuations(req.params.id);
for (let valuation in valuations) {
worksheet.addRow(valuation); // 500μs
}
await workbook.xlsx.write(res);
});
COMPAR
ING
VECTOR
CLOCKS
BUILDING
AN EXCEL
FILE
app.get('/valuations.xlsx', async (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
var valuations = await getValuations(req.params.id);
for (let valuation in valuations) { // 20,000 items
worksheet.addRow(valuation); // 500μs
}
await workbook.xlsx.write(res);
});
COMPAR
ING
VECTOR
CLOCKS
BUILDING
AN EXCEL
FILE
app.get('/valuations.xlsx', async (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
var valuations = await getValuations(req.params.id);
for (let valuation in valuations) { // 20,000 items
worksheet.addRow(valuation); // 500μs
}
await workbook.xlsx.write(res);
});
Total loop execution time: 10 seconds
HOW TO DEAL WITH LONG RUNNING TASKS?
LONG
RUNNING
TASK
SEPARATE
THREAD
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Scheduler
LONG
RUNNING
TASK
SEPARATE
THREAD
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Scheduler
Task
Task.Factory.StartNew(DownloadExcel, TaskCreationOptions.LongRunning)
LONG
RUNNING
TASK
SEPARATE
THREAD
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Scheduler
Task
Task.Factory.StartNew(DownloadExcel, TaskCreationOptions.LongRunning)
Managed
Thread
LONG
RUNNING
TASK
SEPARATE
THREAD
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Scheduler
Managed
Thread
Task
LONG
RUNNING
TASK
SEPARATE
THREAD
CPU 1
Agent
Managed
Thread
CPU 2
Agent
Managed
Thread
CPU 3
Agent
Managed
Thread
CPU 4
Agent
Managed
Thread
Scheduler
Managed
Thread
Task
COMPAR
ING
VECTOR
CLOCKS
INTRODUCING
INTERRUPTION
POINTS
function park() {
return new Promise((resolve) => setTimeout(resolve, 0));
}
app.get('/valuations.xlsx', async (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
var valuations = await getValuations(req.params.id);
var i = 0;
for (let valuation in valuations) { // 20,000 items
if ((i++) % 100 === 0) {
await park();
}
worksheet.addRow(valuation); // 500μs
}
await workbook.xlsx.write(res);
});
PREEMPTIVE SCHEDULER
step-based / time-based
STEP-BASED PREEMPTION
% hello world program
-module(helloworld).
-export([start/0, write/1]).
write([]) -> ok;
write([File|T]) ->
io:fwrite("Writing a file ~p~n", File),
write(T).
start() ->
Files = ["A", "B", "C"],
write(Files).
STEP-BASED PREEMPTION
Reduction counter under the hood
% hello world program
-module(helloworld).
-export([start/0, write/1]).
write([], Reductions) -> ok;
write([File|T], Reductions) ->
% if Reductions == 0 then yield()
io:fwrite("Writing a file ~p~n", File),
write(T, Reductions-1).
start() ->
Files = ["A", "B", "C"],
write(Files, 2000).
• JavaScript promises
• .NET Task Parallel Library
• Java/Scala Futures
PREEMPTIVE
• OS threads
• Go goroutines
• Erlang processes
COOPERATIVE
COROUTINES
eager vs. lazy
let run () = async {
let sw = Stopwatch()
sw.Start()
let t1 = Async.Sleep(5000)
let t2 = Async.Sleep(5000)
do! t1
do! t2
printfn "Time passed: %ims" sw.ElapsedMilliseconds
}
static async Task Run()
{
var sw = new Stopwatch();
sw.Start();
var t1 = Task.Delay(TimeSpan.FromSeconds(5));
var t2 = Task.Delay(TimeSpan.FromSeconds(5));
await t1;
await t2;
Console.WriteLine(
$"Time passed: {sw.ElapsedMilliseconds}ms");
}
let run () = async {
let sw = Stopwatch()
sw.Start()
let t1 = Async.Sleep(5000)
let t2 = Async.Sleep(5000)
do! t1
do! t2
printfn "Time passed: %ims" sw.ElapsedMilliseconds
}
static async Task Run()
{
var sw = new Stopwatch();
sw.Start();
var t1 = Task.Delay(TimeSpan.FromSeconds(5));
var t2 = Task.Delay(TimeSpan.FromSeconds(5));
await t1;
await t2;
Console.WriteLine(
$"Time passed: {sw.ElapsedMilliseconds}ms");
}
5 seconds 10 seconds
let run () = async {
let sw = Stopwatch()
sw.Start()
let t1 = Async.Sleep(5000)
let t2 = Async.Sleep(5000)
do! t1
do! t2
printfn "Time passed: %ims" sw.ElapsedMilliseconds
}
static async Task Run()
{
var sw = new Stopwatch();
sw.Start();
var t1 = Task.Delay(TimeSpan.FromSeconds(5));
var t2 = Task.Delay(TimeSpan.FromSeconds(5));
await t1;
await t2;
Console.WriteLine(
$"Time passed: {sw.ElapsedMilliseconds}ms");
}
EAGER LAZY
5 seconds 10 seconds
COROUTINES
stackless vs. stackful
COMPAR
ING
VECTOR
CLOCKS
STACKLESS
COROUTINES
OLD CALLBACK API
app.get('/valuations.xlsx', function (req, res, next) {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
getValuations(req.params.id, function (err, valuations) {
for (let valuation in valuations) {
worksheet.addRow(valuation);
}
workbook.xlsx.write(res, function (err) {
next();
});
});
});
COMPAR
ING
VECTOR
CLOCKS
STACKLESS
COROUTINES
app.get('/valuations.xlsx', (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
return getValuations(req.params.id)
.then((valuations) => {
for (let valuation in valuations) {
worksheet.addRow(valuation);
}
return workbook.xlsx.write(res);
});
});
OLD PROMISE API
COMPAR
ING
VECTOR
CLOCKS
STACKLESS
COROUTINES
app.get('/valuations.xlsx', async (req, res) => {
var workbook = new Excel.Workbook();
var worksheet = workbook.addWorksheet("Valuations");
var valuations = await getValuations(req.params.id);
for (let valuation in valuations) {
worksheet.addRow(valuation);
}
await workbook.xlsx.write(res);
});
PROBLEM OF FUNCTION COLOURING
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
Start of
coroutine
stack
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
print activation
frame
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
print activation
frame
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
Park
coroutine
stack
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
Park
coroutine
stack
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
print activation
frame
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
Resume
coroutine
stack
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
STACKFUL
COROUTINES
STACK
main activation
frame
function foo(a, b)
print("foo", a)
bar("foo", b)
end
function bar(caller, x)
print(“bar: ", caller)
coroutine.yield()
print(“bar", x)
end
co = coroutine.create(foo)
coroutine.resume(co, 1, 2)
print(“main”)
coroutine.resume(co)
foo activation
frame
bar activation
frame
print activation
frame
WHAT IS THE CORRECT STACK SIZE FOR EACH
COROUTINE?
• JavaScript promises
• .NET Task Parallel Library
• Java/Scala Futures
STACKFUL
• Go goroutines
• LUA coroutines
• (soon) Java Loom project
STACKLESS
STACKLESS COROUTINES AND
YIELD GENERATORS
ASYNC/AWAIT UNITY COROUTINES
IEnumerable WaitAndPrint()
{
print("Starting at " + Time.time);
yield return new WaitForSeconds(0.1f);
print("Ending at " + Time.time);
}
async Task WaitAndPrint()
{
Console.WriteLine("Starting at " + DateTime.Now);
await Task.Delay(100);
Console.WriteLine("Ending at " + DateTime.Now);
}
INTEGRATING TPL AND LINQ
public static class TaskExtensions
{
public static Task<T3> SelectMany<T1, T2, T3>(
this Task<T1> task,
Func<T1, Task<T2>> binding,
Func<T1, T2, T3> combine)
{
var tcs = new TaskCompletionSource<T3>();
task.ContinueWith(t1 =>
{
if (t1.IsFaulted) tcs.SetException(t1.Exception);
else if (t1.IsCanceled) tcs.SetCanceled();
else binding(t1.Result).ContinueWith(t2 =>
{
if (t2.IsFaulted) tcs.SetException(t2.Exception);
else if (t2.IsCanceled) tcs.SetCanceled();
else tcs.SetResult(combine(t1.Result, t2.Result));
});
});
return tcs.Task;
}
}
public static Task Rename(int userId, string name)
{
return from user in GetUser(userId)
from _ in SaveUser(user with { Name = name })
select 0;
}
BEHIND ASYNC/AWAIT
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
WHAT YOU SEE WHAT YOU DON’T SEE
static class Program {
static async Task Foo(int a, int b) {
Console.WriteLine("Foo", a);
await Task.Yield();
Console.WriteLine("Foo", b);
}
}
struct Foo__0 : IStateMachine {
AsyncTaskMethodBuilder builder;
int state;
int a;
int b;
public void MoveNext() {
switch (this.state) {
case -1:
Console.WriteLine("Foo", this.a);
var awaiter = Taks.Yield().GetAwaiter();
this.state = 0;
this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
// ^ awaiter.OnComplete(() =>
// ThreadPool.QueueUserWorkItem(this.MoveNext));
return;
case 0:
Console.WriteLine("Foo", this.b);
builder.SetResult();
return;
}
}
}
static class Program {
static Task Foo(int a, int b) {
var builder = AsyncTaskMethodBuilder.Create();
var stateMachine = new Foo__0();
// initialize state machine fields
stateMachine.a = a;
stateMachine.b = b;
stateMachine.state = -1;
stateMachine.builder = builder;
builder.Start(ref stateMachine);
// ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext);
return builder.Task;
}
}
AWAITER PATTERN
Custom awaitable objects
public readonly struct PromiseAwaiter<T> : INotifyCompletion
{
private readonly Promise<T> promise;
public PromiseAwaiter(Promise<T> promise)
{
this.promise = promise;
}
#region mandatory awaiter methods
public bool IsCompleted => promise.IsCompleted;
public T GetResult() => promise.Result;
public void OnCompleted(Action continuation) => promise.RegisterContinuation(continuation);
#endregion
}
ASYNC METHOD BUILDER
Custom async methods
public struct PromiseAsyncMethodBuilder<T>
{
private Promise<T>? promise;
#region mandatory methods for async state machine builder
public static PromiseAsyncMethodBuilder<T> Create() => default;
public Promise<T> Task => promise ??= new Promise<T>();
public void SetException(Exception e) => Task.TrySetException(e);
public void SetResult(T result) => Task.TrySetResult(result);
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine { }
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { }
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
#endregion
}
COST OF ASYNC
SUMMARY
 Custom AsyncMethodBuilder “docs”: https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md
 Building custom (affine) thread pool: https://bartoszsypytkowski.com/thread-safety-with-affine-thread-pools/
 What color is your function?: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
 How Go scheduler works: https://www.youtube.com/watch?v=-K11rY57K7k
 Thread per core architecture: https://www.datadoghq.com/blog/engineering/introducing-glommio/
REFERENCES
THANK YOU

Contenu connexe

Tendances

Programming with ZooKeeper - A basic tutorial
Programming with ZooKeeper - A basic tutorialProgramming with ZooKeeper - A basic tutorial
Programming with ZooKeeper - A basic tutorial
Jeff Smith
 

Tendances (20)

Reactive Java (33rd Degree)
Reactive Java (33rd Degree)Reactive Java (33rd Degree)
Reactive Java (33rd Degree)
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListenerNode.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
 
bluespec talk
bluespec talkbluespec talk
bluespec talk
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAvoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
 
Programming with ZooKeeper - A basic tutorial
Programming with ZooKeeper - A basic tutorialProgramming with ZooKeeper - A basic tutorial
Programming with ZooKeeper - A basic tutorial
 
Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)
 
Understanding greenlet
Understanding greenletUnderstanding greenlet
Understanding greenlet
 
Non Blocking I/O for Everyone with RxJava
Non Blocking I/O for Everyone with RxJavaNon Blocking I/O for Everyone with RxJava
Non Blocking I/O for Everyone with RxJava
 
Callbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascriptCallbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascript
 
Event loop
Event loopEvent loop
Event loop
 
Reactive programming with RxJava
Reactive programming with RxJavaReactive programming with RxJava
Reactive programming with RxJava
 
Asynchronní programování
Asynchronní programováníAsynchronní programování
Asynchronní programování
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
 
Reactive programming with RxAndroid
Reactive programming with RxAndroidReactive programming with RxAndroid
Reactive programming with RxAndroid
 
Concurrency Concepts in Java
Concurrency Concepts in JavaConcurrency Concepts in Java
Concurrency Concepts in Java
 
CLS & asyncListener: asynchronous observability for Node.js
CLS & asyncListener: asynchronous observability for Node.jsCLS & asyncListener: asynchronous observability for Node.js
CLS & asyncListener: asynchronous observability for Node.js
 
Building Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaBuilding Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJava
 
Reactive Java (GeeCON 2014)
Reactive Java (GeeCON 2014)Reactive Java (GeeCON 2014)
Reactive Java (GeeCON 2014)
 
The Future of Futures - A Talk About Java 8 CompletableFutures
The Future of Futures - A Talk About Java 8 CompletableFuturesThe Future of Futures - A Talk About Java 8 CompletableFutures
The Future of Futures - A Talk About Java 8 CompletableFutures
 

Similaire à Behind modern concurrency primitives

Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
Tom Croucher
 
fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)
fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)
fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)
Wesley Beary
 

Similaire à Behind modern concurrency primitives (20)

Server side JavaScript: going all the way
Server side JavaScript: going all the wayServer side JavaScript: going all the way
Server side JavaScript: going all the way
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Binary Studio Academy: Concurrency in C# 5.0
Binary Studio Academy: Concurrency in C# 5.0Binary Studio Academy: Concurrency in C# 5.0
Binary Studio Academy: Concurrency in C# 5.0
 
CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016
CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016
CI Provisioning with OpenStack - Gidi Samuels - OpenStack Day Israel 2016
 
Container orchestration from theory to practice
Container orchestration from theory to practiceContainer orchestration from theory to practice
Container orchestration from theory to practice
 
Async programming and python
Async programming and pythonAsync programming and python
Async programming and python
 
Phil Basford - machine learning at scale with aws sage maker
Phil Basford - machine learning at scale with aws sage makerPhil Basford - machine learning at scale with aws sage maker
Phil Basford - machine learning at scale with aws sage maker
 
Seastar @ NYCC++UG
Seastar @ NYCC++UGSeastar @ NYCC++UG
Seastar @ NYCC++UG
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Machine learning at scale with aws sage maker
Machine learning at scale with aws sage makerMachine learning at scale with aws sage maker
Machine learning at scale with aws sage maker
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
Play Framework
Play FrameworkPlay Framework
Play Framework
 
fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)
fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)
fog or: How I Learned to Stop Worrying and Love the Cloud (OpenStack Edition)
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
 
Workflow as code with Azure Durable Functions
Workflow as code with Azure Durable FunctionsWorkflow as code with Azure Durable Functions
Workflow as code with Azure Durable Functions
 
Seastar @ SF/BA C++UG
Seastar @ SF/BA C++UGSeastar @ SF/BA C++UG
Seastar @ SF/BA C++UG
 
What the CRaC - Superfast JVM startup
What the CRaC - Superfast JVM startupWhat the CRaC - Superfast JVM startup
What the CRaC - Superfast JVM startup
 
fog or: How I Learned to Stop Worrying and Love the Cloud
fog or: How I Learned to Stop Worrying and Love the Cloudfog or: How I Learned to Stop Worrying and Love the Cloud
fog or: How I Learned to Stop Worrying and Love the Cloud
 
Real World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js ApplicationsReal World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js Applications
 

Plus de Bartosz Sypytkowski

Plus de Bartosz Sypytkowski (12)

Postgres indexes: how to make them work for your application
Postgres indexes: how to make them work for your applicationPostgres indexes: how to make them work for your application
Postgres indexes: how to make them work for your application
 
How do databases perform live backups and point-in-time recovery
How do databases perform live backups and point-in-time recoveryHow do databases perform live backups and point-in-time recovery
How do databases perform live backups and point-in-time recovery
 
Scaling connections in peer-to-peer applications
Scaling connections in peer-to-peer applicationsScaling connections in peer-to-peer applications
Scaling connections in peer-to-peer applications
 
Rich collaborative data structures for everyone
Rich collaborative data structures for everyoneRich collaborative data structures for everyone
Rich collaborative data structures for everyone
 
Postgres indexes
Postgres indexesPostgres indexes
Postgres indexes
 
Collaborative eventsourcing
Collaborative eventsourcingCollaborative eventsourcing
Collaborative eventsourcing
 
Living in eventually consistent reality
Living in eventually consistent realityLiving in eventually consistent reality
Living in eventually consistent reality
 
Virtual machines - how they work
Virtual machines - how they workVirtual machines - how they work
Virtual machines - how they work
 
Short story of time
Short story of timeShort story of time
Short story of time
 
Collaborative text editing
Collaborative text editingCollaborative text editing
Collaborative text editing
 
The last mile from db to disk
The last mile from db to diskThe last mile from db to disk
The last mile from db to disk
 
GraphQL - an elegant weapon... for more civilized age
GraphQL - an elegant weapon... for more civilized ageGraphQL - an elegant weapon... for more civilized age
GraphQL - an elegant weapon... for more civilized age
 

Dernier

VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
dharasingh5698
 
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Dr.Costas Sachpazis
 
result management system report for college project
result management system report for college projectresult management system report for college project
result management system report for college project
Tonystark477637
 

Dernier (20)

VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
 
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance BookingCall Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
 
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordCCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
 
Double rodded leveling 1 pdf activity 01
Double rodded leveling 1 pdf activity 01Double rodded leveling 1 pdf activity 01
Double rodded leveling 1 pdf activity 01
 
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
 
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
 
PVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELL
PVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELLPVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELL
PVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELL
 
Coefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptxCoefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptx
 
Booking open Available Pune Call Girls Koregaon Park 6297143586 Call Hot Ind...
Booking open Available Pune Call Girls Koregaon Park  6297143586 Call Hot Ind...Booking open Available Pune Call Girls Koregaon Park  6297143586 Call Hot Ind...
Booking open Available Pune Call Girls Koregaon Park 6297143586 Call Hot Ind...
 
data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdf
 
(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7
(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7
(INDIRA) Call Girl Meerut Call Now 8617697112 Meerut Escorts 24x7
 
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
 
result management system report for college project
result management system report for college projectresult management system report for college project
result management system report for college project
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSIS
 
Thermal Engineering -unit - III & IV.ppt
Thermal Engineering -unit - III & IV.pptThermal Engineering -unit - III & IV.ppt
Thermal Engineering -unit - III & IV.ppt
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
 
KubeKraft presentation @CloudNativeHooghly
KubeKraft presentation @CloudNativeHooghlyKubeKraft presentation @CloudNativeHooghly
KubeKraft presentation @CloudNativeHooghly
 
Thermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - VThermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - V
 
Generative AI or GenAI technology based PPT
Generative AI or GenAI technology based PPTGenerative AI or GenAI technology based PPT
Generative AI or GenAI technology based PPT
 
UNIT-IFLUID PROPERTIES & FLOW CHARACTERISTICS
UNIT-IFLUID PROPERTIES & FLOW CHARACTERISTICSUNIT-IFLUID PROPERTIES & FLOW CHARACTERISTICS
UNIT-IFLUID PROPERTIES & FLOW CHARACTERISTICS
 

Behind modern concurrency primitives

  • 2. INTRODUCTION Bartosz Sypytkowski ▪ @Horusiath ▪ b.sypytkowski@gmail.com ▪ bartoszsypytkowski.com
  • 3.  Concurrency in user vs. kernel space  Thread pools  Schedulers  Coroutines  State machines AGENDA
  • 4. WHY DO WE USE THIS?
  • 5. ASYNC/AWAIT Task.Run(async () => { await using var conn = new SqlConnection(ConnectionString); await conn.OpenAsync(); var user = await conn.QueryFirstAsync<User>( "SELECT * FROM Users WHERE Id = @id", new { id = 100 }); Console.WriteLine($"Received user {user.FirstName} {user.LastName}"); });
  • 7. THREAD API new Thread(() => { var conn = new SqlConnection(ConnectionString); conn.Open(); var user = conn.QueryFirst<User>( "SELECT * FROM Users WHERE Id = @id", new { id = 100 }); Console.WriteLine($"Received user {user.FirstName} {user.LastName}"); }).Start();
  • 8. Thread Pool* Managed Thread Managed Thread OS Thread THREADS TASKS User space Kernel space User space Kernel space Managed Thread OS Thread OS Thread Scheduler OS Thread OS Thread OS Thread Task Task Task Task Task Task Task Task Task Task Task Task Task Task Task Task Task Task
  • 10. THREAD POOL BASICS CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Job Queue
  • 11. PINNING THREAD TO CPU var threads = Process.GetCurrentProcess().Threads; var cpuCount = Environment.ProcessorCount; Console.WriteLine($"Using {threads.Count} threads on {cpuCount} cores."); // => Using 10 threads on 4 cores. for (int i = 0; i < threads.Count; i++) { var pthread = threads[i]; var cpuMask = (1L << (i % cpuCount)); pthread.IdealProcessor = i % cpuCount; // set preferred thread(s) pthread.ProcessorAffinity = (IntPtr)cpuMask; // set thread affinity mask } NOTE: it’s not always possible (iOS) or respected.
  • 12. SHARING DATA IN MULTI-CPU ARCHITECTURE
  • 13. HARDWARE ARCHITECTURE 101 CPU 1 L1 cache L1 cache L3 cache CPU 2 L1 cache L1 cache RAM CPU L1 cache: 1ns CPU L2 cache: 4-7ns CPU L2 cache: 100ns
  • 14. HARDWARE ARCHITECTURE NUMA CPU 1 CPU 2 L1-2 cache CPU 3 CPU 4 L1-2 cache CPU 5 CPU 6 L1-2 cache CPU 7 CPU 8 L1-2 cache
  • 16. THREAD POOL WORK STEALING CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Job Job Job Job Job Job Job Job Job Job
  • 17. THREAD POOL WORK STEALING CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Job Job Job Job Job Job ? Job Job Job Empty Queue
  • 18. THREAD POOL WORK STEALING CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Job Job Job Job Job Job ? Job Job Job
  • 19. THREAD POOL WORK STEALING CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Job Job Job Job Job Job Job Job Job
  • 20. COMPAR ING VECTOR CLOCKS AFFINITY BASED THREAD POOL CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread S1 S1 S1 S1 S2 S2 S3 S2 S3 S3 S4 S5 S4 S4 S5 Always map entity to the same thread / core
  • 21. AFFINITY THREAD POOL: PERFORMANCE Source: https://scalac.io/improving-akka-dispatchers/
  • 25. WHY IS THIS CODE BAD? static async Task ProcessFiles(FileInfo[] files) { var tasks = new Task[files.Length]; for (int i = 0; i < files.Length; i++) tasks[i] = ProcessFile(files[i]); await Task.WhenAll(tasks); } static async Task ProcessFile(FileInfo file) { using var stream = file.OpenRead(); using var reader = new StreamReader(stream); var text = reader.ReadToEnd(); Process(text); }
  • 26. WHY IS THIS CODE BAD? static async Task ProcessFiles(FileInfo[] files) { var tasks = new Task[files.Length]; for (int i = 0; i < files.Length; i++) tasks[i] = ProcessFile(files[i]); await Task.WhenAll(tasks); } static async Task ProcessFile(FileInfo file) { using var stream = file.OpenRead(); using var reader = new StreamReader(stream); var text = reader.ReadToEnd(); Process(text); } Blocking the thread belonging to a thread pool shared with other tasks.
  • 27. I/O 1. Just use async I/O API… WHAT CAN WE DO ABOUT IT?
  • 28. I/O 1. Just use async I/O API… 2. … but what when it’s not possible? WHAT CAN WE DO ABOUT IT?
  • 29. I/O 1. Just use async I/O API… 2. … but what when it’s not possible? WHAT CAN WE DO ABOUT IT? internal static class LmdbMethods { [DllImport("lmdb", CallingConvention = CallingConvention.Cdecl)] public static extern int mdb_dbi_open(IntPtr txn, string name, DatabaseOpenFlags flags, out uint db); [DllImport("lmdb", CallingConvention = CallingConvention.Cdecl)] public static extern int mdb_cursor_get(IntPtr cursor, ref ValueStructure key, ref ValueStructure data, CursorOperation op); [DllImport("lmdb", CallingConvention = CallingConvention.Cdecl)] public static extern int mdb_cursor_put(IntPtr cursor, ref ValueStructure key, ref ValueStructure value, CursorPutOptions flags); // other methods }
  • 30. THREAD POOL I/O THREADS CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Managed Thread I/O Threads Managed Thread Managed Thread
  • 31. THREAD POOL I/O THREADS CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Managed Thread I/O Threads Managed Thread Managed Thread J1
  • 32. THREAD POOL I/O THREADS CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Managed Thread I/O Threads Managed Thread Managed Thread J1
  • 33. THREAD POOL I/O THREADS CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Managed Thread I/O Threads Managed Thread Managed Thread J1
  • 34. THREAD POOL I/O THREADS CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Managed Thread I/O Threads Managed Thread Managed Thread J2
  • 35. GOROUTINES & I/O DEALING WITH KERNEL CALLS
  • 36. COMPAR ING VECTOR CLOCKS GOROUTINES I/O CALLS CPU 1 Agent Runtime Thread CPU 2 Agent Runtime Thread CPU 3 Agent Runtime Thread CPU 4 Agent Runtime Thread
  • 37. COMPAR ING VECTOR CLOCKS GOROUTINES I/O CALLS CPU 1 Agent Runtime Thread CPU 2 Agent Runtime Thread CPU 3 Agent Runtime Thread CPU 4 Agent Runtime Thread sys call
  • 38. COMPAR ING VECTOR CLOCKS GOROUTINES I/O CALLS CPU 1 Agent CPU 2 Agent Runtime Thread CPU 3 Agent Runtime Thread CPU 4 Agent Runtime Thread Runtime Thread sys call
  • 39. COMPAR ING VECTOR CLOCKS GOROUTINES I/O CALLS CPU 1 Agent CPU 2 Agent Runtime Thread CPU 3 Agent Runtime Thread CPU 4 Agent Runtime Thread Runtime Thread sys call Runtime Thread
  • 41. Scheduler depends on coroutines to return control to scheduler. PREEMPTIVE Scheduler is in charge of execution. Coroutines can be preempted. COOPERATIVE
  • 42. SCENARIO #2 CREATE AN EXCEL FILE
  • 43. COMPAR ING VECTOR CLOCKS BUILDING AN EXCEL FILE app.get('/valuations.xlsx', async (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); var valuations = await getValuations(req.params.id); for (let valuation in valuations) { worksheet.addRow(valuation); } await workbook.xlsx.write(res); });
  • 44. COMPAR ING VECTOR CLOCKS BUILDING AN EXCEL FILE app.get('/valuations.xlsx', async (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); var valuations = await getValuations(req.params.id); for (let valuation in valuations) { worksheet.addRow(valuation); // 500μs } await workbook.xlsx.write(res); });
  • 45. COMPAR ING VECTOR CLOCKS BUILDING AN EXCEL FILE app.get('/valuations.xlsx', async (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); var valuations = await getValuations(req.params.id); for (let valuation in valuations) { // 20,000 items worksheet.addRow(valuation); // 500μs } await workbook.xlsx.write(res); });
  • 46. COMPAR ING VECTOR CLOCKS BUILDING AN EXCEL FILE app.get('/valuations.xlsx', async (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); var valuations = await getValuations(req.params.id); for (let valuation in valuations) { // 20,000 items worksheet.addRow(valuation); // 500μs } await workbook.xlsx.write(res); }); Total loop execution time: 10 seconds
  • 47. HOW TO DEAL WITH LONG RUNNING TASKS?
  • 48. LONG RUNNING TASK SEPARATE THREAD CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Scheduler
  • 49. LONG RUNNING TASK SEPARATE THREAD CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Scheduler Task Task.Factory.StartNew(DownloadExcel, TaskCreationOptions.LongRunning)
  • 50. LONG RUNNING TASK SEPARATE THREAD CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Scheduler Task Task.Factory.StartNew(DownloadExcel, TaskCreationOptions.LongRunning) Managed Thread
  • 51. LONG RUNNING TASK SEPARATE THREAD CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Scheduler Managed Thread Task
  • 52. LONG RUNNING TASK SEPARATE THREAD CPU 1 Agent Managed Thread CPU 2 Agent Managed Thread CPU 3 Agent Managed Thread CPU 4 Agent Managed Thread Scheduler Managed Thread Task
  • 53. COMPAR ING VECTOR CLOCKS INTRODUCING INTERRUPTION POINTS function park() { return new Promise((resolve) => setTimeout(resolve, 0)); } app.get('/valuations.xlsx', async (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); var valuations = await getValuations(req.params.id); var i = 0; for (let valuation in valuations) { // 20,000 items if ((i++) % 100 === 0) { await park(); } worksheet.addRow(valuation); // 500μs } await workbook.xlsx.write(res); });
  • 55. STEP-BASED PREEMPTION % hello world program -module(helloworld). -export([start/0, write/1]). write([]) -> ok; write([File|T]) -> io:fwrite("Writing a file ~p~n", File), write(T). start() -> Files = ["A", "B", "C"], write(Files).
  • 56. STEP-BASED PREEMPTION Reduction counter under the hood % hello world program -module(helloworld). -export([start/0, write/1]). write([], Reductions) -> ok; write([File|T], Reductions) -> % if Reductions == 0 then yield() io:fwrite("Writing a file ~p~n", File), write(T, Reductions-1). start() -> Files = ["A", "B", "C"], write(Files, 2000).
  • 57. • JavaScript promises • .NET Task Parallel Library • Java/Scala Futures PREEMPTIVE • OS threads • Go goroutines • Erlang processes COOPERATIVE
  • 59. let run () = async { let sw = Stopwatch() sw.Start() let t1 = Async.Sleep(5000) let t2 = Async.Sleep(5000) do! t1 do! t2 printfn "Time passed: %ims" sw.ElapsedMilliseconds } static async Task Run() { var sw = new Stopwatch(); sw.Start(); var t1 = Task.Delay(TimeSpan.FromSeconds(5)); var t2 = Task.Delay(TimeSpan.FromSeconds(5)); await t1; await t2; Console.WriteLine( $"Time passed: {sw.ElapsedMilliseconds}ms"); }
  • 60. let run () = async { let sw = Stopwatch() sw.Start() let t1 = Async.Sleep(5000) let t2 = Async.Sleep(5000) do! t1 do! t2 printfn "Time passed: %ims" sw.ElapsedMilliseconds } static async Task Run() { var sw = new Stopwatch(); sw.Start(); var t1 = Task.Delay(TimeSpan.FromSeconds(5)); var t2 = Task.Delay(TimeSpan.FromSeconds(5)); await t1; await t2; Console.WriteLine( $"Time passed: {sw.ElapsedMilliseconds}ms"); } 5 seconds 10 seconds
  • 61. let run () = async { let sw = Stopwatch() sw.Start() let t1 = Async.Sleep(5000) let t2 = Async.Sleep(5000) do! t1 do! t2 printfn "Time passed: %ims" sw.ElapsedMilliseconds } static async Task Run() { var sw = new Stopwatch(); sw.Start(); var t1 = Task.Delay(TimeSpan.FromSeconds(5)); var t2 = Task.Delay(TimeSpan.FromSeconds(5)); await t1; await t2; Console.WriteLine( $"Time passed: {sw.ElapsedMilliseconds}ms"); } EAGER LAZY 5 seconds 10 seconds
  • 63. COMPAR ING VECTOR CLOCKS STACKLESS COROUTINES OLD CALLBACK API app.get('/valuations.xlsx', function (req, res, next) { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); getValuations(req.params.id, function (err, valuations) { for (let valuation in valuations) { worksheet.addRow(valuation); } workbook.xlsx.write(res, function (err) { next(); }); }); });
  • 64. COMPAR ING VECTOR CLOCKS STACKLESS COROUTINES app.get('/valuations.xlsx', (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); return getValuations(req.params.id) .then((valuations) => { for (let valuation in valuations) { worksheet.addRow(valuation); } return workbook.xlsx.write(res); }); }); OLD PROMISE API
  • 65. COMPAR ING VECTOR CLOCKS STACKLESS COROUTINES app.get('/valuations.xlsx', async (req, res) => { var workbook = new Excel.Workbook(); var worksheet = workbook.addWorksheet("Valuations"); var valuations = await getValuations(req.params.id); for (let valuation in valuations) { worksheet.addRow(valuation); } await workbook.xlsx.write(res); });
  • 66. PROBLEM OF FUNCTION COLOURING
  • 67. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co)
  • 68. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame Start of coroutine stack
  • 69. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame print activation frame
  • 70. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame
  • 71. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame print activation frame
  • 72. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame Park coroutine stack
  • 73. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame Park coroutine stack
  • 74. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame
  • 75. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame print activation frame
  • 76. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame Resume coroutine stack
  • 77. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame
  • 78. STACKFUL COROUTINES STACK main activation frame function foo(a, b) print("foo", a) bar("foo", b) end function bar(caller, x) print(“bar: ", caller) coroutine.yield() print(“bar", x) end co = coroutine.create(foo) coroutine.resume(co, 1, 2) print(“main”) coroutine.resume(co) foo activation frame bar activation frame print activation frame
  • 79. WHAT IS THE CORRECT STACK SIZE FOR EACH COROUTINE?
  • 80. • JavaScript promises • .NET Task Parallel Library • Java/Scala Futures STACKFUL • Go goroutines • LUA coroutines • (soon) Java Loom project STACKLESS
  • 82. ASYNC/AWAIT UNITY COROUTINES IEnumerable WaitAndPrint() { print("Starting at " + Time.time); yield return new WaitForSeconds(0.1f); print("Ending at " + Time.time); } async Task WaitAndPrint() { Console.WriteLine("Starting at " + DateTime.Now); await Task.Delay(100); Console.WriteLine("Ending at " + DateTime.Now); }
  • 83. INTEGRATING TPL AND LINQ public static class TaskExtensions { public static Task<T3> SelectMany<T1, T2, T3>( this Task<T1> task, Func<T1, Task<T2>> binding, Func<T1, T2, T3> combine) { var tcs = new TaskCompletionSource<T3>(); task.ContinueWith(t1 => { if (t1.IsFaulted) tcs.SetException(t1.Exception); else if (t1.IsCanceled) tcs.SetCanceled(); else binding(t1.Result).ContinueWith(t2 => { if (t2.IsFaulted) tcs.SetException(t2.Exception); else if (t2.IsCanceled) tcs.SetCanceled(); else tcs.SetResult(combine(t1.Result, t2.Result)); }); }); return tcs.Task; } } public static Task Rename(int userId, string name) { return from user in GetUser(userId) from _ in SaveUser(user with { Name = name }) select 0; }
  • 85. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 86. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 87. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 88. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 89. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 90. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 91. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 92. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 93. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 94. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 95. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 96. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 97. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 98. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 99. WHAT YOU SEE WHAT YOU DON’T SEE static class Program { static async Task Foo(int a, int b) { Console.WriteLine("Foo", a); await Task.Yield(); Console.WriteLine("Foo", b); } } struct Foo__0 : IStateMachine { AsyncTaskMethodBuilder builder; int state; int a; int b; public void MoveNext() { switch (this.state) { case -1: Console.WriteLine("Foo", this.a); var awaiter = Taks.Yield().GetAwaiter(); this.state = 0; this.builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); // ^ awaiter.OnComplete(() => // ThreadPool.QueueUserWorkItem(this.MoveNext)); return; case 0: Console.WriteLine("Foo", this.b); builder.SetResult(); return; } } } static class Program { static Task Foo(int a, int b) { var builder = AsyncTaskMethodBuilder.Create(); var stateMachine = new Foo__0(); // initialize state machine fields stateMachine.a = a; stateMachine.b = b; stateMachine.state = -1; stateMachine.builder = builder; builder.Start(ref stateMachine); // ^ similar to: ThreadPool.QueueUserWorkItem(this.MoveNext); return builder.Task; } }
  • 100. AWAITER PATTERN Custom awaitable objects public readonly struct PromiseAwaiter<T> : INotifyCompletion { private readonly Promise<T> promise; public PromiseAwaiter(Promise<T> promise) { this.promise = promise; } #region mandatory awaiter methods public bool IsCompleted => promise.IsCompleted; public T GetResult() => promise.Result; public void OnCompleted(Action continuation) => promise.RegisterContinuation(continuation); #endregion }
  • 101. ASYNC METHOD BUILDER Custom async methods public struct PromiseAsyncMethodBuilder<T> { private Promise<T>? promise; #region mandatory methods for async state machine builder public static PromiseAsyncMethodBuilder<T> Create() => default; public Promise<T> Task => promise ??= new Promise<T>(); public void SetException(Exception e) => Task.TrySetException(e); public void SetResult(T result) => Task.TrySetResult(result); public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { } public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { } public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { } public void SetStateMachine(IAsyncStateMachine stateMachine) { } #endregion }
  • 104.  Custom AsyncMethodBuilder “docs”: https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md  Building custom (affine) thread pool: https://bartoszsypytkowski.com/thread-safety-with-affine-thread-pools/  What color is your function?: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/  How Go scheduler works: https://www.youtube.com/watch?v=-K11rY57K7k  Thread per core architecture: https://www.datadoghq.com/blog/engineering/introducing-glommio/ REFERENCES