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

Implement BackgroundTaskCompletion and notification #290

Merged
merged 8 commits into from
Jun 5, 2023

Conversation

shibukazu
Copy link
Contributor

@shibukazu shibukazu commented May 24, 2023

Related Issue

close #287
close #288
close #289

What

  • BackgroundTaskCompletionを利用することでバックグラウンドで認識タスクを実行した場合でも、すぐにアプリがキルされないように
  • 認識タスクが残っている状態でバックグラウンドへの遷移時に通知を送信するように
    • BackgroundTaskCompletionのみでは30s程度経過した後、アプリがキルされてしまうため
  • 下記のissueを解決するためにOpenlyアカウントにPartialSheetをフォークし、修正を加えたものをパッケージとして利用するように変更

Memo

@shibukazu shibukazu self-assigned this May 24, 2023
@shibukazu shibukazu changed the title Implement BackgroundTaskCompletion and notification [WIP] Implement BackgroundTaskCompletion and notification May 24, 2023
@shibukazu
Copy link
Contributor Author

通知の多言語対応のために一旦WIPに戻します

@shibukazu shibukazu changed the title [WIP] Implement BackgroundTaskCompletion and notification Implement BackgroundTaskCompletion and notification May 24, 2023
@ooyamatakehisa
Copy link
Contributor

これって今DispatchQueueに4個タスクがあったとして、バックグラウンドにした瞬間にその1つ目のタスクが実行されてたとしたら、それを止めるのはむりそうだけど、2つ目以降のタスク(のこり3つのタスク)は
https://developer.apple.com/documentation/dispatch/dispatchobject/1452801-suspend
これで実行とめたら、裏でリソース使わんくなってキルされないことないかな?
1つのタスクの完了だけなら30sとかでなんとか終わりそうだし(モデルによるけど)

@shibukazu
Copy link
Contributor Author

DispatchQueueに入っているジョブに対してsuspendはめっちゃよさそうです。
ただ、「30s間はバックグラウンドで実行できるということ自体が保証されていない」ことおよび「デバイスによってはsmallモデルですら30sで終了するか不明である」ことからBackgroundTaskCompletionおよび通知は必要だと思います。

@ooyamatakehisa
Copy link
Contributor

@shibukazu あ、もちろん、通知とかあった上でっていう意味やった!
するとしてもまた別のプルリクでする?

@shibukazu
Copy link
Contributor Author

なるほどです。
修正範囲が大きくなりそうなので別のPRで取り組みたいです!!

@ooyamatakehisa
Copy link
Contributor

ooyamatakehisa commented May 29, 2023

@shibukazu このコミットでggml-small.en.binがおそらくshibukazuのローカル環境の何処かを指すようになっててしまってて、こっちでビルドできないから直してもらえるとたすかる!

Copy link
Contributor

@ooyamatakehisa ooyamatakehisa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いろいろ試したんだけど、自分の環境では、beginBackgroundTaskexpirationHandlerが呼ばれる前に、バックグラウンドに入ったら2,3秒後にはアプリがキルされてしまってて、beginBackgroundTaskは単にsuspended状態(コードが何も実行されない状態)に入るまでの時間を伸ばすだけで、キルされる可能性はあるかなと思う。

これを回避するためにはやっぱり、このメソッドとかでバックグラウンドに入った瞬間を検知して、これとかで認識タスクを止めるしかないのかなと思う。

まあなんにしてもこのPRの範疇は超えるからこのPRはとりあえずこのままマージするようにする?

}
let identifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time

一応これを見る感じ、中でも
UIApplication.shared.endBackgroundTask(identifier)を読んだ方がよさそうかも?

@shibukazu
Copy link
Contributor Author

バックグラウンドに入って2、3秒経過してすぐにキルされたってことですか?
通知は出ましたか?

@ooyamatakehisa
Copy link
Contributor

@shibukazu 通知はちゃんとでるんだけど、でた直後くらい
Message from debugger: Terminated due to signal 9というメッセージとともにキルされる!

これが意図されたもの何かわからんから、質問をなげてみたけど、答えがつくかはわからん笑
image

@shibukazu
Copy link
Contributor Author

ちなみに実機ですかね?
正直CPUとかメモリ使用率とかで強制キルはあり得そうですね

@ooyamatakehisa
Copy link
Contributor

@shibukazu 実機です!

@shibukazu
Copy link
Contributor Author

あとcancelに関しては

Cancellation causes future attempts to execute the work item to return immediately. Cancellation does not affect the execution of a work item that has already begun.

これを見た感じ始まってしまった認識タスクには影響できないっぽい..?

@shibukazu
Copy link
Contributor Author

30sという秒数が保証されていないとすると通知の文言を修正したいです。
今日はもう寝るので、時間があるようでしたら他のPRのチェックをお願いします🤲

@ooyamatakehisa
Copy link
Contributor

@shibukazu 確かに始まったタスクはもう無理なのか....
ならもうキルを防止する方法はないのか....
賞味、結構バックグラウンドで録音できないの痛いし、なんとかしたみはあるけどなあ

@ooyamatakehisa
Copy link
Contributor

