スポンサードリンク

Aurex for Android のソース公開

汚ないながらAurexforAndroid(androaurex(仮))を公開します。

ソースはかなり汚ない&コメント無し、また、凄い不安定です、、、スミマセン

実行バイナリについては、いずれAndroidマーケット(Google play)に公開したいと思っています。ファイル選択と、フォント描画ができれば大体公開できるはず。。。

# ちなみにSDカード直下の RForce.exf (/sdcard/rforce.exf) しか開けないバイナリなら以下の zip の bin/androaurex.apk をインストールすると使えますw

ダウンロード:
androaurex_20120307.zip(コンパイルの中間ファイルなども含んでいるため5MB程度あります)

(続)Ruputerエミュレータ(Aurex) on Android

現在の状況(相変わらずRForceを使わせて頂いています)

AndroAurex

  • キー入力を行なえるようにした(上記スクリーンショットの真ん中は方向キー入力用の領域)
  • 表示が小さかったので、拡大表示するようにした

残る大きなところはファイル選択かなぁ。

Ruputerエミュレータ(Aurex) on Android

(いつの間にか始めましたが)ツイッターで報告したとおり、Ruputer エミュレータAurexのAndroidへの移植しています。Twitter / @まちだ: AndroidでRuputerエミュ。キャプチャはR …

現在のところ画面が出るだけでキー入力もできませんが、NDK(ネイティブ)を使っているために手持ちのDesireではフレームスキップなしに動作しています。

ゆっくりとはなってしまいますが今後も開発しつづけ、いずれ皆さんに公開したいと思っています。

GaucheでmysqlのDBにINSERTするときに一意性制約違反をハンドリングする方法

Gaucheのmysqlのdbdを使って、Gauche状からmysqlのデータベースに対してINSERTするときのお話(メモ)です。

  • 「エラーにしたくない」だけであれば……

mysqlの機能を使って、”INSERT IGNORE …” というSQLとすれば一意性制約違反になるようなレコードは挿入されず捨てられエラーとはならない。

  • ちゃんと捕まえるためには……

例外を捕まえて、その中で一意性制約違反の場合の処理を入れます。具体的には以下のようなコードとする必要がある。

(guard (e
        ((<dbi-error> e)
         (if (= (condition-ref e 'error-code) 1062)
             unique-error ;; ここが一意性制約違反
             (raise e)))
        (else (raise e)))
       (dbi-execute hogehoge-sql))

1062 というのが、一意性制約違反のエラーコードとなる。(MySQL :: MySQL 4.1 リファレンスマニュアル :: 12.1 返されるエラー 参考)

# SQLSTATEを取得する方法って無いのかな???

参考リンク:
dbd.mysql | DBI | dev | Kahua Project

O_TRUNCを指定してopenしたファイルをcloseするとsyncすることがある

Linuxのローカルファイルシステムの話です。かなりハマっていたので記事として残しておきます。

既存ファイルがある状態でO_TRUNCを指定してopen(2)を呼び出し、適当に書き込んだあとclose(2)すると、非同期ながらWritebackされ物理IOが発生します。close時はfsyncせず、書き込まれたデータはDirty状態でページキャッシュに保存されるだけのはずなのだが(参考:Linux の close は fsync 相当を調べる – naoyaのはてなダイアリー)。。。何を言っているのかわからねーと思うので、状況を再現する手順は以下のとおり。

  • ファイルを適当に作っておく。

$ echo test > test.dat

  • 以下のようなソースファイルを作成し、コンパイルしておく
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
  int fd;
  char *p = malloc(10*1024*1024);

  fd = open("test.dat", O_TRUNC | O_CREAT | O_WRONLY, 0666 );
  write(fd, p, 10*1024*1024);
  close(fd);

  return 0;
}
  • 実行する前に、/proc/meminfo の Dirty・Writeback の値を確認する。
  • 実行する(ディスクランプ・iostatでディスクへの書き込みを確認する)
  • 実行後、/proc/meminfo の Dirty・Writeback の値を10秒間程度確認し、実行前に比べDirtyの値が10M増えたままであればsyncされていないが、Dirtyの値が変わらない、もしくは、Dirtyの値が増えた後減って実行前の状態に戻った(そのときwritebackの値が0より大きい値)、ようであれば writeback されていることになる。

※ vm_dirty*** 系のパラメタはこのタイミングでwritebackされないように適当に大きくしておく。

色々試してみたところファイルシステム依存で、ext3(data=writeback), xfs についてはclose(2)時にディスクへの書き込みが行なわれた。ext3(data=ordered), ext2, reiserfsについてはそのようにはならなかった。また、ext3(data=writeback), xfs でも既存ファイルが無い状態での O_TRUNC ではWritebackされず、Dirtyのまま残る。

ファイルシステム毎に試してみたところ以下のようになった。(たぶん合っていると思いますが、間違っていたら指摘ください)

ファイルシステム 既存ファイル無しでO_TRUNC 既存ファイル有りでO_TRUNC
ext2 close(2)のタイミングでデータはwritebackされず、Dirtyのままである 同左
ext3
(data=ordered)
close(2)のタイミングでデータはwritebackされず、Dirtyのままであり、メタデータ・データはcommit間隔にて同期される。 close(2)のタイミングでデータはwritebackされず、Dirtyのままであり、メタデータ・データはcommit間隔にて同期される。(しかし、同期された後もDirtyからは消えない?)
ext3
(data=writeback)
メタデータはcommit間隔にて同期される。close(2)のタイミングでデータはwritebackされず、Dirtyのままとなる。 close(2)時にwritebackされる(close(2)はsyncの復帰を待たず復帰)
reiserfs close(2)のタイミングでデータはwritebackされず、Dirtyのままである 同左
xfs close(2)のタイミングでデータはwritebackされず、Dirtyのままとなる。 close(2)時にwritebackされる(close(2)はsyncの復帰を待たず復帰)

今まで、ファイルへの書き込みはLinuxのページキャッシュ機構により、非同期でwritebackされると思っていたがこういうタイミングでもwritebackされるとは思っていなかった。お仕事でLinuxを使う場合は、close(2)のターンアラウンドタイムとディスクアクセスが性能見積りと異なる場合があるので要注意かなぁ。(こういう仕組みになっているのは、どうやらジャーナルとデータとの関係のようだが詳細が分かったらまた追記します)どうやらtruncateしてファイルサイズが0でファイルの実体がsyncされていない状態でクラッシュすると、ファイルが失われるため、それを防止するためにtruncate時は早めにsyncするようです。

参考までにSDカードにファイルシステムを作ってみて、上記プログラムをstrace上で動かしてみたときのシステムコールの時間を残しておきます。

既存ファイル有りでO_TRUNC:(単位はms)

ext2 ext3(data=writeback) ext3(data=ordered) reiserfs xfs
open 0.065 0.0925 0.086 0.134 0.184
write 53.775 90.2 227.090 65.586 37.911
close 0.027 17.874 0.026 0.047 8.159

(太字にした)ext3, xfs のclose時にwritebackの処理のため(?)に時間がかかるようです。また既存ファイルが無い状態での同じプログラムを動かした結果は以下のようになり、close時のwritebackが行なわれませんでした。

既存ファイル無しでO_TRUNC:(単位はms)

ext2 ext3(data=writeback) ext3(data=ordered) reiserfs xfs
open 0.0685 0.075 0.074 0.1 0.134
write 55.501 93.967 134.721 67.165 37.093
close 0.0255 0.028 0.024 0.046 0.0815

2011/1/4 追記:
ext3 のマウントモードがwritebackである旨を追加し、ordered の場合の動作追記。

pthread_t のポインタが示すもの(pthread_create 第一引数/pthread_self戻り値)

実装依存なので注意。今回の話は Linux (glibc) についてです。

pthread_create 第一引数や、pthread_self戻り値で使用される pthread_t 型であるがその値の意味について考えたことは無かった。

pthread_t 型をマニュアルなどを確認すると「スレッドの識別子」と記載されているが、システムコール(gettid)の返す値とは異なり、無意味な値だと考えていた。ところが、前回の調査(Linux の pthread のデフォルトスタックサイズについて « 余談ですが……)を行なったときに、あるところのポインタ値であることが分かった。

まず、pthread_t の値(ポインタ値)を確認しみる。値自体は gdb で “info threads” としたときにも出力されます。

(gdb) info threads

2 Thread 0xb7fedb90 (LWP 8398) 0xb7fff424 in __kernel_vsyscall ()

* 1 Thread 0xb7fee6c0 (LWP 8395) 0xb7fff424 in __kernel_vsyscall ()
(gdb)

スレッドの1がメインスレッド(main関数)で、スレッドの2がpthread_createで作成したスレッドということになる。どちらも値がかなり大きく、あまり意味のあるように思えない。

pthread_createの実装を確認してみた。

nptl/pthread_create.c

int
__pthread_create_2_1 (newthread, attr, start_routine, arg)
     pthread_t *newthread;
     const pthread_attr_t *attr;
     void *(*start_routine) (void *);
     void *arg;
{
  STACK_VARIABLES;

  const struct pthread_attr *iattr = (struct pthread_attr *) attr;
  if (iattr == NULL)
    /* Is this the best idea?  On NUMA machines this could mean
       accessing far-away memory.  */
    iattr = &default_attr;
  struct pthread *pd = NULL;
  int err = ALLOCATE_STACK (iattr, &pd);
//(中略)
  /* Pass the descriptor to the caller.  */
  *newthread = (pthread_t) pd;
  /* Start the thread.  */
  return create_thread (pd, iattr, STACK_VARIABLES_ARGS);
}

ALLOCATE_STACK というマクロの第二引数のポインタが設定されるようだ。ALLOCATE_STACK マクロは allocate_stack に展開される。その関数の中で以下のとおり実施している。

static int
allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
        ALLOCATE_STACK_PARMS)
{
  struct pthread *pd;
  size_t size;
  size_t pagesize_m1 = __getpagesize () - 1;
  void *stacktop;

  assert (attr != NULL);
  assert (powerof2 (pagesize_m1 + 1));
  assert (TCB_ALIGNMENT >= STACK_ALIGN);

  /* Get the stack size from the attribute if it is set.  Otherwise we
     use the default we determined at start time.  */
  size = attr->stacksize ?: __default_stacksize;
  /* Get memory for the stack.  */
  if (__builtin_expect (attr->flags & ATTR_FLAG_STACKADDR, 0))
    {
//スタックアドレスが設定済の場合(中略)
    }
  else
    {
      /* Allocate some anonymous memory.  If possible use the cache.  */
      size_t guardsize;
      size_t reqsize;
      void *mem;
      const int prot = (PROT_READ | PROT_WRITE
            | ((GL(dl_stack_flags) & PF_X) ? PROT_EXEC : 0));

// (中略)
      mem = mmap (NULL, size, prot,
              MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);

      /* Place the thread descriptor at the end of the stack.  */
      pd = (struct pthread *) ((char *) mem + size - coloring) - 1;
// (中略)
    }
// (中略)
  /* We place the thread descriptor at the end of the stack.  */
  *pdp = pd;
// (中略)
  return 0;
}

(スタックアドレスを明示的に設定した場合の処理は中略とした。)
注目すべきは、mmapした領域(スタック領域)のmmap領域サイズの最後尾辺りに、pd というポインタを設定して、それを引数の *pdp に設定している、という点である。

つまり、pthread_t の値は、自身のスレッドのスタックの底(近辺)のアドレスを意味している。ソース内のコメントもそれを意味した内容が記載してある。