这个文件内是针对windows平台的线程实现。在VisualStudio内可以看到,socket_openssl.c、socket_unix.c、thread_fork.c和thread_pthread.c这四个文件在abyss工程生成时不会生成代码。这很好理解,尤其是thread_fork.c和thread_pthread.c这两个文件,再加上thread_windows.c文件,就可以理解成在windows平台下编译abyss只使用windows平台下线程相关的特定代码。
struct abyss_thread { HANDLE handle; void * userHandle; TThreadProc * func; TThreadDoneFn * threadDone; };
typedef struct abyss_thread TThread;
TThread是库内部针对线程的封装。ThreadCreate函数的作用就是创建一个TThread结构体变量,并启动一个线程。TThread结构体变量将提供给线程主函数。用户代码想向线程提供任何自定义的数据可以将其放置在userHandle中。用户代码中的线程主函数必须是TThreadProc类型。同时,用户代码还可以提供一个TThreadDoneFn类型的线程完成处理函数。useSigchld参数可能在windows平台下无意义,所以没有使用它。注意到线程被创建后不会立即启动,因为使用了CREATE_SUSPENDED参数。线程创建完毕后句柄保存在TThread变量的handle属性内。
void ThreadCreate(TThread ** const threadPP, void * const userHandle, TThreadProc * const func, TThreadDoneFn * const threadDone, bool const useSigchld, const char ** const errorP) { TThread * threadP; MALLOCVAR(threadP); if (threadP == NULL) xmlrpc_asprintf(errorP, "Can't allocate memory for thread descriptor."); else { DWORD z; threadP->userHandle = userHandle; threadP->func = func; threadP->threadDone = threadDone; threadP->handle = (HANDLE)_beginthreadex(NULL, THREAD_STACK_SIZE, threadRun, threadP, CREATE_SUSPENDED, &z); if (threadP->handle == NULL) xmlrpc_asprintf(errorP, "_beginthreadex() failed."); else { *errorP = NULL; *threadPP = threadP; } if (*errorP) free(threadP); } }
static uint32_t WINAPI threadRun(void * const arg) { struct abyss_thread * const threadP = arg; threadP->func(threadP->userHandle); threadP->threadDone(threadP->userHandle); return 0; }
由于TThread中保存了线程句柄,所以其他所有线程封装函数均针对TThread的handle句柄进行操作。
struct abyss_mutex { HANDLE winMutex; };
typedef struct abyss_mutex TMutex;
abyss_mutex在windows平台下只是封装了一个句柄。MutexCreate函数处理过程也说明了这点。申请空间创建好TMutex后,接着只是调用CreateMutex函数创建windows平台下的mutex对象,成功后将句柄赋给TMutex的winMutex属性。其他Mutex函数就像线程函数一样,都只是针对Mutex句柄进行操作。
bool MutexCreate(TMutex ** const mutexPP) { TMutex * mutexP; bool succeeded; MALLOCVAR(mutexP); if (mutexP) { mutexP->winMutex = CreateMutex(NULL, FALSE, NULL); succeeded = (mutexP->winMutex != NULL); } else succeeded = FALSE; if (!succeeded) free(mutexP); *mutexPP = mutexP; TraceMsg( "Created Mutex %s\n", (succeeded ? "ok" : "FAILED") ); return succeeded; }