本気でやろうと思えば、whisper_cppのwhisper_freeメソッドは呼べばやろうと思えばできそうかも。
https://github.com/ggerganov/whisper.cpp/blob/d7c936b44a80b8070676093fc00622333ba09cd3/whisper.cpp#L2840

モデル動いている途中にこれ読んだらどうなるかはわからんけど笑

@shibukazu
Copy link
Contributor Author

本気でやろうと思えば、whisper_cppのwhisper_freeメソッドは呼べばやろうと思えばできそうかも。 https://github.com/ggerganov/whisper.cpp/blob/d7c936b44a80b8070676093fc00622333ba09cd3/whisper.cpp#L2840

モデル動いている途中にこれ読んだらどうなるかはわからんけど笑

キルすることはできるかもしれませんが、キルされたジョブを復元してDispatchQueueの先頭に入れ直すのが難しいと思います。

@shibukazu
Copy link
Contributor Author

一応このPRにおける残りのタスクとしては

  • UIApplication.shared.endBackgroundTask(identifier)をexprirationHandlerでも呼び出す
  • 通知の文言の修正

上記達成され次第マージでよろしいでしょうか?

@ooyamatakehisa
Copy link
Contributor

ooyamatakehisa commented May 31, 2023

各タスクをDispatchWorkItemとしてラップして、このDispatchWorkItemを他のリストとかに別に保持しとけば、あとでフォアグラウンドに戻ったときにDispatchQueueに入れ直せるくない?リストからは本当に認識タスクが終わったタイミングで削除する的な。

というかまあDispatchWorkItem使わんでも必要な情報だけ適当なstruct二保存するだけでいい気もするけど

@ooyamatakehisa
Copy link
Contributor

一応このPRにおける残りのタスクとしては
UIApplication.shared.endBackgroundTask(identifier)をexprirationHandlerでも呼び出す
通知の文言の修正
上記達成され次第マージでよろしいでしょうか?

OK 👍

@shibukazu
Copy link
Contributor Author

shibukazu commented May 31, 2023

各タスクをDispatchWorkItemとしてラップして、このDispatchWorkItemを他のリストとかに別に保持しとけば、あとでフォアグラウンドに戻ったときにDispatchQueueに入れ直せるくない?リストからは本当に認識タスクが終わったタイミングで削除する的な。

というかまあDispatchWorkItem使わんでも必要な情報だけ適当なstruct二保存するだけでいい気もするけど

なるほど、いまのclosureを配列として保持しておいて、キャンセルされた場合はQueueを再構成してあげるのが良さそうですね。(既存のQueueの任意の位置に入れるのは無理そう)
ただ現状のストリーミング認識の方法だと各セグメントが認識されるごとにRecognizedSpeechにTranscriptionLineを追加してしまっているので、途中でジョブがキャンセルされた場合に中途半端な認識結果を取り除いてあげるなどの後処理が必要そうですね
なので、理想としては認識ジョブをクロージャ内で閉じさせるか、DBにおけるトランザクションのような感じでロールバックできる形で実装できるといいですね。

@ooyamatakehisa
Copy link
Contributor

@shibukazu そうそう、Queueの再編というのを書き忘れたけど、それを意図してた。closureを配列としてもつでもいいし、自分はstreamingRecognition関数の引数の値たちを保持しとけば、あとで同じクロージャーは再現できるかなと思ってた。

ただ現状のストリーミング認識の方法だと各セグメントが認識されるごとにRecognizedSpeechにTranscriptionLineを追加してしまっているので、途中でジョブがキャンセルされた場合に中途半端な認識結果を取り除いてあげるなどの後処理が必要そうですね

確かにこの問題があるのか...
まあ途中で終わっても、新しく認識やり直すときに入れるTranscriptionLineが、もうDBにはいってるやつの時間よりあとか前かを見て、すでにDBに入っているやつと重複しないようにするというのもいける??whisperがnon deterministicなら厳しいか?

まあどっちにしろこれやるとすると割と重いタスクだし一生やらなさそう笑
これでキルされなくなるなら結構大事やけど笑

shibukazu added 2 commits June 3, 2023 00:01
add UIApplication.shared.endBackgroundTask to expiration handler
var body: some Scene {
WindowGroup {
StartView()
.onChange(of: scenePhase) { phase in
if phase == .background {
if numRecognitionTasks > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

厳密にいったらスレッドセーフじゃなさそうなのと、グローバル変数よりはRecognizerのプロパティのほうがいいかな、とか思ったけど、なおすのめんどうだなのでとりあえずコメントだけ!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numRecognitionTasksの書き換えはserialDispatchQueueの中でしか起きないので現状は安全ですが、確かにメインスレッドでも参照する以上、スレッドセーフであるべきですね。
Issueに切り出しておきます。

@ooyamatakehisa
Copy link
Contributor

遅くなってすまん!LGTM!

@ooyamatakehisa
Copy link
Contributor

ごめんまじで手が滑って間違えてクローズしてもうた

@shibukazu shibukazu merged commit fc5051d into dev Jun 5, 2023
@shibukazu shibukazu deleted the fix/background-task branch June 5, 2023 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants