2014年12月17日水曜日

入社二年目のプログラムをなおしてみた。

日本の最高学府の大学院を卒業後、とある会社に入社した二年目の社員が、なぜプログラムが落ちるのかわからなくて悩んでいる。しかももう一ヶ月も。

やっぱ、企業ってお金稼ぐことも大切ですが、雇用の安定とか社会的な責任を果たすことも大事ですからね。

わかりやすくするためにコードを簡単にするとこんな感じ。

#include <stdio.h>
void func(int* a){
*a=12345;
}
int main(int argc,char* argv[])
{
int a;
func((int*)a);
printf("a=%d\n",(int)a);
return 0;
}

うーん、これはとっても難しい問題だね。
なのでどうすれば動くのか一緒に考えてみました。


案その1:

グローバルなint*をオペレーターを定義できないかなぁ

operator int*(int a){
 return &a;
}

文法上キャストのグローバルオペレータは定義できなかった気が・・・。



案その2:

変数aに自分自身のアドレスを入れる。

void func(int* a){
*a=12345;
}
int main(int argc,char* argv[])
{
int a=(int)&a;
func((int*)a);
printf("a=%d\n",(int)a);
return 0;
}

一応androidだと動くけれど、iphone5Sとiphone6だとたぶん動かない。


案その3:

変数aをintptr_tにする。

void func(int* a){
*a=12345;
}
int main(int argc,char* argv[])
{
intptr_t a=(intptr_t)&a;
func((int*)a);
printf("a=%d\n",(int)a);
return 0;
}

これで大体どんな環境でも動くじゃん。
64ビットのBIGエンディアンとかは動かない気もする。
ということはstrict aliasingルールに反する気も。
やっぱ、どうしてもあそこに&をつけたいけれど、女性とあの行だけは決して手をだしていけない。&をつけてはいけないにルール変更。


案その4:

void func(intptr_t* a){
*a=12345;
}
int main(int argc,char* argv[])
{
intptr_t a=(intptr_t)&a;
func((intptr_t*)a);
printf("a=%d\n",(int)a);
return 0;
}


やったーやっとこれできちんと動く。
やっぱり、どんな環境でもきちんと動くってとっても難しいです。

2014年12月15日月曜日

USB2BTでMac-miniとUSBキーボードを無線接続でつないでみた。

以前この日記で、CerevoのEnebrickを使ってUSBキーボードをBluetoothキーボードに変換してMac-miniで使う記事を書いたのですが、この製品、すぐにハングって使い物にならないので、別の製品を使うことにしました。




これが今回使うUSB2BT
http://bit-trade-one.co.jp/product/assemblydisk/adu2b01/
これを使うといろいろなUSBデバイスをBluetoothに変換できます。

Bluetoothって素人が開発すると結構OSによって繋がったり繋がらなかったりということが起こるのですが、この製品の開発者は大変優秀な方のようで、全く問題なくいろいろなOSで問題なくつながります。

これからはこれを使おうっと。



秋月電子で売っている「RN−42使用 Bluetooth無線モジュール評価キット」でシリアルポートを無線化してみた

マイコンとかのプログラム開発をするときに、PCとマイコンボードをだいたいUSBシリアル変換器でつなぎます。
この部分にケーブルがあるととても作業しづらいので、秋月電子で売っている「RN−42使用 Bluetooth無線モジュール評価キット」でシリアルポートを無線化してみました。

http://akizukidenshi.com/catalog/g/gK-07378/

といってもこのキットを買ってきて、最初の一回だけ評価モジュールの初期設定を行うだけです。
この評価モジュールとPCとをUSBケーブルでつないで、TeraTermなどからBluetoothの認証モードの設定を変更します。
シリアルから「$$$」と入力して設定モードに移行して、その次にコマンド「sa,2」を入力して認証モードを簡単認証モードに変更。
これだけでPCとマイコンが無線でシリアル通信ができます。
シリアルの信号線は評価モジュールのJ2コネクタの6番ピンと7番品に出ます。
あとはこのピンとグランドをマイコンのシリアルピンとグランドにつなぐだけ。

