Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/mobile: UI hangs when Go code called from Swift communicates over the network; time.Sleep works fine #65371

Open
SuzukiTakuto opened this issue Jan 30, 2024 · 5 comments
Labels
mobile Android, iOS, and x/mobile NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@SuzukiTakuto
Copy link

I used quic-go to implement a function to send messages to the server and receive data, which I turned into an iOS library with the gomobile bind command.

func Fetch(message string) ([]byte) {
	fmt.Printf("Client: Sending '%s'\n", message)
	
	stream, err := conn.OpenStreamSync(context.Background())  // conn is of type quic.Connection.
	if err != nil {
		fmt.Printf("go_Client: Error opening stream: %s\n", err)
	}

	fmt.Printf("go_client: Sending '%s'\n", message)
	_, err = stream.Write([]byte(message))
	if err != nil {
		fmt.Printf("go_client: Error writing to stream: %s\n", err)
	}

	bufferSize := 1024
	var receivedData []byte 

	for {
		buf := make([]byte, bufferSize)
		n, err := stream.Read(buf)
		if err != nil {
			if err == io.EOF {
				break 
			}
			fmt.Printf("Client: Error reading from stream: %s\n", err)
			break
		}
		receivedData = append(receivedData, buf[:n]...) 
	}
	
	stream.Close()

	return receivedData
}

This function is called in swift as follows.

func fetchObjectFromServer(_ message: String, completion: @escaping (Data?) -> Void) {
    Task.detached{
        let data = QuicFetch(message)
        if let unwrappedData = data {
            completion(unwrappedData)
        } else {
            completion(nil)
        }
    }
}

I am using "Task.detached" to make asynchronous calls, but when the QuicFetch function starts processing, the UI stops.

I experimentally modified the code of golang's Fetch function as follows and used it in the same way in swift.

func Fetch() {
	sleepTime := 10 * time.Second
	fmt.Printf("start sleep\n")
	time.Sleep(sleepTime)
	fmt.Printf("end sleep\n")
}

This function was properly processed in the background and the UI did not stop.

I would like to know why the UI stops when trying to process in the background only when communicating with quic-go.

@gopherbot gopherbot added the mobile Android, iOS, and x/mobile label Jan 30, 2024
@gopherbot gopherbot added this to the Unreleased milestone Jan 30, 2024
@mknyszek
Copy link
Contributor

If I were to guess, the calls you're making in Fetch end up doing syscalls on iOS that the Go runtime needs to block the thread. If you end up on the UI thread (usually the main thread, which I suppose might be where you're calling into Go from Swift) then you'll see UI stalls. This doesn't fully explain why time.Sleep works though, since once you call into Go, the goroutine created for the thread calling into Go is bound to that thread. That being said, time.Sleep does not in general block a thread, and the goroutine just ends up in some timer data structures, to be executed later.

Do you have more details on how Go is being called from Swift? I'm not personally familiar with x/mobile, so I don't know what exactly the intermediate code is doing. I assume the call is going through cgo, but other than that, it would be good to have more details from someone familiar.

@mknyszek mknyszek changed the title x/mobile: Asynchronous bug occurs when the method that communicates with the server in qui-go is an IOS library. x/mobile: UI hangs when Go code called from Swift communicates over the network; time.Sleep works fine Jan 30, 2024
@mknyszek mknyszek added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jan 30, 2024
@mknyszek
Copy link
Contributor

CC @golang/runtime and maybe @hyangah for x/mobile knowledge?

@SuzukiTakuto
Copy link
Author

The XCFramework is created by executing the command "gomobile bind -target ios". This can be loaded in xcode to call Go from swift.

@SuzukiTakuto
Copy link
Author

@hyangah , @crawshaw , do you have any solutions? I would like you to check this issue if you don't mind.

@rohansingh
Copy link

rohansingh commented Jul 27, 2024

I was seeing something similar, but fixed it by moving the Go call out of the UI thread.

@SuzukiTakuto In your code above, it looks like you are using Task.detached. I'm far from a Swift expert, but my understanding is that a "task" is part of Swift's lightweight concurrency model. It does not actually spawn a new thread.

Go doesn't know anything about Swift tasks and cannot yield to other Swift tasks. I'm guessing that's why it blocks the thread entirely. If you spawn an actual new thread, that should solve the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mobile Android, iOS, and x/mobile NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants