Hatena::ブログ(Diary)

kazuhoのメモ置き場

2011-07-28

[] 僕が shared_ptr よりも retain() / release() 方式が好きだと思う理由

それは、shared_ptr のようなラッパーよりも、オブジェクト自体が参照カウンタをもっているほうがコードが書きやすいから。

たとえば、継承したクラスでオブジェクトの参照カウントをいじることは、shared_ptr では難しい。

shared_ptr を使った場合:

class Base {
public:
   virtual void foo() = 0;
};

class Derived : public Base {
public:
  virtual void foo() {
    // ここで自分自身への参照カウンタをインクリメントしたいけどできない…
    gManager_.register(this);
};

// 呼び出し側
obj = shared_ptr<Base>(new Derived());
obj->foo();

オブジェクト自体が参照カウンタをもっていれば、このような問題は発生しない。そして、コード量が増えているように見えるけど、参照カウントを管理するための mix-in クラスと CComPtr みたいなラッパーを用意すれば、コーディング量は実質ゼロにできる。

class Base {
  size_t refcnt_;
public:
  Base() : refcnt_(1) {}
  void retain() { refcnt_++; }
  void release() { if (--refcnt_ == 0) delete this; }
  virtual void foo() = 0;
};

class Derived : public Base {
public:
  virtual void foo() {
    retain(); // incr refcount to myself and register
    gManager_.register(this);
  }
};

// 呼び出し側
obj = new Derived();
derived->foo();

だから、「データ型」を扱う場合は shared_ptr でもいいけど、高レベルな処理を記述する「オブジェクト」については retain() / release() でいいんじゃないかと思ってる。

2011-07-06

[メモ] TCP/IPプログラミングにおけるエラー処理 (RSTパケットの飛ばし方と検出の仕方)

SO_LINGER の秒数を 0 にしてソケットを閉じればいい。Perl で書くなら以下のような感じ。

setsockopt($sock, SOL_SOCKET, SO_LINGER, pack("II", 1, 0)) or die $!;
$sock->close()

一方で RST によって切断 (=異常切断された) かどうかはソケットからの読み込みが ECONNRESET エラーを返すかどうかで判断できる(正常切断なら 0 (==EOF) が返る)。

2011-05-20

nicesort ってのを書いてみた

sleep sort とか環境にやさしすぎて21世紀には不向きだと思うの。なので nicesort なるものを作ってみた。

#! /bin/bash

function f() {
  nice -n "$1" perl -we 'for (1..1000000) {}'
  echo "$1"
}
while [ -n "$1" ] ; do
  f "$1" &
  shift
done
wait

こんな感じで動きます。注意点としては、マルチコアだとうまく動かないので taskset 使いましょう (linux の場合) ってのと、0-20の範囲でしかそーとできないよってあたりかしら.

$ taskset 1 ./nicesort.sh 1 9 6 4
1
4
6
9

ちなみに、nice 値が1増えると実行時間は1.25倍になる。これは試験に出るよ!

参考: 革命の日々! CFSのnice値について

Cによるsleep sortの実装がないのもどうかと思ったので書いてみた

Sleep sortの各言語での実装まとめ | Yuyak Blog を見て、Cによるsleep sortの実装がないのもどうかと思ったので書いてみた。シンプルですな。

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char** argv)
{
  int values[] = { 1, 9, 5, 3 };
  int i;
  
  for (i = 0; i < sizeof(values) / sizeof(values[0]); i++)
    switch (fork()) {
    case -1:
      assert(0);
      break;
    case 0:
      /* child process */
      sleep(values[i]);
      printf("%d\n", values[i]);
      exit(0);
      break;
    default:
      break;
    }
  
  for (i = 0; i < sizeof(values) / sizeof(values[0]); i++)
    while (wait(NULL) <= 0)
      assert(errno == EINTR);
  
  return 0;
}

C++0x で sleep sort 書いてみた (was そういえば C++0x での sleep sort ってこんな感じでいいのかな)

GCC 4.6.0 をがんばって OSX に入れたのに std::thread 使えなくて泣きながら修正。こんな感じすね。

#include <cstdio>
#include <vector>
extern "C" {
#include <pthread.h>
#include <unistd.h>
}

int main(int, char**)
{
  int values[] = { 1, 9, 3, 6 };
  std::vector<pthread_t> threads;
  
  for (auto& v : values) {
    pthread_t tid;
    pthread_create(&tid, NULL, 
		   [](void* _v) -> void* {
		     int v = *reinterpret_cast<int*>(_v);
		     sleep(v);
		     printf("%d\n", v);
		     return NULL;
		   },
		   &v);
    threads.push_back(tid);
  }
  for (auto tid : threads)
    pthread_join(tid, NULL);
  
  return 0;
}

std::thread が使えればこうなる。

int main(int, char**)
{
  int values[] = { 1, 9, 3, 6 };
  std::vector<std::thread> threads;
  
  for (auto v : values)
    threads.emplace_back([v]() {
                           sleep(v);
                           printf("%d\n", v);
	                 });
  for (auto& thread : threads)
    thread.join();
  
  return 0;
}

コンパイラないので試せないわ。

Re 常識を覆すソートアルゴリズム!その名も"sleep sort"!

常識を覆すソートアルゴリズム!その名も"sleep sort"! - Islands in the byte streamを読んで、自分が書くとしたらこんな感じかなーと思った。多重化して select 使う必要ないよねということで。

use Time::HiRes qw(sleep);

sub sleep_sort {
    # create pipe
    pipe(my $rfh, my $wfh)
        or die "pipe failed: $!";
    # spawn the processes
    my @pids;
    while (@_) {
        my $value = shift;
        my $pid = fork;
        die "fork failed: $!"
            unless defined $pid;
        if ($pid == 0) {
            # child process
            $| = 1;
            sleep $value / 10;
            print $wfh "$value\n";
            exit 0;
        }
        push @pids, $pid;
    }
    # close write handle and swallow all input
    close $wfh;
    my @values = <$rfh>;
    # get rid of the zombie processes
    for my $pid (@pids) {
        while (waitpid($pid, 0) != $pid) {
            if ($! == Errno::EINTR) {
                # continue
            } elsif ($! == Errno::ECHILD) {
                # somehow got reaped (maybe $SIG{CHLD} is 'IGNORE')
                last;
            } else {
                die "unexpected response from waitpid: $!";
            }
        }
    }
    # remove \n and return the result
    chomp @values;
    @values;
}

まあ、wait するくらいなら print する必要もないと思うけどw

sub sleep_sort {
    my %pid_value = map {
        +(fork || do {
            sleep $_ / 10;
            exit;
        }) => $_
    } @_;
    my @values;
    while (%pid_value) {
        if ((my $pid = wait) != -1) {
            push @values, delete $pid_value{$pid};
        }
    }
    @values;
}

2011-05-18

jailbreakしてるAndroid端末でプロセスを殺すためのワンライナー

% adb shell sh -c `adb shell ps | fgrep PROCNAME | awk '{print "\"echo kill -9 ", $2, "| su\""}'`

2011-03-03

ファイルに書かれたら irc とかネットに通知するとかそういうデーモンを作るときは mkfifo するといいんじゃないか

デーモン側をこんな感じで書きます。

use Errno qw(EEXIST);
use Fcntl qw(S_IFIFO);
use POSIX qw(mkfifo);

my $FIFO_NAME = "/tmp/my_messenger.fifo";

if (mkfifo($FIFO_NAME, 0666)) {
    # ok
} elsif ($! == EEXIST) {
    die "$FIFO_NAME is not a fifo!"
        unless +(stat($FIFO_NAME) & !S_IFIFO) != 0;
} else {
    die "failed to create fifo:$FIFO_NAME, $!";
}

while (1) {
    open my $fh, '<', $FIFO_NAME
        or die "failed to open fifo:$FIFO_NAME, $!";
    while (my $line = <$fh>) {
        send_message_to_somewhere($line); # どっかに通知する関数
    }
}

メッセージを送る側は、普通にリダイレクトするだけで OK

% echo hello world >> /tmp/my_messenger.fifo

デーモンのコードの中で FIFO かどうか毎回チェックしてるので万が一ファイル上書いちゃってもデーモン再起動すればOK。というわけで、FIFO いいよという話でした。もうちょっとサンプルコード整理しといた

2011/3/4追記: 通常ファイルを使わずに mkfifo する理由は、通常ファイルだと定期的にトランケートが必要になるけど、そのタイミングで書き込まれたらどうするのという問題(競合)があるから。なのでたとえば crontab で5分かかる処理の結果を /somescript >> /tmp/to_irc みたくして通知しようと思うと通常ファイル + inotify とかじゃダメで mkfifo 一択になる。一方で mkfifo を使う場合は、バッファサイズが 4KB (linuxの場合) なので、通知を受け取って転送する側のプログラムは、できるだけ早くバッファからの読み込みを行う必要がある(そうしないと最悪書き込み側がブロックする)