超簡単なんですが、いまのところ、Bluetoothのシリアルポートプロファイルにデフォルトで対応しているOSがWindows8.1以降とMacOSXしかないので、Windows7ユーザの人は接続できなくてはまります。

あと注意点としてはネットで評価モジュールのCTSとRTSを繋いだ方が良いと書いてあります。僕も以下のように改良してCTSとRTSをとりあえずつないでみました。





僕はこの試験をするためだけにWindows8.1を入れました。
一番高かったのがWindows8.1のライセンス料とは。。。


2014年12月11日木曜日

androidのbinderをWindows、Linux、MacOSXで実装する

毎回マニアックなプログラムのネタを書いていて、こんな日記を参考にする人がいるのかなぁと思っていたのですが、結構いるんですね。
さて、今日はプロセス間通信の移植の話。

androidのIPC(プロセス間通信)は/dev/bindrというバインダーデバイスを介して行われます。
androidではC/C++で実装されているネイティブサービスがこのバインダーを通してJavaレイヤと通信を行うのですが、このドライバー部分がネックとなりネイティブサービスをPC-Linux、WindowsやMacOSXで動かすことができません。
実機でしかネイティブサービスを開発できないのは大変不便なので、このbinderデバイス部分とC++の関連クラス一式をWindowsでエミュレーションして動くようにしてみました。
たぶんLinuxやMacOSXでも動きます。
dev/ashmemや複数のサービスを登録するなどはまだまだエミュレーションできませんが、数値や文字列だけを扱う関数ならば完全にエミュレーションで動かすことができます。
また、単独プロセスでサービスとクライアントを開発できるので、XcodeやVisualStudioなどの高度なIDEで開発を行うことができてとっても便利。

移植していて気付いたのですが、androidオリジナルのバインダーライブラリの設計の完成度の高さに感動します。
完全ロックフリーでマルチコアで高速動作するようになっているんですね。
どのような戦略で不完全なソースを高速化すれば良いのかとても勉強させられるコードです。

ソースはこちら
https://drive.google.com/file/d/0B5M9qMMg3tfQREJZekY0NDNNb00/view?usp=sharing

2014年12月9日火曜日

WindowsでUnixドメインソケットを使う

LinuxのプログラムをWindowsで動くように移植するとき、スレッドやミューテックなどは同等のAPIがWindowsにあるため、どうにかなるのですが、Unixドメインソケットの部分だけはなかなか同等のAPIがありません。

そこでみんなどうしているのかいろいろ調べてみました。

まずGoogle。
LocalBiChannnel.cppというのがAndroidのソースコードの中にあります。
たぶんローカルバイナリーチャンネルの略なのでしょう。
このファイルは1:1でローカルソケットを使うときはLinux、MacOSX、WindowsすべてのOSで動くので大変便利。Androidでは大体これを使っています。このやり方とても頭よいです。さすが。


それ以外の人はCygwinでUnixドメインソケットが使えるのでCygwinのローカルを使っているようです。

Cygwinを使わないで、Windowsで1:nのUnixドメインソケットを使いたい場合、よいライブラリがないので、Googleのコードを参考に変更して作ってみました。


---------------------------------------
localsocket_.c
---------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "localsocket_.h"

#if defined(_WIN32)
# include <windows.h>
#endif

#if defined(unix) || defined(__APPLE__)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#endif



#ifndef SUN_LEN
/*
 * Our current set of ARM header files don't define this.
 */
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
                      + strlen ((ptr)->sun_path))
#endif


static const unsigned long kInvalidHandle = (unsigned long) -1;
static const int kPipeSize = 4096;

/*
 * Destructor.  If we're the server side, we may need to clean up after
 * ourselves.
 */

void localsocket_close(localsocket_t* ls)
{
#ifdef _WIN32
if(ls==NULL)return;
    if (ls->mHandle != kInvalidHandle){
        FlushFileBuffers((HANDLE)(ls->mHandle));
        CloseHandle((HANDLE)(ls->mHandle));
}
    if (ls->mHandle2 != kInvalidHandle){
        FlushFileBuffers((HANDLE)(ls->mHandle2));
        CloseHandle((HANDLE)(ls->mHandle2));
}
free(ls);
#endif
#if defined(unix) || defined(__APPLE__)
if(ls==NULL)return;
    if (ls->mHandle != kInvalidHandle)
        close((int) (ls->mHandle));
    if (ls->mIsListener) {
        (void) unlink(ls->mFileName);
    }
free(ls);
#endif
}

