1
0
Fork 0
forked from fte/fteqw
fteqw/engine/common/sys_linux_threads.c
Spoike 27a59a0cbc LOTS OF CHANGES. was hoping to get revision 5000 perfect, but really that's never going to happen. this has gone on for too long now.
vulkan, wasapi, quake injector features added.
irc, avplug, cef plugins/drivers reworked/updated/added
openal reverb, doppler effects added.
'dir' console command now attempts to view clicked files.
lots of warning fixes, should now only be deprecation warnings for most targets (depending on compiler version anyway...).
SendEntity finally reworked to use flags properly.
effectinfo improved, other smc-targetted fixes.
mapcluster stuff now has support for linux.
.basebone+.baseframe now exist in ssqc.
qcc: -Fqccx supports qccx syntax, including qccx hacks. don't expect these to work in fteqw nor dp though.
qcc: rewrote function call handling to use refs rather than defs. this makes struct passing more efficient and makes the __out keyword usable with fields etc.
qccgui: can cope a little better with non-unicode files. can now represent most quake chars.
qcc: suppressed warnings from *extensions.qc

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5000 fc73d0e0-1445-4013-8a0c-d673dee63da5
2016-07-12 00:40:13 +00:00

479 lines
11 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//well, linux or cygwin (windows with posix emulation layer), anyway...
#include "quakedef.h"
#ifdef MULTITHREAD
#include <limits.h>
#include <pthread.h>
/* Thread creation calls */
typedef void *(*pfunction_t)(void *);
static pthread_t mainthread;
void Sys_ThreadsInit(void)
{
mainthread = pthread_self();
}
qboolean Sys_IsThread(void *thread)
{
return pthread_equal(pthread_self(), *(pthread_t*)thread);
}
qboolean Sys_IsMainThread(void)
{
return Sys_IsThread(&mainthread);
}
void Sys_ThreadAbort(void)
{
pthread_exit(NULL);
}
void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)
{
pthread_t *thread;
pthread_attr_t attr;
thread = (pthread_t *)malloc(sizeof(pthread_t));
if (!thread)
return NULL;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (stacksize < PTHREAD_STACK_MIN*2)
stacksize = PTHREAD_STACK_MIN*2;
pthread_attr_setstacksize(&attr, stacksize);
if (pthread_create(thread, &attr, (pfunction_t)func, args))
{
free(thread);
thread = NULL;
}
pthread_attr_destroy(&attr);
return (void *)thread;
}
void Sys_WaitOnThread(void *thread)
{
int err;
err = pthread_join(*(pthread_t *)thread, NULL);
if (err)
printf("pthread_join(%p) failed, error %s\n", thread, strerror(err));
free(thread);
}
/* Mutex calls */
void *Sys_CreateMutex(void)
{
pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
if (mutex && !pthread_mutex_init(mutex, NULL))
return mutex;
return NULL;
}
qboolean Sys_TryLockMutex(void *mutex)
{
return !pthread_mutex_trylock(mutex);
}
qboolean Sys_LockMutex(void *mutex)
{
return !pthread_mutex_lock(mutex);
}
qboolean Sys_UnlockMutex(void *mutex)
{
return !pthread_mutex_unlock(mutex);
}
void Sys_DestroyMutex(void *mutex)
{
pthread_mutex_destroy(mutex);
free(mutex);
}
/* Conditional wait calls */
typedef struct condvar_s
{
pthread_mutex_t *mutex;
pthread_cond_t *cond;
} condvar_t;
void *Sys_CreateConditional(void)
{
condvar_t *condv;
pthread_mutex_t *mutex;
pthread_cond_t *cond;
condv = (condvar_t *)malloc(sizeof(condvar_t));
if (!condv)
return NULL;
mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
if (!mutex)
return NULL;
cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));
if (!cond)
return NULL;
if (!pthread_mutex_init(mutex, NULL))
{
if (!pthread_cond_init(cond, NULL))
{
condv->cond = cond;
condv->mutex = mutex;
return (void *)condv;
}
else
pthread_mutex_destroy(mutex);
}
free(cond);
free(mutex);
free(condv);
return NULL;
}
qboolean Sys_LockConditional(void *condv)
{
return !pthread_mutex_lock(((condvar_t *)condv)->mutex);
}
qboolean Sys_UnlockConditional(void *condv)
{
return !pthread_mutex_unlock(((condvar_t *)condv)->mutex);
}
qboolean Sys_ConditionWait(void *condv)
{
return !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);
}
qboolean Sys_ConditionSignal(void *condv)
{
return !pthread_cond_signal(((condvar_t *)condv)->cond);
}
qboolean Sys_ConditionBroadcast(void *condv)
{
return !pthread_cond_broadcast(((condvar_t *)condv)->cond);
}
void Sys_DestroyConditional(void *condv)
{
condvar_t *cv = (condvar_t *)condv;
pthread_cond_destroy(cv->cond);
pthread_mutex_destroy(cv->mutex);
free(cv->cond);
free(cv->mutex);
free(cv);
}
#endif
void Sys_Sleep (double seconds)
{
struct timespec ts;
ts.tv_sec = (time_t)seconds;
seconds -= ts.tv_sec;
ts.tv_nsec = seconds * 1000000000.0;
nanosleep(&ts, NULL);
}
#ifdef SUBSERVERS
#include <spawn.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#define STDIN 0
#define STDOUT 1
#define STDERR 2
typedef struct slaveserver_s
{
pubsubserver_t pub;
int inpipe;
int outpipe;
pid_t pid; //so we don't end up with zombie processes
qbyte inbuffer[2048];
int inbufsize;
} linsubserver_t;
static void Sys_InstructSlave(pubsubserver_t *ps, sizebuf_t *cmd)
{
//FIXME: this is blocking. this is bad if the target is also blocking while trying to write to us.
//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full
linsubserver_t *s = (linsubserver_t*)ps;
if (s->outpipe == -1)
return; //it already died.
cmd->data[0] = cmd->cursize & 0xff;
cmd->data[1] = (cmd->cursize>>8) & 0xff;
write(s->outpipe, cmd->data, cmd->cursize);
}
static int Sys_SubServerRead(pubsubserver_t *ps)
{
linsubserver_t *s = (linsubserver_t*)ps;
if (s->inbufsize < sizeof(s->inbuffer) && s->inpipe != -1)
{
ssize_t avail = read(s->inpipe, s->inbuffer+s->inbufsize, sizeof(s->inbuffer)-s->inbufsize);
if (!avail)
{ //eof
close(s->inpipe);
close(s->outpipe);
Con_Printf("%i:%s has died\n", s->pub.id, s->pub.name);
s->inpipe = -1;
s->outpipe = -1;
waitpid(s->pid, NULL, 0);
}
else if (avail < 0)
{
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK)
;
else
perror("subserver read");
}
else
s->inbufsize += avail;
}
if(s->inbufsize >= 2)
{
unsigned short len = s->inbuffer[0] | (s->inbuffer[1]<<8);
if (s->inbufsize >= len && len>=2)
{
memcpy(net_message.data, s->inbuffer+2, len-2);
net_message.cursize = len-2;
memmove(s->inbuffer, s->inbuffer+len, s->inbufsize - len);
s->inbufsize -= len;
MSG_BeginReading (msg_nullnetprim);
return 1;
}
}
else if (s->inpipe == -1)
return -1;
return 0;
}
#ifdef SQL
#include "sv_sql.h"
#endif
pubsubserver_t *Sys_ForkServer(void)
{
#ifdef SERVERONLY
// extern jmp_buf host_abort;
int toslave[2];
int tomaster[2];
linsubserver_t *ctx;
pid_t pid;
//make sure we're fully synced, so that workers can't mess up
Cvar_Set(Cvar_FindVar("worker_count"), "0");
COM_WorkerFullSync();
DL_DeThread();
#ifdef SQL
SQL_KillServers(); //FIXME: this is bad...
#endif
//FIXME: we should probably use posix_atfork for those.
pipe(toslave);
pipe(tomaster);
//make the reads non-blocking.
fcntl(toslave[1], F_SETFL, fcntl(toslave[1], F_GETFL, 0)|O_NONBLOCK);
fcntl(tomaster[0], F_SETFL, fcntl(tomaster[0], F_GETFL, 0)|O_NONBLOCK);
pid = fork();
if (!pid)
{ //this is the child
dup2(toslave[0], STDIN);
close(toslave[1]);
close(toslave[0]);
dup2(tomaster[1], STDOUT);
isClusterSlave = true;
FS_UnloadPackFiles(); //these handles got wiped. make sure they're all properly wiped before loading new handles.
NET_Shutdown();
FS_ReloadPackFiles();
return NULL; //lets hope the caller can cope.
//jump out into the main work loop
// longjmp(host_abort, 1);
// exit(0); //err...
}
else
{ //this is the parent
close(toslave[0]);
close(tomaster[1]);
if (pid == -1)
{ //fork failed. make sure everything is destroyed.
close(toslave[1]);
close(tomaster[0]);
return NULL;
}
Con_DPrintf("Forked new server node\n");
ctx = Z_Malloc(sizeof(*ctx));
}
#else
int toslave[2];
int tomaster[2];
char exename[MAX_OSPATH];
posix_spawn_file_actions_t action;
linsubserver_t *ctx;
char *argv[64];
int argc = 0;
argv[argc++] = exename;
argv[argc++] = "-clusterslave";
argc += FS_GetManifestArgv(argv+argc, countof(argv)-argc-1);
argv[argc++] = NULL;
#if 0
strcpy(exename, "/bin/ls");
args[1] = NULL;
#elif 0
strcpy(exename, "/tmp/ftedbg/fteqw.sv");
#else
memset(exename, 0, sizeof(exename)); //having problems with valgrind being stupid.
if (readlink("/proc/self/exe", exename, sizeof(exename)-1) <= 0)
return NULL;
#endif
Con_DPrintf("Execing %s\n", exename);
ctx = Z_Malloc(sizeof(*ctx));
pipe(toslave);
pipe(tomaster);
//make the reads non-blocking.
fcntl(toslave[1], F_SETFL, fcntl(toslave[1], F_GETFL, 0)|O_NONBLOCK);
fcntl(tomaster[0], F_SETFL, fcntl(tomaster[0], F_GETFL, 0)|O_NONBLOCK);
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, toslave[1]);
posix_spawn_file_actions_addclose(&action, tomaster[0]);
posix_spawn_file_actions_adddup2(&action, toslave[0], STDIN);
posix_spawn_file_actions_adddup2(&action, tomaster[1], STDOUT);
// posix_spawn_file_actions_adddup2(&action, tomaster[1], STDERR);
posix_spawn_file_actions_addclose(&action, toslave[0]);
posix_spawn_file_actions_addclose(&action, tomaster[1]);
posix_spawn(&ctx->pid, exename, &action, NULL, argv, NULL);
#endif
ctx->inpipe = tomaster[0];
close(tomaster[1]);
close(toslave[0]);
ctx->outpipe = toslave[1];
ctx->pub.funcs.InstructSlave = Sys_InstructSlave;
ctx->pub.funcs.SubServerRead = Sys_SubServerRead;
return &ctx->pub;
}
void SSV_InstructMaster(sizebuf_t *cmd)
{
cmd->data[0] = cmd->cursize & 0xff;
cmd->data[1] = (cmd->cursize>>8) & 0xff;
write(STDOUT, cmd->data, cmd->cursize);
//FIXME: handle partial writes.
}
void SSV_CheckFromMaster(void)
{
static char inbuffer[1024];
static int inbufsize;
for(;;)
{
if(inbufsize >= 2)
{
unsigned short len = inbuffer[0] | (inbuffer[1]<<8);
if (inbufsize >= len && len>=2)
{
memcpy(net_message.data, inbuffer+2, len-2);
net_message.cursize = len-2;
memmove(inbuffer, inbuffer+len, inbufsize - len);
inbufsize -= len;
MSG_BeginReading (msg_nullnetprim);
SSV_ReadFromControlServer();
continue; //keep trying to handle it
}
}
if (inbufsize == sizeof(inbuffer))
{ //fatal: we can't easily recover from this.
SV_FinalMessage("Cluster message too large\n");
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
break;
}
{
ssize_t avail = read(STDIN, inbuffer+inbufsize, sizeof(inbuffer)-inbufsize);
if (!avail)
{ //eof
SV_FinalMessage("Cluster shut down\n");
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
break;
}
else if (avail < 0)
{
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK)
;
else
perror("master read");
break;
}
else
inbufsize += avail;
}
}
}
#endif