UNIXソケットをポーリングするために単一のスレッドをディスパッチしようとすると、奇妙な動作に気づきます。osx 10.6.8でコードをテストすると、コードがクラッシュし、複数のスレッドがコードを実行しているのがわかります。奇妙なことに、osx 10.7.5では、クラッシュすると、1つのスレッドでしか実行されないことがわかります。
私のAppDelegateでは、次のようにスレッドをディスパッチします(1回):
[pp performSelectorInBackground:@selector(runloop) withObject:nil];
ppはMyIPCクラスのインスタンスです。
私のIPCクラスでは、runloopはいくつかのCコードのラッパーです。
@implementation IPC
-(void) runloop
{
ipc_busy = 0;
unixsocket_runloop();
}
runloopは、(ランタイムごとに)単一の接続を待機することを目的としています。次に、ハンドラーは、プログラムが終了するまでパケットを待機します。Cコードは次のようになります-
/**
* Waits for a connection and dispatches the socket handler
* No need to fork since we will only ever have a single connection per program launch
*/
int unixsocket_runloop()
{
int connection_fd;
socklen_t address_length;
while((connection_fd = accept(socket_fd,
(struct sockaddr *) &address,
&address_length)) > -1)
{
return unixsocket_handler(connection_fd);
}
return 0; // for posterity
}
これは接続ハンドラーです-
/**
* Handles the connection from the client
* loops infinitely and reads from the socket when there is a message
* Stores message into a ring buffer of buffers
*/
int unixsocket_handler(int connection_fd)
{
size_t nbytes;
char readbuffer[USBUFSIZE];
bzero(readbuffer,USBUFSIZE);
while((nbytes = read(connection_fd, readbuffer, USBUFSIZE)))
{
// store in buffer
bufPut(readbuffer,nbytes);
bzero(readbuffer,USBUFSIZE);
}
close(connection_fd);
return 0;
}
スタックトレースでは、このunixsocket_runloopが2つのスレッドで発生していることがわかります。これがクラッシュの根本的な原因であるとは思いませんが、対処したい予期しない動作です。
私が独自のソケットクラスを使用した唯一の理由は、オープンソースコミュニティで見られるオプションの多くがTCPにハードワイヤードされていることでした。特に魅力的なcocoaAsyncSocketクラス。