void makeFilename(char* fileName, const char* name)
{
#ifdef _WIN32
static const char* kBasePath = "\\\\.\\pipe\\android-";
    strcpy(fileName, kBasePath);
    strcat(fileName, name);

#endif
#if defined(unix) || defined(__APPLE__)
    static const char* kBasePath = "/tmp/android-";
    strcpy(fileName, kBasePath);
    strcat(fileName, name);
#endif
}

static localsocket_t* alloc()
{
localsocket_t* ret;
ret=(localsocket_t*)malloc(sizeof(localsocket_t));
if(ret==NULL)return ret;
memset(ret,0,sizeof(localsocket_t));
ret->mHandle=kInvalidHandle;
ret->mHandle2=kInvalidHandle;
ret->mPort=1;

return ret;
}

localsocket_t* localsocket_create(const char* name)
{
#ifdef _WIN32
localsocket_t* ret;
if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

makeFilename(ret->mFileName,name);
return ret;
#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    struct stat sb;
    int result = 0;
    int sock = -1;
    int cc;
    struct sockaddr_un addr;

if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

    makeFilename(ret->mFileName,name);

    cc = stat(ret->mFileName, &sb);
    if (cc < 0) {
        if (errno != ENOENT) {
free(ret);
return NULL;
        }
    } else {
        /* don't touch it if it's not a socket */
        if (!(S_ISSOCK(sb.st_mode))) {
free(ret);
return NULL;
   }

       /* remove the cruft */
        if (unlink(ret->mFileName) < 0) {
free(ret);
return NULL;
        }
    }

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
free(ret);
return NULL;
    }

    /* bind the socket; this creates the file on disk */
    strcpy(addr.sun_path, ret->mFileName);    // max 108 bytes
    addr.sun_family = AF_UNIX;
    cc = bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    if (cc < 0) {
close(sock);
free(ret);
return NULL;
    }

    ret->mHandle = (unsigned long) sock;
    ret->mIsListener = 1;

return ret;

#endif
}

localsocket_t* localsocket_attach(const char* name)
{
#ifdef _WIN32
localsocket_t* ret;
HANDLE handle;
DWORD bytesRead;
int port;
char buf[16];
int ok;

if(name==NULL || strlen(name)>100)return NULL;
ret=alloc();
if(ret==NULL)return ret;

//
// open common pipe to fix port num
//

makeFilename(ret->mFileName,name);
do{
ok=WaitNamedPipe(ret->mFileName,1000*5);
if(ok==0){
// No calling CreateNamedPipe() API.
free(ret);
return NULL;
}
// If other thread called CreateFile() API, Windows returned INVALID_HANDLE_VALUE.
handle= CreateFile(
                ret->mFileName,                      // filename
                GENERIC_READ | GENERIC_WRITE,   // access
                0,                              // no sharing
                NULL,                           // security
                OPEN_EXISTING,                  // don't create
                0,                              // attributes
                NULL);                          // template
}while(handle == INVALID_HANDLE_VALUE);

buf[0]=0;
bytesRead=0;
ReadFile(handle, buf, 1, &bytesRead,NULL);
//printf("read=%d\n",bytesRead);
port=buf[0]&255;

CloseHandle(handle);
sprintf(buf,"-%d",port);

//
// open port 
//

strcat(ret->mFileName,buf);
//printf("port=%d   name=%s\n",port,ret->mFileName);
ok=WaitNamedPipe(ret->mFileName,1000*5);
if(ok==0){
// 
free(ret);
return NULL;
}
handle= CreateFile(
                ret->mFileName,                      // filename
                GENERIC_READ | GENERIC_WRITE,   // access
                0,                              // no sharing
                NULL,                           // security
                OPEN_EXISTING,                  // don't create
                0,                              // attributes
                NULL);                          // template
    if (handle == INVALID_HANDLE_VALUE) {
free(ret);
        return NULL;
    }
ret->mHandle=(unsigned long)handle;
return ret;
#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    int result = 0;
    int sock = -1;
    int cc;
    struct sockaddr_un addr;

if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

    makeFilename(ret->mFileName,name);

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
free(ret);
return NULL;
    }

    /* connect to socket; fails if file doesn't exist */
    strcpy(addr.sun_path, ret->mFileName); 
