├── .gitignore ├── GoSwift ├── GoSwift.h ├── Info.plist └── go.swift ├── GoSwift.podspec ├── run.sh ├── examples ├── channels.swift ├── panic.go ├── channel-buffering.swift ├── channel-synchronization.swift ├── panic.swift ├── channels.go ├── channel-buffering.go ├── channel-synchronization.go ├── defer.swift ├── select.swift ├── select.go ├── goroutines.go ├── goroutines.swift ├── defer.go ├── closing-channels.swift └── closing-channels.go ├── LICENSE ├── README.md └── GoSwift.xcodeproj ├── xcshareddata └── xcschemes │ └── GoSwift.xcscheme └── project.pbxproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | Pods 20 | Carthage/Build 21 | play.swift 22 | play.go 23 | 24 | -------------------------------------------------------------------------------- /GoSwift/GoSwift.h: -------------------------------------------------------------------------------- 1 | // 2 | // GoSwift.h 3 | // GoSwift 4 | // 5 | // Created by Josh Baker on 6/4/15. 6 | // Copyright (c) 2015 ONcast, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for GoSwift. 12 | FOUNDATION_EXPORT double GoSwiftVersionNumber; 13 | 14 | //! Project version string for GoSwift. 15 | FOUNDATION_EXPORT const unsigned char GoSwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /GoSwift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "GoSwift" 3 | s.version = "1.1.0" 4 | s.summary = "Go Goodies for Swift. Including goroutines, channels, defer, and panic." 5 | s.homepage = "https://github.com/tidwall/GoSwift" 6 | s.license = { :type => "Attribution License", :file => "LICENSE" } 7 | s.source = { :git => "https://github.com/tidwall/GoSwift.git", :tag => "v#{s.version}" } 8 | s.authors = { 'Josh Baker' => 'joshbaker77@gmail.com' } 9 | s.social_media_url = "https://twitter.com/tidwall" 10 | s.ios.platform = :ios, '8.0' 11 | s.ios.deployment_target = "8.0" 12 | s.osx.platform = :osx, '10.9' 13 | s.osx.deployment_target = "10.9" 14 | s.source_files = "GoSwift/go.swift" 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /GoSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | cd $(dirname "${BASH_SOURCE[0]}") 5 | 6 | if [[ "$@" == "" ]]; then 7 | echo "usage: $0 [file (.swift | .go)]" 8 | echo "" 9 | echo "examples:" 10 | echo " $0 examples/goroutines.swift" 11 | echo " $0 examples/goroutines.go" 12 | exit -1 13 | fi 14 | 15 | f="$@" 16 | ext=${f##*.} 17 | 18 | if [[ "$ext" == "swift" ]]; then 19 | cat GoSwift/go.swift > /tmp/go-main.swift 20 | echo "private var printmut = Mutex();func print(value: Any){let str=\"\\(value)\"; printmut.lock {try! str.writeToFile(\"/dev/stdout\", atomically:false, encoding:NSUTF8StringEncoding)}}" >> /tmp/go-main.swift 21 | echo "func println(value: Any){print(\"\\(value)\\n\")}" >> /tmp/go-main.swift 22 | cat "$f" >> /tmp/go-main.swift 23 | echo "" >> /tmp/go-main.swift 24 | echo "main()" >> /tmp/go-main.swift 25 | swift /tmp/go-main.swift 26 | elif [[ "$ext" == "go" ]]; then 27 | GOMAXPROCS=4 go run "$f" 28 | else 29 | echo "$f: invalid file type" 30 | exit -1 31 | fi 32 | -------------------------------------------------------------------------------- /examples/channels.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channels 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // _Channels_ are the pipes that connect concurrent 6 | // goroutines. You can send values into channels from one 7 | // goroutine and receive those values into another 8 | // goroutine. 9 | 10 | func main() { 11 | 12 | // Create a new channel with `make(chan val-type)`. 13 | // Channels are typed by the values they convey. 14 | let messages = Chan() 15 | 16 | // _Send_ a value into a channel using the `channel <-` 17 | // syntax. Here we send `"ping"` to the `messages` 18 | // channel we made above, from a new goroutine. 19 | go { messages <- "ping" } 20 | 21 | // The `<-channel` syntax _receives_ a value from the 22 | // channel. Here we'll receive the `"ping"` message 23 | // we sent above and print it out. 24 | let msg = <-messages 25 | println(msg!) 26 | } 27 | -------------------------------------------------------------------------------- /examples/panic.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // A `panic` typically means something went unexpectedly 6 | // wrong. Mostly we use it to fail fast on errors that 7 | // shouldn't occur during normal operation, or that we 8 | // aren't prepared to handle gracefully. 9 | 10 | package main 11 | 12 | import "os" 13 | 14 | func main() { 15 | 16 | // We'll use panic throughout this site to check for 17 | // unexpected errors. This is the only program on the 18 | // site designed to panic. 19 | panic("a problem") 20 | 21 | // A common use of panic is to abort if a function 22 | // returns an error value that we don't know how to 23 | // (or want to) handle. Here's an example of 24 | // `panic`king if we get an unexpected error when creating a new file. 25 | _, err := os.Create("/tmp/file") 26 | if err != nil { 27 | panic(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/channel-buffering.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // By default channels are _unbuffered_, meaning that they 6 | // will only accept sends (`chan <-`) if there is a 7 | // corresponding receive (`<- chan`) ready to receive the 8 | // sent value. _Buffered channels_ accept a limited 9 | // number of values without a corresponding receiver for 10 | // those values. 11 | 12 | func main() { 13 | 14 | // Here we `make` a channel of strings buffering up to 15 | // 2 values. 16 | let messages = Chan(2) 17 | 18 | // Because this channel is buffered, we can send these 19 | // values into the channel without a corresponding 20 | // concurrent receive. 21 | messages <- "buffered" 22 | messages <- "channel" 23 | 24 | // Later we can receive these two values as usual. 25 | println((<-messages)!) 26 | println((<-messages)!) 27 | } 28 | -------------------------------------------------------------------------------- /examples/channel-synchronization.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-synchronization 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // We can use channels to synchronize execution 6 | // across goroutines. Here's an example of using a 7 | // blocking receive to wait for a goroutine to finish. 8 | 9 | // This is the function we'll run in a goroutine. The 10 | // `done` channel will be used to notify another 11 | // goroutine that this function's work is done. 12 | func worker(done : Chan) { 13 | print("working...") 14 | NSThread.sleepForTimeInterval(1) 15 | println("done") 16 | 17 | // Send a value to notify that we're done. 18 | done <- true 19 | } 20 | 21 | func main() { 22 | 23 | // Start a worker goroutine, giving it the channel to 24 | // notify on. 25 | let done = Chan(1) 26 | go { worker(done) } 27 | 28 | // Block until we receive a notification from the 29 | // worker on the channel. 30 | <-done 31 | } 32 | -------------------------------------------------------------------------------- /examples/panic.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // A `panic` typically means something went unexpectedly 6 | // wrong. Mostly we use it to fail fast on errors that 7 | // shouldn't occur during normal operation, or that we 8 | // aren't prepared to handle gracefully. 9 | 10 | func main() { 11 | ${ // ${} is required for 'panic', 'recover' 12 | // We'll use panic throughout this site to check for 13 | // unexpected errors. This is the only program on the 14 | // site designed to panic. 15 | panic("a problem") 16 | 17 | // A common use of panic is to abort if a function 18 | // returns an error value that we don't know how to 19 | // (or want to) handle. Here's an example of 20 | // `panic`king if we get an unexpected error when creating a new file. 21 | let f = fopen("/tmp/file", "wb+") 22 | if f == nil { 23 | panic("file error") 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/channels.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channels 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // _Channels_ are the pipes that connect concurrent 6 | // goroutines. You can send values into channels from one 7 | // goroutine and receive those values into another 8 | // goroutine. 9 | 10 | package main 11 | 12 | import "fmt" 13 | 14 | func main() { 15 | 16 | // Create a new channel with `make(chan val-type)`. 17 | // Channels are typed by the values they convey. 18 | messages := make(chan string) 19 | 20 | // _Send_ a value into a channel using the `channel <-` 21 | // syntax. Here we send `"ping"` to the `messages` 22 | // channel we made above, from a new goroutine. 23 | go func() { messages <- "ping" }() 24 | 25 | // The `<-channel` syntax _receives_ a value from the 26 | // channel. Here we'll receive the `"ping"` message 27 | // we sent above and print it out. 28 | msg := <-messages 29 | fmt.Println(msg) 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /examples/channel-buffering.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // By default channels are _unbuffered_, meaning that they 6 | // will only accept sends (`chan <-`) if there is a 7 | // corresponding receive (`<- chan`) ready to receive the 8 | // sent value. _Buffered channels_ accept a limited 9 | // number of values without a corresponding receiver for 10 | // those values. 11 | 12 | package main 13 | 14 | import "fmt" 15 | 16 | func main() { 17 | 18 | // Here we `make` a channel of strings buffering up to 19 | // 2 values. 20 | messages := make(chan string, 2) 21 | 22 | // Because this channel is buffered, we can send these 23 | // values into the channel without a corresponding 24 | // concurrent receive. 25 | messages <- "buffered" 26 | messages <- "channel" 27 | 28 | // Later we can receive these two values as usual. 29 | fmt.Println(<-messages) 30 | fmt.Println(<-messages) 31 | } 32 | -------------------------------------------------------------------------------- /examples/channel-synchronization.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-synchronization 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // We can use channels to synchronize execution 6 | // across goroutines. Here's an example of using a 7 | // blocking receive to wait for a goroutine to finish. 8 | 9 | package main 10 | 11 | import "fmt" 12 | import "time" 13 | 14 | // This is the function we'll run in a goroutine. The 15 | // `done` channel will be used to notify another 16 | // goroutine that this function's work is done. 17 | func worker(done chan bool) { 18 | fmt.Print("working...") 19 | time.Sleep(time.Second) 20 | fmt.Println("done") 21 | 22 | // Send a value to notify that we're done. 23 | done <- true 24 | } 25 | 26 | func main() { 27 | 28 | // Start a worker goroutine, giving it the channel to 29 | // notify on. 30 | done := make(chan bool, 1) 31 | go worker(done) 32 | 33 | // Block until we receive a notification from the 34 | // worker on the channel. 35 | <-done 36 | } 37 | -------------------------------------------------------------------------------- /examples/defer.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // _Defer_ is used to ensure that a function call is 6 | // performed later in a program's execution, usually for 7 | // purposes of cleanup. `defer` is often used where e.g. 8 | // `ensure` and `finally` would be used in other languages. 9 | 10 | // Suppose we wanted to create a file, write to it, 11 | // and then close when we're done. Here's how we could 12 | // do that with `defer`. 13 | func main() { 14 | let f = createFile("/tmp/defer.txt") 15 | defer { closeFile(f) } 16 | writeFile(f) 17 | } 18 | 19 | func createFile(p : String) -> UnsafeMutablePointer { 20 | println("creating") 21 | let f = fopen(p, "wb+") 22 | if f == nil { 23 | panic("file error") 24 | } 25 | return f 26 | } 27 | 28 | func writeFile(f : UnsafeMutablePointer) { 29 | println("writing") 30 | // fprintf(f, "data") // fprintf not available in swift 31 | } 32 | 33 | func closeFile(f : UnsafeMutablePointer) { 34 | println("closing") 35 | fclose(f) 36 | } 37 | -------------------------------------------------------------------------------- /examples/select.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // Go's _select_ lets you wait on multiple channel 6 | // operations. Combining goroutines and channels with 7 | // select is a powerful feature of Go. 8 | 9 | func main() { 10 | 11 | // For our example we'll select across two channels. 12 | let c1 = Chan() 13 | let c2 = Chan() 14 | 15 | // Each channel will receive a value after some amount 16 | // of time, to simulate e.g. blocking RPC operations 17 | // executing in concurrent goroutines. 18 | go { 19 | NSThread.sleepForTimeInterval(1) 20 | c1 <- "one" 21 | } 22 | go { 23 | NSThread.sleepForTimeInterval(2) 24 | c2 <- "two" 25 | } 26 | 27 | // We'll use `select` to await both of these values 28 | // simultaneously, printing each one as it arrives. 29 | for var i = 0; i < 2; i++ { 30 | select { 31 | _case(c1) { (msg1, ok) in 32 | println("received \(msg1!)") 33 | } 34 | _case(c2) { (msg2, ok) in 35 | println("received \(msg2!)") 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/select.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // Go's _select_ lets you wait on multiple channel 6 | // operations. Combining goroutines and channels with 7 | // select is a powerful feature of Go. 8 | 9 | package main 10 | 11 | import "time" 12 | import "fmt" 13 | 14 | func main() { 15 | 16 | // For our example we'll select across two channels. 17 | c1 := make(chan string) 18 | c2 := make(chan string) 19 | 20 | // Each channel will receive a value after some amount 21 | // of time, to simulate e.g. blocking RPC operations 22 | // executing in concurrent goroutines. 23 | go func() { 24 | time.Sleep(time.Second * 1) 25 | c1 <- "one" 26 | }() 27 | go func() { 28 | time.Sleep(time.Second * 2) 29 | c2 <- "two" 30 | }() 31 | 32 | // We'll use `select` to await both of these values 33 | // simultaneously, printing each one as it arrives. 34 | for i := 0; i < 2; i++ { 35 | select { 36 | case msg1 := <-c1: 37 | fmt.Println("received", msg1) 38 | case msg2 := <-c2: 39 | fmt.Println("received", msg2) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/goroutines.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/goroutines 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // A _goroutine_ is a lightweight thread of execution. 6 | 7 | package main 8 | 9 | import "fmt" 10 | 11 | func f(from string) { 12 | for i := 0; i < 3; i++ { 13 | fmt.Println(from, ":", i) 14 | } 15 | } 16 | 17 | func main() { 18 | 19 | // Suppose we have a function call `f(s)`. Here's how 20 | // we'd call that in the usual way, running it 21 | // synchronously. 22 | f("direct") 23 | 24 | // To invoke this function in a goroutine, use 25 | // `go f(s)`. This new goroutine will execute 26 | // concurrently with the calling one. 27 | go f("goroutine") 28 | 29 | // You can also start a goroutine for an anonymous 30 | // function call. 31 | go func(msg string) { 32 | fmt.Println(msg) 33 | }("going") 34 | 35 | // Our two function calls are running asynchronously in 36 | // separate goroutines now, so execution falls through 37 | // to here. This `Scanln` code requires we press a key 38 | // before the program exits. 39 | var input string 40 | fmt.Scanln(&input) 41 | fmt.Println("done") 42 | } 43 | -------------------------------------------------------------------------------- /examples/goroutines.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/goroutines 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // A _goroutine_ is a lightweight thread of execution. 6 | 7 | func f(from : String) { 8 | for var i = 0; i < 3; i++ { 9 | println("\(from) : \(i)") 10 | } 11 | } 12 | 13 | func main() { 14 | 15 | // Suppose we have a function call `f(s)`. Here's how 16 | // we'd call that in the usual way, running it 17 | // synchronously. 18 | f("direct") 19 | 20 | // To invoke this function in a goroutine, use 21 | // `go f(s)`. This new goroutine will execute 22 | // concurrently with the calling one. 23 | go { f("goroutine") } 24 | 25 | // You can also start a goroutine for an anonymous 26 | // function call. 27 | go { { (msg : String) in 28 | println(msg) 29 | }("going") } 30 | 31 | // Our two function calls are running asynchronously in 32 | // separate goroutines now, so execution falls through 33 | // to here. This `Scanln` code requires we press a key 34 | // before the program exits. 35 | NSFileHandle.fileHandleWithStandardInput().availableData 36 | println("done") 37 | } 38 | -------------------------------------------------------------------------------- /examples/defer.go: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // _Defer_ is used to ensure that a function call is 6 | // performed later in a program's execution, usually for 7 | // purposes of cleanup. `defer` is often used where e.g. 8 | // `ensure` and `finally` would be used in other languages. 9 | 10 | package main 11 | 12 | import "fmt" 13 | import "os" 14 | 15 | // Suppose we wanted to create a file, write to it, 16 | // and then close when we're done. Here's how we could 17 | // do that with `defer`. 18 | func main() { 19 | 20 | // Immediately after getting a file object with 21 | // `createFile`, we defer the closing of that file 22 | // with `closeFile`. This will be executed at the end 23 | // of the enclosing function (`main`), after 24 | // `writeFile` has finished. 25 | f := createFile("/tmp/defer.txt") 26 | defer closeFile(f) 27 | writeFile(f) 28 | } 29 | 30 | func createFile(p string) *os.File { 31 | fmt.Println("creating") 32 | f, err := os.Create(p) 33 | if err != nil { 34 | panic(err) 35 | } 36 | return f 37 | } 38 | 39 | func writeFile(f *os.File) { 40 | fmt.Println("writing") 41 | fmt.Fprintln(f, "data") 42 | 43 | } 44 | 45 | func closeFile(f *os.File) { 46 | fmt.Println("closing") 47 | f.Close() 48 | } 49 | -------------------------------------------------------------------------------- /examples/closing-channels.swift: -------------------------------------------------------------------------------- 1 | // https://gobyexample.com/channel-buffering 2 | // The original work is copyright Mark McGranaghan and licensed under a Creative Commons Attribution 3.0 Unported License. 3 | // The swift port is by Josh Baker 4 | 5 | // _Closing_ a channel indicates that no more values 6 | // will be sent on it. This can be useful to communicate 7 | // completion to the channel's receivers. 8 | 9 | // In this example we'll use a `jobs` channel to 10 | // communicate work to be done from the `main()` goroutine 11 | // to a worker goroutine. When we have no more jobs for 12 | // the worker we'll `close` the `jobs` channel. 13 | func main() { 14 | let jobs = Chan(5) 15 | let done = Chan() 16 | 17 | // Here's the worker goroutine. It repeatedly receives 18 | // from `jobs` with `j, more := <-jobs`. In this 19 | // special 2-value form of receive, the `more` value 20 | // will be `false` if `jobs` has been `close`d and all 21 | // values in the channel have already been received. 22 | // We use this to notify on `done` when we've worked 23 | // all our jobs. 24 | go { 25 | for ;; { 26 | let (j, more) =  GoSwift - Go Goodies for Swift 2 | 3 | Bring some of the more powerful features of Go to your iOS / Swift project such as channels, goroutines, and defers. 4 | 5 | **This is an experimental project.** For production use of channels and sync APIs, check out the [Safe](https://github.com/tidwall/Safe) project. 6 | 7 | **Built for Swift 2.0** - For Swift 1.2 support use v0.1.4 or earlier. 8 | 9 | 10 | ## Features 11 | 12 | - Goroutines 13 | - Defer 14 | - Panic, Recover 15 | - Channels 16 | - Buffered Channels 17 | - Select, Case, Default 18 | - Closing 19 | - Sync Package 20 | - Mutex, Cond, Once, WaitGroup 21 | 22 | ## Example 23 | 24 | *Note that the following example and all of the examples in the `examples` directory originated from http://gobyexample.com and Mark McGranaghan* 25 | 26 | **Go** 27 | 28 | ```go 29 | package main 30 | 31 | import "fmt" 32 | 33 | func main() { 34 | jobs := make(chan int, 5) 35 | done := make(chan bool) 36 | 37 | go func() { 38 | for { 39 | j, more := <-jobs 40 | if more { 41 | fmt.Println("received job", j) 42 | } else { 43 | fmt.Println("received all jobs") 44 | done <- true 45 | return 46 | } 47 | } 48 | }() 49 | 50 | for j := 1; j <= 3; j++ { 51 | jobs <- j 52 | fmt.Println("sent job", j) 53 | } 54 | close(jobs) 55 | fmt.Println("sent all jobs") 56 | 57 | <-done 58 | } 59 | ``` 60 | 61 | **Swift** 62 | 63 | ```swift 64 | func main() { 65 | var jobs = Chan(5) 66 | var done = Chan() 67 | 68 | go { 69 | for ;; { 70 | var (j, more) = 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /GoSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0311F57E1B20A06F003616AD /* GoSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 0311F57D1B20A06F003616AD /* GoSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 0311F5951B20A138003616AD /* go.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0311F5941B20A138003616AD /* go.swift */; }; 12 | 0311F5971B20A213003616AD /* run.sh in Resources */ = {isa = PBXBuildFile; fileRef = 0311F5961B20A213003616AD /* run.sh */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | 0311F5781B20A06F003616AD /* GoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 17 | 0311F57C1B20A06F003616AD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18 | 0311F57D1B20A06F003616AD /* GoSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoSwift.h; sourceTree = ""; }; 19 | 0311F5941B20A138003616AD /* go.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = go.swift; sourceTree = ""; }; 20 | 0311F5961B20A213003616AD /* run.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = run.sh; sourceTree = ""; }; 21 | /* End PBXFileReference section */ 22 | 23 | /* Begin PBXFrameworksBuildPhase section */ 24 | 0311F5741B20A06F003616AD /* Frameworks */ = { 25 | isa = PBXFrameworksBuildPhase; 26 | buildActionMask = 2147483647; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXFrameworksBuildPhase section */ 32 | 33 | /* Begin PBXGroup section */ 34 | 0311F56E1B20A06F003616AD = { 35 | isa = PBXGroup; 36 | children = ( 37 | 0311F5961B20A213003616AD /* run.sh */, 38 | 0311F57A1B20A06F003616AD /* GoSwift */, 39 | 0311F5791B20A06F003616AD /* Products */, 40 | ); 41 | sourceTree = ""; 42 | }; 43 | 0311F5791B20A06F003616AD /* Products */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | 0311F5781B20A06F003616AD /* GoSwift.framework */, 47 | ); 48 | name = Products; 49 | sourceTree = ""; 50 | }; 51 | 0311F57A1B20A06F003616AD /* GoSwift */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 0311F5941B20A138003616AD /* go.swift */, 55 | 0311F57D1B20A06F003616AD /* GoSwift.h */, 56 | 0311F57B1B20A06F003616AD /* Supporting Files */, 57 | ); 58 | path = GoSwift; 59 | sourceTree = ""; 60 | }; 61 | 0311F57B1B20A06F003616AD /* Supporting Files */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 0311F57C1B20A06F003616AD /* Info.plist */, 65 | ); 66 | name = "Supporting Files"; 67 | sourceTree = ""; 68 | }; 69 | /* End PBXGroup section */ 70 | 71 | /* Begin PBXHeadersBuildPhase section */ 72 | 0311F5751B20A06F003616AD /* Headers */ = { 73 | isa = PBXHeadersBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | 0311F57E1B20A06F003616AD /* GoSwift.h in Headers */, 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXHeadersBuildPhase section */ 81 | 82 | /* Begin PBXNativeTarget section */ 83 | 0311F5771B20A06F003616AD /* GoSwift */ = { 84 | isa = PBXNativeTarget; 85 | buildConfigurationList = 0311F58E1B20A06F003616AD /* Build configuration list for PBXNativeTarget "GoSwift" */; 86 | buildPhases = ( 87 | 0311F5731B20A06F003616AD /* Sources */, 88 | 0311F5741B20A06F003616AD /* Frameworks */, 89 | 0311F5751B20A06F003616AD /* Headers */, 90 | 0311F5761B20A06F003616AD /* Resources */, 91 | ); 92 | buildRules = ( 93 | ); 94 | dependencies = ( 95 | ); 96 | name = GoSwift; 97 | productName = GoSwift; 98 | productReference = 0311F5781B20A06F003616AD /* GoSwift.framework */; 99 | productType = "com.apple.product-type.framework"; 100 | }; 101 | /* End PBXNativeTarget section */ 102 | 103 | /* Begin PBXProject section */ 104 | 0311F56F1B20A06F003616AD /* Project object */ = { 105 | isa = PBXProject; 106 | attributes = { 107 | LastSwiftUpdateCheck = 0700; 108 | LastUpgradeCheck = 0700; 109 | ORGANIZATIONNAME = "ONcast, LLC"; 110 | TargetAttributes = { 111 | 0311F5771B20A06F003616AD = { 112 | CreatedOnToolsVersion = 6.3.2; 113 | }; 114 | }; 115 | }; 116 | buildConfigurationList = 0311F5721B20A06F003616AD /* Build configuration list for PBXProject "GoSwift" */; 117 | compatibilityVersion = "Xcode 3.2"; 118 | developmentRegion = English; 119 | hasScannedForEncodings = 0; 120 | knownRegions = ( 121 | en, 122 | ); 123 | mainGroup = 0311F56E1B20A06F003616AD; 124 | productRefGroup = 0311F5791B20A06F003616AD /* Products */; 125 | projectDirPath = ""; 126 | projectRoot = ""; 127 | targets = ( 128 | 0311F5771B20A06F003616AD /* GoSwift */, 129 | ); 130 | }; 131 | /* End PBXProject section */ 132 | 133 | /* Begin PBXResourcesBuildPhase section */ 134 | 0311F5761B20A06F003616AD /* Resources */ = { 135 | isa = PBXResourcesBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | 0311F5971B20A213003616AD /* run.sh in Resources */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXResourcesBuildPhase section */ 143 | 144 | /* Begin PBXSourcesBuildPhase section */ 145 | 0311F5731B20A06F003616AD /* Sources */ = { 146 | isa = PBXSourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | 0311F5951B20A138003616AD /* go.swift in Sources */, 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | /* End PBXSourcesBuildPhase section */ 154 | 155 | /* Begin XCBuildConfiguration section */ 156 | 0311F58C1B20A06F003616AD /* Debug */ = { 157 | isa = XCBuildConfiguration; 158 | buildSettings = { 159 | ALWAYS_SEARCH_USER_PATHS = NO; 160 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 161 | CLANG_CXX_LIBRARY = "libc++"; 162 | CLANG_ENABLE_MODULES = YES; 163 | CLANG_ENABLE_OBJC_ARC = YES; 164 | CLANG_WARN_BOOL_CONVERSION = YES; 165 | CLANG_WARN_CONSTANT_CONVERSION = YES; 166 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 167 | CLANG_WARN_EMPTY_BODY = YES; 168 | CLANG_WARN_ENUM_CONVERSION = YES; 169 | CLANG_WARN_INT_CONVERSION = YES; 170 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 171 | CLANG_WARN_UNREACHABLE_CODE = YES; 172 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 173 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 174 | COPY_PHASE_STRIP = NO; 175 | CURRENT_PROJECT_VERSION = 1; 176 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 177 | ENABLE_STRICT_OBJC_MSGSEND = YES; 178 | ENABLE_TESTABILITY = YES; 179 | GCC_C_LANGUAGE_STANDARD = gnu99; 180 | GCC_DYNAMIC_NO_PIC = NO; 181 | GCC_NO_COMMON_BLOCKS = YES; 182 | GCC_OPTIMIZATION_LEVEL = 0; 183 | GCC_PREPROCESSOR_DEFINITIONS = ( 184 | "DEBUG=1", 185 | "$(inherited)", 186 | ); 187 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 188 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 189 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 190 | GCC_WARN_UNDECLARED_SELECTOR = YES; 191 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 192 | GCC_WARN_UNUSED_FUNCTION = YES; 193 | GCC_WARN_UNUSED_VARIABLE = YES; 194 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 195 | MTL_ENABLE_DEBUG_INFO = YES; 196 | ONLY_ACTIVE_ARCH = YES; 197 | SDKROOT = iphoneos; 198 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 199 | TARGETED_DEVICE_FAMILY = "1,2"; 200 | VERSIONING_SYSTEM = "apple-generic"; 201 | VERSION_INFO_PREFIX = ""; 202 | }; 203 | name = Debug; 204 | }; 205 | 0311F58D1B20A06F003616AD /* Release */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | ALWAYS_SEARCH_USER_PATHS = NO; 209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 210 | CLANG_CXX_LIBRARY = "libc++"; 211 | CLANG_ENABLE_MODULES = YES; 212 | CLANG_ENABLE_OBJC_ARC = YES; 213 | CLANG_WARN_BOOL_CONVERSION = YES; 214 | CLANG_WARN_CONSTANT_CONVERSION = YES; 215 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 220 | CLANG_WARN_UNREACHABLE_CODE = YES; 221 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 222 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 223 | COPY_PHASE_STRIP = NO; 224 | CURRENT_PROJECT_VERSION = 1; 225 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 226 | ENABLE_NS_ASSERTIONS = NO; 227 | ENABLE_STRICT_OBJC_MSGSEND = YES; 228 | GCC_C_LANGUAGE_STANDARD = gnu99; 229 | GCC_NO_COMMON_BLOCKS = YES; 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 234 | GCC_WARN_UNUSED_FUNCTION = YES; 235 | GCC_WARN_UNUSED_VARIABLE = YES; 236 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 237 | MTL_ENABLE_DEBUG_INFO = NO; 238 | SDKROOT = iphoneos; 239 | TARGETED_DEVICE_FAMILY = "1,2"; 240 | VALIDATE_PRODUCT = YES; 241 | VERSIONING_SYSTEM = "apple-generic"; 242 | VERSION_INFO_PREFIX = ""; 243 | }; 244 | name = Release; 245 | }; 246 | 0311F58F1B20A06F003616AD /* Debug */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | DEFINES_MODULE = YES; 250 | DYLIB_COMPATIBILITY_VERSION = 1; 251 | DYLIB_CURRENT_VERSION = 1; 252 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 253 | INFOPLIST_FILE = GoSwift/Info.plist; 254 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 255 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 256 | PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.$(PRODUCT_NAME:rfc1034identifier)"; 257 | PRODUCT_NAME = "$(TARGET_NAME)"; 258 | SKIP_INSTALL = YES; 259 | }; 260 | name = Debug; 261 | }; 262 | 0311F5901B20A06F003616AD /* Release */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | DEFINES_MODULE = YES; 266 | DYLIB_COMPATIBILITY_VERSION = 1; 267 | DYLIB_CURRENT_VERSION = 1; 268 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 269 | INFOPLIST_FILE = GoSwift/Info.plist; 270 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 271 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 272 | PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.$(PRODUCT_NAME:rfc1034identifier)"; 273 | PRODUCT_NAME = "$(TARGET_NAME)"; 274 | SKIP_INSTALL = YES; 275 | }; 276 | name = Release; 277 | }; 278 | /* End XCBuildConfiguration section */ 279 | 280 | /* Begin XCConfigurationList section */ 281 | 0311F5721B20A06F003616AD /* Build configuration list for PBXProject "GoSwift" */ = { 282 | isa = XCConfigurationList; 283 | buildConfigurations = ( 284 | 0311F58C1B20A06F003616AD /* Debug */, 285 | 0311F58D1B20A06F003616AD /* Release */, 286 | ); 287 | defaultConfigurationIsVisible = 0; 288 | defaultConfigurationName = Release; 289 | }; 290 | 0311F58E1B20A06F003616AD /* Build configuration list for PBXNativeTarget "GoSwift" */ = { 291 | isa = XCConfigurationList; 292 | buildConfigurations = ( 293 | 0311F58F1B20A06F003616AD /* Debug */, 294 | 0311F5901B20A06F003616AD /* Release */, 295 | ); 296 | defaultConfigurationIsVisible = 0; 297 | defaultConfigurationName = Release; 298 | }; 299 | /* End XCConfigurationList section */ 300 | }; 301 | rootObject = 0311F56F1B20A06F003616AD /* Project object */; 302 | } 303 | -------------------------------------------------------------------------------- /GoSwift/go.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * GoSwift (go.swift) 3 | * 4 | * Copyright (C) 2015 ONcast, LLC. All Rights Reserved. 5 | * Created by Josh Baker (joshbaker77@gmail.com) 6 | * 7 | * This software may be modified and distributed under the terms 8 | * of the MIT license. See the LICENSE file for details. 9 | * 10 | */ 11 | 12 | import Foundation 13 | 14 | private let pt_entry: @convention(c) (UnsafeMutablePointer) -> UnsafeMutablePointer = { (ctx) in 15 | let np = UnsafeMutablePointer<()->()>(ctx) 16 | np.memory() 17 | np.destroy() 18 | np.dealloc(1) 19 | return nil 20 | } 21 | public func dispatch_thread(block : ()->()){ 22 | let p = UnsafeMutablePointer<()->()>.alloc(1) 23 | p.initialize(block) 24 | var t = pthread_t() 25 | pthread_create(&t, nil, pt_entry, p) 26 | pthread_detach(t) 27 | } 28 | public protocol Locker { 29 | func lock() 30 | func unlock() 31 | } 32 | public class Mutex : Locker { 33 | private var mutex = pthread_mutex_t() 34 | public init(){ 35 | pthread_mutex_init(&mutex, nil) 36 | } 37 | deinit{ 38 | pthread_mutex_destroy(&mutex) 39 | } 40 | public func lock(){ 41 | pthread_mutex_lock(&mutex) 42 | } 43 | public func unlock(){ 44 | pthread_mutex_unlock(&mutex) 45 | } 46 | func lock(closure : ()->()){ 47 | lock() 48 | closure() 49 | unlock() 50 | } 51 | } 52 | public class Cond { 53 | private var cond = pthread_cond_t() 54 | private var mutex : Mutex 55 | public init(locker : Locker){ 56 | if let m = locker as? Mutex{ 57 | self.mutex = m 58 | } else { 59 | fatalError("Locker must be a Mutex (for now at least)") 60 | } 61 | pthread_cond_init(&cond, nil) 62 | } 63 | var locker : Locker { 64 | return mutex 65 | } 66 | 67 | deinit { 68 | pthread_cond_destroy(&cond) 69 | } 70 | func broadcast(){ 71 | pthread_cond_broadcast(&cond) 72 | } 73 | func signal(){ 74 | pthread_cond_signal(&cond) 75 | } 76 | func wait(){ 77 | pthread_cond_wait(&cond, &mutex.mutex) 78 | } 79 | } 80 | public class Once { 81 | private var mutex = Mutex() 82 | private var oncer = false 83 | public func doit(closure:()->()){ 84 | mutex.lock() 85 | if oncer{ 86 | mutex.unlock() 87 | return 88 | } 89 | oncer = true 90 | closure() 91 | mutex.unlock() 92 | } 93 | } 94 | public class WaitGroup { 95 | private var cond = Cond(locker: Mutex()) 96 | private var count = 0 97 | public func add(delta : Int){ 98 | cond.locker.lock() 99 | count += delta 100 | if count < 0 { 101 | fatalError("sync: negative WaitGroup counter") 102 | } 103 | cond.broadcast() 104 | cond.locker.unlock() 105 | } 106 | public func done(){ 107 | add(-1) 108 | } 109 | public func wait(){ 110 | cond.locker.lock() 111 | while count > 0 { 112 | cond.wait() 113 | } 114 | cond.locker.unlock() 115 | } 116 | } 117 | public protocol ChanAny { 118 | func receive(wait : Bool, mutex : Mutex?, inout flag : Bool) -> (msg : Any?, ok : Bool, ready : Bool) 119 | func send(msg : Any?) 120 | func close() 121 | func signal() 122 | func count() -> Int 123 | func capacity() -> Int 124 | } 125 | public class Chan : ChanAny { 126 | private var msgs = [Any?]() 127 | private var cap = 0 128 | private var cond = Cond(locker: Mutex()) 129 | private var closed = false 130 | 131 | public convenience init(){ 132 | self.init(0) 133 | } 134 | public init(_ buffer: Int){ 135 | cap = buffer 136 | } 137 | public init(buffer: Int){ 138 | cap = buffer 139 | } 140 | public func count() -> Int{ 141 | if cap == 0 { 142 | return 0 143 | } 144 | return msgs.count 145 | } 146 | public func capacity() -> Int{ 147 | return cap 148 | } 149 | public func close(){ 150 | cond.locker.lock() 151 | if !closed { 152 | closed = true 153 | cond.broadcast() 154 | } 155 | cond.locker.unlock() 156 | } 157 | public func send(msg: Any?) { 158 | cond.locker.lock() 159 | if closed { 160 | cond.locker.unlock() 161 | fatalError("send on closed channel") 162 | } 163 | msgs.append(msg) 164 | cond.broadcast() 165 | while msgs.count > cap { 166 | cond.wait() 167 | } 168 | cond.locker.unlock() 169 | } 170 | public func receive(wait : Bool, mutex : Mutex?, inout flag : Bool) -> (msg : Any?, ok : Bool, ready : Bool) { 171 | // Peek 172 | if !wait { 173 | cond.locker.lock() 174 | if closed { 175 | cond.locker.unlock() 176 | return (nil, false, true) 177 | } 178 | if msgs.count == 0 { 179 | cond.locker.unlock() 180 | return (nil, true, false) 181 | } 182 | let msg = msgs.removeAtIndex(0) 183 | cond.broadcast() 184 | cond.locker.unlock() 185 | return (msg, true, true) 186 | } 187 | // SharedWait 188 | if mutex != nil { 189 | cond.locker.lock() 190 | for ;; { 191 | mutex!.lock() 192 | if flag { 193 | mutex!.unlock() 194 | cond.locker.unlock() 195 | return (nil, true, false) 196 | } 197 | if closed { 198 | flag = true 199 | mutex!.unlock() 200 | cond.locker.unlock() 201 | return (nil, false, true) 202 | } 203 | if msgs.count > 0 { 204 | flag = true 205 | mutex!.unlock() 206 | let msg = msgs.removeAtIndex(0) 207 | cond.broadcast() 208 | cond.locker.unlock() 209 | return (msg, true, true) 210 | } 211 | mutex!.unlock() 212 | cond.wait() 213 | } 214 | } 215 | // StandardWait 216 | cond.locker.lock() 217 | for ;; { 218 | if closed { 219 | cond.locker.unlock() 220 | return (nil, false, true) 221 | } 222 | if msgs.count > 0 { 223 | let msg = msgs.removeAtIndex(0) 224 | cond.broadcast() 225 | cond.locker.unlock() 226 | return (msg, true, true) 227 | } 228 | cond.wait() 229 | } 230 | } 231 | public func signal(){ 232 | cond.broadcast() 233 | } 234 | } 235 | infix operator <- { associativity right precedence 155 } 236 | prefix operator <- { } 237 | prefix operator (l: Chan, r: T?){ 239 | l.send(r) 240 | } 241 | public prefix func (r: Chan) -> (T?, Bool){ 242 | var flag = false 243 | let (v, ok, _) = r.receive(true, mutex: nil, flag: &flag) 244 | return (v as? T, ok) 245 | } 246 | public prefix func <-(r: Chan) -> T?{ 247 | var flag = false 248 | let (v, _, _) = r.receive(true, mutex: nil, flag: &flag) 249 | return v as? T 250 | } 251 | public func close(chan : Chan){ 252 | chan.close() 253 | } 254 | public func len(chan : Chan) -> Int{ 255 | return chan.count() 256 | } 257 | public func cap(chan : Chan) -> Int{ 258 | return chan.capacity() 259 | } 260 | private struct GoPanicError { 261 | var what: AnyObject? 262 | var file: StaticString = "" 263 | var line: UInt = 0 264 | } 265 | private class GoRoutineStack { 266 | var error = GoPanicError() 267 | var (jump, jumped) = (UnsafeMutablePointer(), false) 268 | var select = false 269 | var cases : [(msg : Any?, ok : Bool)->()] = [] 270 | var chans : [ChanAny] = [] 271 | var defalt : (()->())? 272 | init(){ 273 | jump = UnsafeMutablePointer(malloc(4*50)) 274 | } 275 | deinit{ 276 | free(jump) 277 | } 278 | } 279 | private class GoRoutine { 280 | var stack = [GoRoutineStack]() 281 | func $(closure:()->()){ 282 | let s = GoRoutineStack() 283 | if stack.count > 0 { 284 | let ls = stack.last! 285 | s.error = ls.error 286 | ls.error.what = nil 287 | } 288 | stack.append(s) 289 | if setjmp(s.jump) == 0{ 290 | closure() 291 | } 292 | stack.removeLast() 293 | if s.error.what != nil{ 294 | if stack.count > 0 { 295 | panic(s.error.what!, file: s.error.file, line: s.error.line) 296 | } else { 297 | fatalError("\(s.error.what!)", file: s.error.file, line: s.error.line) 298 | } 299 | } 300 | } 301 | func panic(what : AnyObject, file : StaticString = __FILE__, line : UInt = __LINE__){ 302 | if stack.count == 0{ 303 | fatalError("\(what)", file: file, line: line) 304 | } 305 | let s = stack.last! 306 | (s.error.what,s.error.file,s.error.line) = (what,file,line) 307 | if !s.jumped{ 308 | s.jumped = true 309 | longjmp(s.jump, 1) 310 | } 311 | } 312 | func recover(file : StaticString = __FILE__, line : UInt = __LINE__) -> AnyObject?{ 313 | if stack.count == 0{ 314 | fatalError("missing ${} context", file: file, line: line) 315 | } 316 | let s = stack.last! 317 | let res: AnyObject? = s.error.what 318 | s.error.what = nil 319 | return res 320 | } 321 | func randomInts(count : Int) -> [Int]{ 322 | var ints = [Int](count: count, repeatedValue:0) 323 | for var i = 0; i < count; i++ { 324 | ints[i] = i 325 | } 326 | for var i = 0; i < count; i++ { 327 | let r = Int(arc4random()) % count 328 | let t = ints[i] 329 | ints[i] = ints[r] 330 | ints[r] = t 331 | } 332 | return ints 333 | } 334 | func select(file : StaticString = __FILE__, line : UInt = __LINE__, closure:()->()){ 335 | ${ 336 | let s = self.stack.last! 337 | s.select = true 338 | closure() 339 | let idxs = self.randomInts(s.chans.count) 340 | if s.defalt != nil{ 341 | var (flag, handled) = (false, false) 342 | for i in idxs { 343 | let (msg, ok, ready) = s.chans[i].receive(false, mutex: nil, flag: &flag) 344 | if ready { 345 | s.cases[i](msg: msg, ok: ok) 346 | handled = true 347 | break 348 | } 349 | } 350 | if !handled { 351 | s.defalt!() 352 | } 353 | } else if idxs.count == 0 { 354 | for ;; { 355 | if goapp.singleThreaded { 356 | fatalError("all goroutines are asleep - deadlock!", file: file, line: line) 357 | } 358 | NSThread.sleepForTimeInterval(0.05) 359 | } 360 | } else { 361 | let wg = WaitGroup() 362 | wg.add(idxs.count) 363 | let signal : (except : Int)->() = { (except) in 364 | for i in idxs { 365 | if i != except { 366 | s.chans[i].signal() 367 | } 368 | } 369 | } 370 | var flag = false 371 | let mutex = Mutex() 372 | for i in idxs { 373 | let (c, f, ci) = (s.chans[i], s.cases[i], i) 374 | dispatch_thread { 375 | let (msg, ok, ready) = c.receive(true, mutex: mutex, flag: &flag) 376 | if ready { 377 | signal(except: ci) 378 | f(msg: msg, ok: ok) 379 | } 380 | wg.done() 381 | } 382 | } 383 | wg.wait() 384 | } 385 | } 386 | } 387 | func case_(chan : ChanAny, file : StaticString = __FILE__, line : UInt = __LINE__, closure:(msg : Any?, ok : Bool)->()) { 388 | if stack.count == 0 || !stack.last!.select { 389 | fatalError("missing select{} context", file: file, line: line) 390 | } 391 | let s = stack.last! 392 | s.cases.append(closure) 393 | s.chans.append(chan) 394 | } 395 | func default_(file : StaticString = __FILE__, line : UInt = __LINE__, closure:()->()) { 396 | if stack.count == 0 || !stack.last!.select { 397 | fatalError("missing select{} context", file: file, line: line) 398 | } 399 | let s = stack.last! 400 | if s.defalt != nil { 401 | fatalError("only one default{} per select{}", file: file, line: line) 402 | } 403 | s.defalt = closure 404 | } 405 | 406 | } 407 | private class GoApp { 408 | typealias QueueID = UnsafeMutablePointer 409 | private var gocount = 0 410 | private var mutex = pthread_mutex_t() 411 | private var routines = [QueueID: GoRoutine]() 412 | init(){ 413 | pthread_mutex_init(&mutex, nil) 414 | } 415 | deinit{ 416 | pthread_mutex_destroy(&mutex) 417 | } 418 | func lock(){ 419 | pthread_mutex_lock(&mutex) 420 | } 421 | func unlock(){ 422 | pthread_mutex_unlock(&mutex) 423 | } 424 | var queueID : QueueID { 425 | return QueueID(pthread_self()) 426 | } 427 | func routine() -> GoRoutine { 428 | let id = queueID 429 | lock() 430 | var r = routines[id] 431 | if r == nil { 432 | r = GoRoutine() 433 | routines[id] = r 434 | } 435 | unlock() 436 | return r! 437 | } 438 | 439 | var singleThreaded : Bool{ 440 | var res = false 441 | lock() 442 | res = gocount == 0 443 | unlock() 444 | return res 445 | } 446 | 447 | func _go(closure: ()->()){ 448 | lock() 449 | gocount++ 450 | unlock() 451 | 452 | routine().$(closure) 453 | 454 | lock(); 455 | routines[queueID] = nil 456 | gocount-- 457 | unlock() 458 | 459 | } 460 | func go(closure: ()->()){ 461 | dispatch_thread{ 462 | self._go(closure) 463 | } 464 | } 465 | } 466 | private let goapp = GoApp() 467 | public func $(closure: ()->()){ 468 | goapp.routine().$(closure) 469 | } 470 | public func go(closure: ()->()){ 471 | goapp.go(closure) 472 | } 473 | public func panic(what : AnyObject, file : StaticString = __FILE__, line : UInt = __LINE__){ 474 | goapp.routine().panic(what, file: file, line: line) 475 | } 476 | public func recover(file : StaticString = __FILE__, line : UInt = __LINE__) -> AnyObject? { 477 | return goapp.routine().recover(file, line: line) 478 | } 479 | public func select(file : StaticString = __FILE__, line : UInt = __LINE__, closure:()->()) { 480 | goapp.routine().select(file, line: line, closure: closure) 481 | } 482 | public func _case(l : Chan, file : StaticString = __FILE__, line : UInt = __LINE__, closure:(msg : T?, ok : Bool)->()) { 483 | goapp.routine().case_(l, file: file, line: line, closure: { (msg, ok) in closure(msg: msg as? T, ok: ok) }) 484 | } 485 | public func _default(file : StaticString = __FILE__, line : UInt = __LINE__, closure:()->()) { 486 | goapp.routine().default_(file, line: line, closure: closure) 487 | } 488 | --------------------------------------------------------------------------------