kubo39's blog

ただの雑記です。

D言語で一ヶ月前を表すとき

日付の扱いは忘れそうなのでメモ。

一ヶ月前を表す場合、可能であれば前月の同日同時刻を表し、前月に同日が存在しない場合は差分を計算して付け足す。

import std.datetime;
import std.stdio;

void main()
{
    auto currTime = Clock.currTime(UTC());
    writeln(currTime);                              //  2018-Sep-30 20:12:00.0198441Z
    writeln(currTime.add!"months"(-1));  // 2018-Aug-30 20:12:00.0198441Z
    currTime = Clock.currTime(UTC());
    writeln(currTime.add!"months"(1));  // 2018-Oct-30 20:12:00.0199153Z

    auto date = Date(2018, 7, 31);
    writeln(date.add!"months"(-1));  // 2018-Jul-01
    date = Date(2018, 7, 30);
    writeln(date.add!"months"(-1));  // 2018-Jun-30
}

なので3月とかはこうなる。

import std.datetime;
import std.stdio;

void main()
{
    auto date = Date(2018, 3, 31);
    writeln(date.add!"months"(-1));  // 2018-Mar-03
    date = Date(2018, 3, 29);
    writeln(date.add!"months"(-1));  // 2018-Mar-01

    // うるう年は考慮される
    date = Date(2016, 3, 29);
    writeln(date.add!"months"(-1));  // 2016-Feb-29
}

D言語のprecise(正確な) GC

(追記: 編集あり)

実は結構前に保守的なGCから正確なGCになって いた いなかった。仕組みはマーク・アンド・スィープで変わらないが性能は大幅に向上が期待できそう。

github.com

これによって、

  • メモリ確保時
    • allocatorの使い分けによる効率的なメモリ管理(それぞれのアロケータの特性が活かせる)、これは保守的GCでは安全側に倒さざるをえなかったので最大のメリットになる
    • スレッドローカルキャッシュを使うことでグローバルなGCロックを避けられる
  • ゴミ集め時
    • 並列にスキャンするようになったのでパフォーマンスが向上
    • 特定の条件を満たしていればストップ・ザ・ワールド(STW)が発生しなくなる
      • 例えば新たに追加された型レベルの仕組み isolated によって静的にスレッドローカルなヒープのみを使っているかを判定(isolatedはスレッドローカルなヒープにつく型といえる)でき、isloatedしかないと判定したときはSTWしない

わりとGoに近い感じで、アロケータの柔軟性でGoより優れていて、インクリメンタルGCによるレイテンシの仕組みはシステムプログラミング言語という特性上入れなかったという感じか。 さすがにJVMに比べると厳しいが、これまでの保守的GCに比べるとずいぶんよくなっている、はず。

(追記ここから)

まだマージされてなかった。。すまんな、、

OpenSUSEを試してみてる

デスクトップ環境として最新のLeap使ってみてる。 感想としては、

  • IM(Mozx <-> US)の切り替えがめちゃくちゃ遅い。普通にタイピングに支障が出るレベル。
    • 方向関係なく、「hankakuzenkaku」で切り替えると遅く、「hankakuzenkaku+Shift」だと気にならない程度の遅さになる。追ってみてるがよくわからん。
  • キーボードレイアウトの切り替えが何故か反映されない?setxkbmapでは変更できずYaSTを使って一時的にできたと思ったがいつのまにかまたUS配列に戻っていた。
  • 「設定」とYaSTの切り分けが意味不明。設定の「デバイス」->「キーボード」でbindingとかを変更するようになっているのにキーボードレイアウトはYaSTとか意味がわからん。
  • GNOMEデフォルトのキーバインドの操作性がいまいち
    • これは慣れもある気がするけどなんとなく使いづらい感じはしてる、こっちもi3wmにしようかどうか

総じてクソと言わざるをえない、なんのためにわざわざLeap選んだと思ってんだよ。こういう雑事に悩まされないための初心者向けのやつなんじゃねえのか。 Ubuntuもいろいろ不満に思うことはあるけど、完成度は天地ほどの差がある。

まあ単に慣れてない、わかってないだけならいいんだけど、まさかこのレベルではまるとはなあ。。。とりあえず初心者にはおすすめできないですね。

プロセス起動でposix_spawnとかvforkとかを使うはなし

posix_spawn/vfork/clone(CLONE_VFORK) はメモリを共有するので速い. 速いが、親子でメモリを共有するので危険である. それでも速いのでプロセス起動で気をつけて使っている言語があり、どういう実装をしているか調べた.

前提

Rust

posix_spawnを使う条件

  • ENOENTを直接返すこと
    • 単にposix_spawn側の実装バグの問題
  • Linux/MacOS/FreeBSDであること
    • ENOENTを直接返すことができるOSのため
  • Linuxの場合, Glibcのバージョンが2.24以上であること(それ以前ではposix_spawnが存在していない)
    • ENOENTを返すposix_spawnはこのバージョンから
  • getcwdでディレクトリを返すこと
  • getgidが成功する
  • getuidが成功する
  • PATHが変更されていないこと(?)

どう使っているか

  • posix_spawnに持たせる属性を決めている
  • posix_spawnattr_setsigmask シグナルをマスクしている
    • forkするスレッドはfork-exec間でシグナルを受け付けたくない
  • posix_spawnattr_setsigdefault SIGPIPEをSIG_DFLにしている
    • シグナルハンドラでグローバル変数を参照するかもしれないので避けたい
    • 別スレッドがシグナルをうけたときにメモリを書き換えるかも、という危険があるので
    • (追記): そういえばRustはランタイム初期化時にSIGPIPEをSIG_IGNにセットしているので、その設定によって起動するプロセスが影響を受けることを避けたいためにSIG_DFLにしなおしているというほうがメインな気がする