// max 108 bytes
    addr.sun_family = AF_UNIX;
    cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    if (cc < 0) {
close(sock);
free(ret);
return NULL;
    }

ret->mHandle=sock;
    ret->mIsListener = 0;

    return ret;
#endif
}

localsocket_t* localsocket_listen(localsocket_t* ls)
{
return localsocket_listen_timeout(ls,0);
}

localsocket_t* localsocket_listen_timeout(localsocket_t* ls,int timeout)
{
#ifdef _WIN32
localsocket_t* ret;
    HANDLE hPipe;
    BOOL connected;
OVERLAPPED ol ={0};
char port[16];
DWORD bytesWritten;

if(ls==NULL)return NULL;

ret=alloc();
if(ret==NULL)return ret;

//
// open common port
//
if(ls->mHandle==kInvalidHandle){
hPipe = CreateNamedPipe(
                    ls->mFileName,              // unique pipe name
                    PIPE_ACCESS_DUPLEX      // open mode
| (timeout?FILE_FLAG_OVERLAPPED:0)
                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
                    0,                      // pipe mode (byte, blocking)
                    3,                      // max instances
                    kPipeSize,              // output buffer
                    kPipeSize,              // input buffer
                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
                    NULL);                  // security

   if (hPipe == 0) {
free(ret);
return NULL;
}
ls->mHandle=(unsigned long)hPipe;
}
//
// open first port
//
if(ls->mHandle2==kInvalidHandle){
int pp;
pp=ls->mPort & 255;
strcpy(ls->mFileName2,ls->mFileName);
sprintf(port,"-%d",pp);
strcat(ls->mFileName2,port);

//printf("port=%d  name=%s  name=%s\n",pp,ls->mFileName,ls->mFileName2);

hPipe = CreateNamedPipe(
                    ls->mFileName2,              // unique pipe name
                    PIPE_ACCESS_DUPLEX      // open mode
                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
                    0,                      // pipe mode (byte, blocking)
                    3,                      // max instances
                    kPipeSize,              // output buffer
                    kPipeSize,              // input buffer
                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
                    NULL);                  // security

   if (hPipe == 0) {
free(ret);
return NULL;
}
ls->mHandle2=(unsigned long)hPipe;
//printf("handle=%p\n",hPipe);
}

//
// connect
//
hPipe=(HANDLE)(ls->mHandle);

if(timeout){
connected=0;
ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
connected=ConnectNamedPipe(hPipe, &ol);
if(connected==0){
switch(GetLastError()){
case ERROR_PIPE_CONNECTED:
connected=TRUE;
break;
case ERROR_IO_PENDING:
if(WaitForSingleObject(ol.hEvent,timeout)==WAIT_OBJECT_0){
DWORD ig;
connected=GetOverlappedResult(hPipe,&ol,&ig,FALSE);
}else{
CancelIo(hPipe);
}
break;

}
}
CloseHandle(ol.hEvent);
}else{
   connected = ConnectNamedPipe(hPipe, NULL) ?
   TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
}
    if (connected==FALSE) {
free(ret);
return NULL;
}
//printf("connect step2\n");

// write port no.
port[0]=ls->mPort;
ls->mPort++;
WriteFile((HANDLE) (ls->mHandle), port, 1, &bytesWritten, NULL);
FlushFileBuffers((HANDLE) (ls->mHandle));
DisconnectNamedPipe(hPipe);

ConnectNamedPipe((HANDLE) (ls->mHandle2), NULL);

ret->mHandle=(unsigned long)(ls->mHandle2);
ls->mHandle2=(unsigned long)kInvalidHandle;

return ret;


#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    int result = 0;
    struct sockaddr_un from;
    socklen_t fromlen;

    int sock, lsock;
    int cc;
fd_set readfds;
struct timeval tv;
int n;

if(ls==NULL)return NULL;

ret=(localsocket_t*)malloc(sizeof(localsocket_t));
if(ret==NULL)return ret;

lsock = (int) (ls->mHandle);
if(ls->mIsListened==0){
   cc = listen(lsock, 5);
if (cc < 0) {
free(ret);
return NULL;
}
ls->mIsListened=1;
}


if(timeout){
FD_ZERO(&readfds);
FD_SET(lsock, &readfds);

memset(&tv,0,sizeof(tv));
tv.tv_sec=timeout/1000;
tv.tv_usec=(timeout%1000)*1000;

n = select(FD_SETSIZE, &readfds, NULL, NULL, &tv);
if(n==0){
free(ret);
return NULL;
}
}
    fromlen = sizeof(from);     // not SUN_LEN()
    sock = accept(lsock, (struct sockaddr*) &from, &fromlen);
    if (sock < 0) {
free(ret);
return NULL;
    }

ret->mHandle=sock;
ret->mIsListener=0;
    return ret;
#endif
}


int localsocket_read(localsocket_t* ls,void* buf, int count)
{
#ifdef _WIN32
    DWORD totalBytesAvail = count;
    DWORD bytesRead;

if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;

if (!ReadFile((HANDLE) (ls->mHandle), buf, totalBytesAvail, &bytesRead,
            NULL))
    {
        DWORD err = GetLastError();
        if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
            return 0;
        return -1;
    }
    return (int) bytesRead;
#endif
#if defined(unix) || defined(__APPLE__)
    int cc;
if(ls==NULL)return -1;
    cc = read((int)(ls->mHandle), buf, count);
    if (cc < 0 && errno == EAGAIN)
        return 0;
    return cc;
#endif
}
int localsocket_write(localsocket_t* ls,const void* buf, int count)
{
#ifdef _WIN32
    DWORD bytesWritten;
if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;

    if (!WriteFile((HANDLE) (ls->mHandle), buf, count, &bytesWritten, NULL)) {
        return -1;
    }
    return (int) bytesWritten;
#endif
#if defined(unix) || defined(__APPLE__)
    int cc;
if(ls==NULL)return -1;
    cc = write((int)(ls->mHandle), buf, count);
    if (cc < 0 && errno == EAGAIN)
        return 0;
    return cc;

#endif
}
int localsocket_readReady(localsocket_t* ls)
{
#ifdef _WIN32
    DWORD totalBytesAvail;
if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;


    if (!PeekNamedPipe((HANDLE) (ls->mHandle), NULL, 0, NULL,
            &totalBytesAvail, NULL))
    {
        return 1;
    }

    return (totalBytesAvail != 0);
#endif
#if defined(unix) || defined(__APPLE__)
    errno = 0;
    fd_set readfds;
    struct timeval tv = { 0, 0 };
    int cc;

if(ls==NULL)return -1;

FD_ZERO(&readfds);
    FD_SET((int)(ls->mHandle), &readfds);

    cc = select((int)(ls->mHandle)+1, &readfds, NULL, NULL, &tv);
    if (cc < 0) {
        return 1;
    } else if (cc == 0) {
        /* timed out, nothing available */
        return 0;
    } else if (cc == 1) {
        /* our fd is ready */
        return 1;
    } else {
        return 1;
    }
#endif
}


---------------------------------------
localsocket_.h
---------------------------------------
#ifndef __LOCALSOCKET_H
#define __LOCALSOCKET_H

typedef struct st_local_socket{
    char       mFileName[128];
    char       mFileName2[128];
    int        mIsListener;
    unsigned long mHandle;
    unsigned long mHandle2;
int        mIsListened;
unsigned char mPort;
} localsocket_t;


#ifdef __cplusplus
extern "C" {
#endif

localsocket_t* localsocket_create(const char* name);
localsocket_t* localsocket_listen(localsocket_t* ls);
localsocket_t* localsocket_listen_timeout(localsocket_t* ls,int to);
localsocket_t* localsocket_attach(const char* name);

void localsocket_close(localsocket_t* ls);

int localsocket_read(localsocket_t* ls,void* buf, int count);
int localsocket_write(localsocket_t* ls,const void* buf, int count);
int localsocket_readReady(localsocket_t* ls);


#ifdef __cplusplus
}
#endif

#endif