Alastair’s Place

Software development, Cocoa, Objective-C, life. Stuff like that.

C#-like Async in Swift

Justin Williams was wishing for C#-like async support in Swift. I think it’s possible to come up with a fairly straightforward implementation in Swift, without any changes to the compiler, and actually without any hacking either. (If it weren’t for compiler bugs, the code below would be more than just a toy implementation too…)

Anyway, here goes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import Dispatch

var async_q : dispatch_queue_t = dispatch_queue_create("Async queue",
  DISPATCH_QUEUE_CONCURRENT)

/* If generics worked, we'd use Task<T> here and result would be of type T? */
class Task {
  var result : Int?
  var sem : dispatch_semaphore_t = dispatch_semaphore_create(0)
    
  func await() -> Int {
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
    return result!
  }
}

func await(task: Task) -> Int {
  return task.await()
}

func async(b: () -> Int) -> Task {
  var r = Task()
  
  dispatch_async(async_q, {
    r.result = b()
    dispatch_semaphore_signal(r.sem)
    })
  
  return r
}

/* Now use it */
func Test2(var a : Int) -> Task { return async {
  sleep(1)
  return a * 7
  }
}

func Test(var a : Int) -> Task { return async {
  var t2 = Test2(a)
  var b = await(t2)
  
  return a + b
  }
}

var t = Test(100)

println("Waiting for result")

for n in 1..10 {
  println("I can do work here while the function works.")
}

var result = await(t)

println("Result is available")

Now, obviously if Swift supported continuations, this might be done more efficiently (i.e. without any background threads or semaphores), but that’s an implementation detail.

There are also some syntax changes that would make it cleaner, notably if it was permissible to remove the { return and } from the async function declarations. I did briefly try to see whether I was allowed to assign to a function, ala

func Test(var a : Int) -> Task = async { }

but that syntax isn’t allowed (if it was, async would obviously need to return a block).