314 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* $Id$ */
 | 
						|
 | 
						|
/** @file thread.cpp */
 | 
						|
 | 
						|
#include "stdafx.h"
 | 
						|
#include "thread.h"
 | 
						|
#include <stdlib.h>
 | 
						|
#include "helpers.hpp"
 | 
						|
 | 
						|
#if defined(__AMIGA__) || defined(PSP) || defined(NO_THREADS)
 | 
						|
OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg) { return NULL; }
 | 
						|
void *OTTDJoinThread(OTTDThread *t) { return NULL; }
 | 
						|
void OTTDExitThread() { NOT_REACHED(); };
 | 
						|
 | 
						|
#elif defined(__OS2__)
 | 
						|
 | 
						|
#define INCL_DOS
 | 
						|
#include <os2.h>
 | 
						|
#include <process.h>
 | 
						|
 | 
						|
struct OTTDThread {
 | 
						|
	TID thread;
 | 
						|
	OTTDThreadFunc func;
 | 
						|
	void* arg;
 | 
						|
	void* ret;
 | 
						|
};
 | 
						|
 | 
						|
static void Proxy(void* arg)
 | 
						|
{
 | 
						|
	OTTDThread* t = (OTTDThread*)arg;
 | 
						|
	t->ret = t->func(t->arg);
 | 
						|
}
 | 
						|
 | 
						|
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 | 
						|
{
 | 
						|
	OTTDThread* t = MallocT<OTTDThread>(1);
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	t->func = function;
 | 
						|
	t->arg  = arg;
 | 
						|
	t->thread = _beginthread(Proxy, NULL, 32768, t);
 | 
						|
	if (t->thread != (TID)-1) {
 | 
						|
		return t;
 | 
						|
	} else {
 | 
						|
		free(t);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void* OTTDJoinThread(OTTDThread* t)
 | 
						|
{
 | 
						|
	void* ret;
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	DosWaitThread(&t->thread, DCWW_WAIT);
 | 
						|
	ret = t->ret;
 | 
						|
	free(t);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void OTTDExitThread()
 | 
						|
{
 | 
						|
	_endthread();
 | 
						|
}
 | 
						|
 | 
						|
#elif defined(UNIX) && !defined(MORPHOS)
 | 
						|
 | 
						|
#include <pthread.h>
 | 
						|
 | 
						|
struct OTTDThread {
 | 
						|
	pthread_t thread;
 | 
						|
};
 | 
						|
 | 
						|
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 | 
						|
{
 | 
						|
	OTTDThread* t = MallocT<OTTDThread>(1);
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	if (pthread_create(&t->thread, NULL, function, arg) == 0) {
 | 
						|
		return t;
 | 
						|
	} else {
 | 
						|
		free(t);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void* OTTDJoinThread(OTTDThread* t)
 | 
						|
{
 | 
						|
	void* ret;
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	pthread_join(t->thread, &ret);
 | 
						|
	free(t);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void OTTDExitThread()
 | 
						|
{
 | 
						|
	pthread_exit(NULL);
 | 
						|
}
 | 
						|
 | 
						|
#elif defined(WIN32)
 | 
						|
 | 
						|
#include <windows.h>
 | 
						|
 | 
						|
struct OTTDThread {
 | 
						|
	HANDLE thread;
 | 
						|
	OTTDThreadFunc func;
 | 
						|
	void* arg;
 | 
						|
	void* ret;
 | 
						|
};
 | 
						|
 | 
						|
static DWORD WINAPI Proxy(LPVOID arg)
 | 
						|
{
 | 
						|
	OTTDThread* t = (OTTDThread*)arg;
 | 
						|
	t->ret = t->func(t->arg);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 | 
						|
{
 | 
						|
	OTTDThread* t = MallocT<OTTDThread>(1);
 | 
						|
	DWORD dwThreadId;
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	t->func = function;
 | 
						|
	t->arg  = arg;
 | 
						|
	t->thread = CreateThread(NULL, 0, Proxy, t, 0, &dwThreadId);
 | 
						|
 | 
						|
	if (t->thread != NULL) {
 | 
						|
		return t;
 | 
						|
	} else {
 | 
						|
		free(t);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void* OTTDJoinThread(OTTDThread* t)
 | 
						|
{
 | 
						|
	void* ret;
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	WaitForSingleObject(t->thread, INFINITE);
 | 
						|
	CloseHandle(t->thread);
 | 
						|
	ret = t->ret;
 | 
						|
	free(t);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void OTTDExitThread()
 | 
						|
{
 | 
						|
	ExitThread(0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#elif defined(MORPHOS)
 | 
						|
 | 
						|
#include <exec/types.h>
 | 
						|
#include <exec/rawfmt.h>
 | 
						|
#include <dos/dostags.h>
 | 
						|
 | 
						|
#include <proto/dos.h>
 | 
						|
#include <proto/exec.h>
 | 
						|
 | 
						|
#include <setjmp.h>
 | 
						|
 | 
						|
/* NOTE: this code heavily depends on latest libnix updates. So make
 | 
						|
 *        sure you link with new stuff which supports semaphore locking of
 | 
						|
 *        the IO resources, else it will just go foobar. */
 | 
						|
 | 
						|
struct OTTDThreadStartupMessage {
 | 
						|
	struct Message msg;  ///< standard exec.library message (MUST be the first thing in the message struct!)
 | 
						|
	OTTDThreadFunc func; ///< function the thread will execute
 | 
						|
	void *arg;           ///< functions arguments for the thread function
 | 
						|
	void *ret;           ///< return value of the thread function
 | 
						|
	jmp_buf jumpstore;   ///< storage for the setjump state
 | 
						|
};
 | 
						|
 | 
						|
struct OTTDThread {
 | 
						|
	struct MsgPort *replyport;
 | 
						|
	struct OTTDThreadStartupMessage msg;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 *  Default OpenTTD STDIO/ERR debug output is not very useful for this, so we
 | 
						|
 *  utilize serial/ramdebug instead.
 | 
						|
 */
 | 
						|
#ifndef NO_DEBUG_MESSAGES
 | 
						|
void KPutStr(CONST_STRPTR format)
 | 
						|
{
 | 
						|
	RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL);
 | 
						|
}
 | 
						|
#else
 | 
						|
#define KPutStr(x)
 | 
						|
#endif
 | 
						|
 | 
						|
static void Proxy(void)
 | 
						|
{
 | 
						|
	struct Task *child = FindTask(NULL);
 | 
						|
	struct OTTDThreadStartupMessage *msg;
 | 
						|
 | 
						|
	/* Make sure, we don't block the parent. */
 | 
						|
	SetTaskPri(child, -5);
 | 
						|
 | 
						|
	KPutStr("[Child] Progressing...\n");
 | 
						|
 | 
						|
	if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
 | 
						|
		/* Make use of setjmp() here, so this point can be reached again from inside
 | 
						|
		 *  OTTDExitThread() which can be called from anythere inside msg->func.
 | 
						|
		 *  It's a bit ugly and in worst case it leaks some memory. */
 | 
						|
		if (setjmp(msg->jumpstore) == 0) {
 | 
						|
			msg->ret = msg->func(msg->arg);
 | 
						|
		} else {
 | 
						|
			KPutStr("[Child] Returned to main()\n");
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/*  Quit the child, exec.library will reply the startup msg internally. */
 | 
						|
	KPutStr("[Child] Done.\n");
 | 
						|
}
 | 
						|
 | 
						|
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void *arg)
 | 
						|
{
 | 
						|
	OTTDThread *t;
 | 
						|
	struct Task *parent;
 | 
						|
 | 
						|
	KPutStr("[OpenTTD] Create thread...\n");
 | 
						|
 | 
						|
	t = (struct OTTDThread *)AllocVecTaskPooled(sizeof(struct OTTDThread));
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	parent = FindTask(NULL);
 | 
						|
 | 
						|
	/* Make sure main thread runs with sane priority */
 | 
						|
	SetTaskPri(parent, 0);
 | 
						|
 | 
						|
	/* Things we'll pass down to the child by utilizing NP_StartupMsg */
 | 
						|
	t->msg.func = function;
 | 
						|
	t->msg.arg  = arg;
 | 
						|
	t->msg.ret  = NULL;
 | 
						|
 | 
						|
	t->replyport = CreateMsgPort();
 | 
						|
 | 
						|
	if (t->replyport != NULL) {
 | 
						|
		struct Process *child;
 | 
						|
 | 
						|
		t->msg.msg.mn_Node.ln_Type = NT_MESSAGE;
 | 
						|
		t->msg.msg.mn_ReplyPort    = t->replyport;
 | 
						|
		t->msg.msg.mn_Length       = sizeof(struct OTTDThreadStartupMessage);
 | 
						|
 | 
						|
		child = CreateNewProcTags(
 | 
						|
			NP_CodeType,     CODETYPE_PPC,
 | 
						|
			NP_Entry,        Proxy,
 | 
						|
			NP_StartupMsg,   (ULONG)&t->msg,
 | 
						|
			NP_Priority,     5UL,
 | 
						|
			NP_Name,         (ULONG)"OpenTTD Thread",
 | 
						|
			NP_PPCStackSize, 131072UL,
 | 
						|
			TAG_DONE);
 | 
						|
 | 
						|
		if (child != NULL) {
 | 
						|
			KPutStr("[OpenTTD] Child process launched.\n");
 | 
						|
			return t;
 | 
						|
		}
 | 
						|
		DeleteMsgPort(t->replyport);
 | 
						|
	}
 | 
						|
	FreeVecTaskPooled(t);
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void* OTTDJoinThread(OTTDThread *t)
 | 
						|
{
 | 
						|
	struct OTTDThreadStartupMessage *reply;
 | 
						|
	void *ret;
 | 
						|
 | 
						|
	KPutStr("[OpenTTD] Join threads...\n");
 | 
						|
 | 
						|
	if (t == NULL) return NULL;
 | 
						|
 | 
						|
	KPutStr("[OpenTTD] Wait for child to quit...\n");
 | 
						|
	WaitPort(t->replyport);
 | 
						|
 | 
						|
	reply = (struct OTTDThreadStartupMessage *)GetMsg(t->replyport);
 | 
						|
	ret   = reply->ret;
 | 
						|
 | 
						|
	DeleteMsgPort(t->replyport);
 | 
						|
	FreeVecTaskPooled(t);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void OTTDExitThread()
 | 
						|
{
 | 
						|
	struct OTTDThreadStartupMessage *msg;
 | 
						|
 | 
						|
	KPutStr("[Child] Aborting...\n");
 | 
						|
 | 
						|
	if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
 | 
						|
		KPutStr("[Child] Jumping back...\n");
 | 
						|
		longjmp(msg->jumpstore, 0xBEAFCAFE);
 | 
						|
	}
 | 
						|
 | 
						|
	NOT_REACHED();
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |