2014年9月12日金曜日

pthreadでスレッドの中断と再開をする

LinuxやmacOSのpthreadってスレッドを中断・再開するAPIがありません。このようなAPIがないのに、なぜかLinux上で動くJavaにはスレッドの中断や再開があります。

どうやって実装しているのかなぁと思っていたのですが、調べても見るとシグナルを使っていることがわかりました。

やっとやりかたができることがわかったのでサンプルを作ってみました。

これでlinux,windows、cygwin、ios、macos、androidで動く、中断再開可能なスレッドが作れます。
さらにスレッドからUI表示系のAPIを呼ぶときにmutexロックをすればiosのperformSelectorInBackground:withObject: のようなものも作れます。

iosのフレームワークはこのperformセレクターやGlobalCentralDispatchなどいろいろ便利な機能がたくさんあるのですが、その中に高度なテクニックが隠蔽されています。
いつもどうやって実装しているのかたまに調べているのですが、iosのフレームってすごい。

mythread2.c
--------------
#include <stdio.h>
#include <stdlib.h>
#if defined(unix) || defined(__APPLE__)
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif

#include "mythread2.h"



#ifdef HAVE_PTHREAD_H
#define PTHREAD_CREATE(thread, attr, func, arg)                          \
(int)pthread_create((pthread_t *)(thread), (pthread_attr_t *)(attr), \
    (void * (*)(void *))(func), (void *)(arg))
#else
#ifdef _WIN32
#define BEGINTHREAD(src, stack, func, arg, flag, id)         \
(HANDLE)_beginthreadex((void *)(src), (unsigned)(stack), \
(unsigned (_stdcall *)(void *))(func), (void *)(arg),    \
(unsigned)(flag), (unsigned *)(id))
#endif
#endif



/* yomei fixed: translate __stdcall to __cdecl  */
#ifdef _WIN32
static unsigned __stdcall internal_start_thread( void * vp)
{
mythread2_t *t;
if(vp==NULL)return 0;

t=(mythread2_t *)vp;
return (unsigned)(((void * (*)(void *))(t->func))(t->arg));
}
#endif



#if defined(unix) || defined(__APPLE__)
void suspend_signal_handler( int sig ) {
    sigset_t signal_set;
    int no = 0;
 
    sigemptyset( &signal_set );
    sigaddset( &signal_set, SIGUSR2 );
    sigwait( &signal_set, &no );

    return;
}

void resume_signal_handler( int sig ) {
    return;
}
#endif

static void mythread2_init()
{
#if defined(unix) || defined(__APPLE__)
    struct sigaction    sigusr1;
    struct sigaction    sigusr2;
 #endif

static int flag_init=0;
if(flag_init)return;
flag_init=1;
//printf("mythread2_init\n");

#if defined(unix) || defined(__APPLE__)
    sigusr1.sa_flags = 0;
    sigusr1.sa_handler = suspend_signal_handler;
    sigemptyset( &sigusr1.sa_mask );

    sigusr2.sa_flags = 0;
    sigusr2.sa_handler = resume_signal_handler;
    sigemptyset( &sigusr2.sa_mask );

    sigaction( SIGUSR1, &sigusr1, 0 );
    sigaction( SIGUSR2, &sigusr2, 0 );
#endif

}


mythread2_t*
mythread2_create(void* func, void* arg)
{
mythread2_t *t;
mythread2_init();
t = (mythread2_t *)malloc(sizeof(mythread2_t));
if (!t)
{
goto fail0;
}
t->issuspend=0;
t->func = func;
t->arg = arg;

#ifdef HAVE_PTHREAD_H
if (PTHREAD_CREATE(&t->hthread, 0, func, arg))
{
goto fail1;
}
#else
#ifdef _WIN32
{
int id;
HANDLE hnd;
/* yomei fixed: this thread function must be a standard call.  */
hnd = BEGINTHREAD(0, 0, internal_start_thread, t, 0, &id);
if (!hnd)
{
goto fail1;
}
t->hthread = hnd;
}
#endif
#endif
return t;

fail1:
if (t)
{
free(t);
t = NULL;
}
fail0:
return t;
}



void
mythread2_join(mythread2_t* t)
{
mythread2_init();
if (!t)
{
goto fail;
}
#ifdef HAVE_PTHREAD_H
pthread_join(t->hthread, NULL);
free(t);
#else
#ifdef _WIN32
WaitForSingleObject(t->hthread, INFINITE);
CloseHandle(t->hthread);
free(t);
#endif
#endif

fail:
return;
}


void mythread2_sleep(int s)
{
#if defined(unix) || defined(__APPLE__)
sleep(s);
#endif
#ifdef _WIN32
Sleep(s*1000);
#endif
}


void
mythread2_suspend(mythread2_t* t)
{
if(t==NULL)return;
if(t->issuspend)return;
t->issuspend=1;

#if defined(unix) || defined(__APPLE__)
    pthread_kill( t->hthread, SIGUSR1 );
#endif
#if defined(_WIN32)
SuspendThread(t->hthread);
#endif
}

void
mythread2_resume(mythread2_t* t)
{
if(t==NULL)return;
if(t->issuspend==0)return;
t->issuspend=0;
#if defined(unix) || defined(__APPLE__)
    pthread_kill( t->hthread, SIGUSR2 );
#endif
#if defined(_WIN32)
ResumeThread(t->hthread);
#endif
}
--------------


mythread2.h
--------------
#ifndef __MYTHREAD2_H__
#define __MYTHREAD2_H__

#define __MYTHREAD2_H__VER "20140911"


#if defined(unix) || defined(__APPLE__)
/* define if use pthread */
#define HAVE_PTHREAD_H
#endif

#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#include <string.h>
#else
#ifdef _WIN32
#include <windows.h>
#include <winbase.h>
#include <process.h>
#endif
#endif


#ifdef HAVE_PTHREAD_H
typedef pthread_t hthread_t;
#else
#ifdef _WIN32
typedef HANDLE hthread_t;
#endif
#endif



typedef struct _thread
{
hthread_t hthread;
void* func;
void* arg;
int issuspend;
} mythread2_t;


#ifdef __cplusplus
extern "C"
{
#endif

mythread2_t* mythread2_create(void* func, void *arg);
void mythread2_join(mythread2_t* t);
void mythread2_suspend(mythread2_t* t);
void mythread2_resume(mythread2_t* t);

void mythread2_sleep(int s);

#ifdef __cplusplus
}
#endif

#endif /* __MYTHREAD_H__ */

--------------




0 件のコメント:

コメントを投稿