気温の上下が激しすぎて俺の体はもうだめだ。
先々週は急に寒くなって風邪引いた。寒くなるだけならたぶん大丈夫だったんだが、会社の中は容赦無く暑いので、外が11℃とかでも中に入ると28℃。コート着て来るようなのに、中はTシャツ1枚でも暑いとか完全に殺す気。
先週は風邪もやっと治ってきたところで普通に暑くなったので体調がずっとおかしいままで、週末に熱が出たんで寝てた。 あと歯がめちゃくちゃ痛くなったんだけど、一日である程度落ち着いたし、レントゲンでは特に問題ないというので(別の治療中の歯が終わるまで)しばらく様子見ということになった。 気温変化の体調不良で古傷が疼く的なやつだったかもしんない。
土曜は一日寝てて、日曜も起きたら午後だったけど、髪切りにだけ行ってきた。さすがにもう一週間ほっとくには長くなってたしなー。 だいたい良くなった気はしてるけど、ちょっと頭ぼーっとするところあるし、おなかの調子も微妙なんで万全とは言えないかもな。
今のアップデートって、起動時にDLLをコピってメインのクラスを別AppDomainで起動し、アップデート時は元のDLLを上書きしたあとAppDomainをアンロードして新しいAppDomainを起動ということをやっていた。
これちょっといろいろ問題あって、AppDomain内でスレッドがまだ生きてるのにアンロードしようとすると文句言われたり、新しいAppDomain側でCtrl+Cをトラップするのが動かなかったり、デバッグもなんか怪しかったりと、あんま良い作りじゃなかった。
なんでこんなことしていたかというと、monoで新しいプロセスの起動が難しいと思っていたので同一プロセス内で処理しようとした結果だった。
monoで新しい.NETのプロセス起動しようとしたらmono newhoge.exe
とか起動すれば良さそうに思えるが、元のプロセス起動したmonoがパス通ってるところにあるmonoとは限らないし、mono自体になんか引数指定していたかもしれないが、.NETのレベルからはmonoのコマンドラインは取れないのであった。/tmp/mono --mono-option newhoge.exe arg1 arg2
とかやったら取れるのはnewhoge.exe arg1 arg2
の部分だけ。/tmp/mono --mono-option
の方は取れないので、自分が起動しているmonoと同じmono同じ設定でサブプロセスを起動させるのは無理なのだ。
……と思い込んでいたんだが、実はProcess.Startで普通に起動できるのを知った。monoではProcess.Startで起動するファイルに.NETのアセンブリ(exe)を指定すると、勝手に自分と同じmonoに同じオプションを指定して新しいプロセスを起動してくれるのだった。だからProcess.Startにmono
コマンドを指定するんじゃなくて直接exeを指定すりゃ良かったのだ。なんだそれ知らんかったわ。
ちなみにこれはDuplicatiのソースを読んで知った。monoでアップデートどうやってんだべと読んでみたら普通にexe起動しててびびったんで、monoのソースを見てみたらそうなってたわけだ。
で、AppDomain別にするのはちょっとよろしくないし、プロセス分けるのが比較的簡単にできるのが分かったんでこの方向で作り直しましょうという話。
最初は起動プロセスにアップデート部分を埋め込もうとしたけど、アップデータは分けて、メインプロセスはアップデータを起動して死んで行き、アップデータはアップデートが終わったら新しいメインプロセスを起動して死んで行くことにした。よく考えたらこれ普通のインストーラと同じ動きだしこっちの方が良いでしょう。
作ってみると特に問題なく出来た……と思ったんだが、いざmonoで試すと落ちまくる。 普通にファイル置き換え自体がバグりまくってたのは仕方ないんで直したが1、新しいメインプロセス起動時に落ちてしまうのはどうしようもない。 新しいメインプロセスはCtrl+Cをトラップしようとして標準入力から読み込めねえよって落ちている。俺の書いたコード内で落ちてるわけじゃないから手が出ねえな。 てかなんで標準入力から読めない?
よく考えると、新しいプロセスが起動した時点で親のプロセスは死んでいってしまっている。親プロセス死んだところで共有されてる標準入出力が閉じられているのでは?と思ったが調べると標準入出力は常に使えるぽい。標準出力らしいのは出てるし。ただ親のプロセスが死ぬとシェルの子プロセスじゃなくてinit(macOSなんでinitじゃねえかもしれんが)の子に移動しちゃうぽい。で、シェルとの入出力の協調ができなくなると。そりゃそうだな。それでおかしくなっているのか。
なんとかできねえか調べてみるが、.NETではできなさげ。
親プロセスが死んでいきながら子プロセスをシェルにくっつけておきたいてのが無理筋で、やるなら親プロセスはfork
せずにexec
して新しいプロセスに成り代わるのが筋なのかな?
ただ、.NETやmonoにはそんなexec
のみするてな機能はない。monoで無理矢理システムコールのexec
呼ぶのは出来そうだが、それやっても大変なことになるだけだし。
似たような機能としては新しいAppDomain作って、前のAppDomainをアンロードすればできるけど、それ最初にやってたやつだー。あんますっきりいかないやつだー。
そんなわけで仕方ないので子プロセスは常に待ち合わせることにしよう。 先に考えたように起動プロセスは死なずに、メインプロセスを別に起動し待ち合わせ、アップデートがあったらメインプロセス終了後にアップデータプロセスを起動してやっぱり待ち合わせ、アップデータ終了後はまた新しいメインプロセスを起動して待つ、という形にした。 起動中のファイルは上書きできないので2、起動プロセスはアップデートできないんだけど、それは元々そうだったから仕方ないかー。 アップデータは分離してるんで、やってることは薄いしたぶん大丈夫だろう。 アップデータ自体はコピーを起動してるので上書きできる。
まあ作り自体はそんな難しくはないから出来たけど、メインプロセスが別実行ファイルになったのはちょっと微妙だな。 あとサービス版の方も作り変えないと。
なんか結局AppDomain形式のアップデートとあんま変わりなくなってしまった。 スレッドきっちり終わってなくても終了させられるようになったのはいいけど本当にこれでいいんだろうか? AppDomain形式のアップデートをちゃんと作り直した方がいいかもしれない。