まとめ

Dellの中の人でFreeBSDのコアコミッタの人なので、けっこう信頼できるんじゃないだろうか. ただいくつか気になる点はあり、

  • すべてのシグナルに対してSIG_DFLを定義しなおさなくてもいいのか
  • uid/gidが特権ユーザでないことはチェックしなくてよいのか
    • これはposix_spawn側で対策されていそうな雰囲気がある
  • PATHの変更をみていること
    • PATHは状態変わることで振るまいは変わるが具体的にどういった問題があるんだろう

Go

clone(CLONE_VFORK)を使う条件

どう使っているか

  • clone(2)に SIGCHLD|CLONE_VFORK|CLONE_VM を指定して使っている
  • cloneでシステムコール呼び出しをしている場合, 親子はスタックを共有しない

fork-exec間に子プロセスは何をしているか

  • keep capabilitiesをprctlでセットしている(実行ユーザとかが変更されたかチェック)
  • ほかにもいろいろやっているがforkと共通なので割愛
    • 主にcapabilitiesとかなので権限まわりをセキュアにしたいのだろう

まとめ

自前でがんばっているためコードはかなり複雑になる. 現時点では制限がかなり厳しい(Linuxかつamd64のみ)ので、逆に意図しない問題は起きづらそう. 当然ゴルーチンが全部停止するためスループットが悪化するという懸念はされている.

Ruby

vforkを使う条件

  • vforkを実装していること
  • uid/gidが特権ユーザでないこと
    • setuidとかされたくない

fork-exec間に子プロセスは何をしているか

  • シグナルに対してSIG_DFLを再定義
    • invalidなsignumは無視
    • SIG_IGN が定義されてたら SIG_IGN
      • その場合でもSIGPIPEはSIG_DFLにする
  • sigprocmaskでシグナルをmaskする
    • pthread_sigmaskでないのはasync signal safeなものを使いたいため
  • pthread_setcancelstatePTHREAD_CANCEL_DISABLE して cancel が無効であることを宣言する
    • pthread_cleanup_push で登録した cancellation cleanup handler も呼び出されるのは困る
    • シグナルハンドラと同様の理由

まとめ

だいぶ安全よりに倒しているのではないか.

Nim

posix_spawnを使う条件

  • posix_spawnが存在していてLinuxでないこと
    • Linuxだとclone(2)を使う
    • いろいろよくわからない条件指定がある

cloneを使う条件

  • Linuxだったら基本使う
    • fork-exec間はasync-signal-safeな関数しか使わないようになっている

どう使っているか

  • 基本Rustと一緒だけどシグナルまわりが怪しい
    • 具体的にいうとSIGPIPEをSIG_DFLにセットしていないとかが違う

まとめ

基本的に優良な実装にみえる. ただしいろいろ不明すぎて安全に使えるのか不安. シグナルまわりとか.

D言語の八進数リテラルの特殊扱い

D言語はかつて八進数リテラルを定義していたが、現在は仕様上Invalidとなっており std.conv.octal を使うようにエラーメッセージが出現する。

(dmd-2.081.1)$ cat octal.d
void main()
{
    auto _ = 010;
}
(dmd-2.081.1)$ dmd octal.d
octal.d(3): Error: octal literals 010 are no longer supported, use std.conv.octal!10 instead

ところが 00 ~ 07 の八進数リテラルのみ特殊扱いしており、以下のコードは評価可能であり実行もされる。 (実はある時点で 0809 というそもそも不正なリテラルをうけつけるバグが混入してしまったのだが、これは修正しておいたので2.082.0以降では正しくエラーとなる)

void main()
{
    assert(01 == 1);
}

これは特殊扱いというより過去そのまま残ったバグなのであるが、phobosをはじめ2文字で表す八進数リテラルは広く使われているために今後修正は(おそらく)行われないだろう。

つまりD言語には隠れた仕様上不正だが許容されている構文が存在しているということである。

D言語の静的配列のちょっとかわった挙動

以下のコードの振る舞いはちょっと変わった感じをうけるかもしれない。

int[2] ARR = [5];

void main()
{
    // int[2] arr = [5]; // Error: mismatched array lengths, 2 and 1
    assert(ARR == [5, 0]);
}

この場合、D言語の静的配列はグローバルに宣言した場合は初期化(ArrayInitializer)として意味解析を行い、ローカルの場合は式(AssignExpression)の文脈で意味解析を行う。 これは構文上問題はないのだが意味論は定義されていないし、ぱっと見た感じ不自然な挙動に思える。 ここでもしエラーにする場合、単純にローカルと同じように扱ってしまえばいいのではと思うが、DのArrayInitializerで評価可能な構文に以下のようなものがある。

int[4] arr = [1:2]; // [0, 2, 0, 0]

これは現在(dmd-2.081.1)意味解析上で先のグローバルな配列宣言と区別することができないので、仮にエラーとする場合はおそらく構文解析に手を入れる必要があると思われる。

追記

実際試してみるとパーサいじる必要はなくすんなり修正できる。しかし、これなんて言って報告すればいいんだろう。

DMDにContributeした

D言語の数値まわりの字句解析のはなし - kubo39's blog これがほっといてもなおってなかったので、自分で報告して修正した。

github.com

DMDのビルドははやくて快適だけど、フルテスト回すとそこそこ時間がかかる。

追記

今きづいたけどエラーメッセージにSuffix入らんのでInteger LiteralじゃなくてIntegerにしたほうがよかった。。

あとDeprecationからErrorに昇格したときに0x._pのときにエラーメッセージが正しくならないような気がする。 まあこっちはその時考えればええんちゃう?という感じだけど。