mirror of
https://github.com/id-Software/DOOM-3.git
synced 2026-03-20 08:59:56 +01:00
hello world
This commit is contained in:
136
neo/sys/posix/posix_input.cpp
Normal file
136
neo/sys/posix/posix_input.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../../idlib/precompiled.h"
|
||||
#include "posix_public.h"
|
||||
|
||||
typedef struct poll_keyboard_event_s
|
||||
{
|
||||
int key;
|
||||
bool state;
|
||||
} poll_keyboard_event_t;
|
||||
|
||||
typedef struct poll_mouse_event_s
|
||||
{
|
||||
int action;
|
||||
int value;
|
||||
} poll_mouse_event_t;
|
||||
|
||||
#define MAX_POLL_EVENTS 50
|
||||
#define POLL_EVENTS_HEADROOM 2 // some situations require to add several events
|
||||
static poll_keyboard_event_t poll_events_keyboard[MAX_POLL_EVENTS + POLL_EVENTS_HEADROOM];
|
||||
static int poll_keyboard_event_count;
|
||||
static poll_mouse_event_t poll_events_mouse[MAX_POLL_EVENTS + POLL_EVENTS_HEADROOM];
|
||||
static int poll_mouse_event_count;
|
||||
|
||||
/*
|
||||
==========
|
||||
Posix_AddKeyboardPollEvent
|
||||
==========
|
||||
*/
|
||||
bool Posix_AddKeyboardPollEvent(int key, bool state) {
|
||||
if (poll_keyboard_event_count >= MAX_POLL_EVENTS + POLL_EVENTS_HEADROOM)
|
||||
common->FatalError( "poll_keyboard_event_count exceeded MAX_POLL_EVENT + POLL_EVENTS_HEADROOM\n");
|
||||
poll_events_keyboard[poll_keyboard_event_count].key = key;
|
||||
poll_events_keyboard[poll_keyboard_event_count++].state = state;
|
||||
if (poll_keyboard_event_count >= MAX_POLL_EVENTS) {
|
||||
common->DPrintf("WARNING: reached MAX_POLL_EVENT poll_keyboard_event_count\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==========
|
||||
Posix_AddMousePollEvent
|
||||
==========
|
||||
*/
|
||||
bool Posix_AddMousePollEvent(int action, int value) {
|
||||
if (poll_mouse_event_count >= MAX_POLL_EVENTS + POLL_EVENTS_HEADROOM)
|
||||
common->FatalError( "poll_mouse_event_count exceeded MAX_POLL_EVENT + POLL_EVENTS_HEADROOM\n");
|
||||
poll_events_mouse[poll_mouse_event_count].action = action;
|
||||
poll_events_mouse[poll_mouse_event_count++].value = value;
|
||||
if (poll_mouse_event_count >= MAX_POLL_EVENTS) {
|
||||
common->DPrintf("WARNING: reached MAX_POLL_EVENT poll_mouse_event_count\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
===========================================================================
|
||||
polled from GetDirectUsercmd
|
||||
async input polling is obsolete
|
||||
we have a single entry point for both mouse and keyboard
|
||||
the mouse/keyboard seperation is API legacy
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
int Sys_PollKeyboardInputEvents( void ) {
|
||||
return poll_keyboard_event_count;
|
||||
}
|
||||
|
||||
int Sys_ReturnKeyboardInputEvent( const int n, int &key, bool &state ) {
|
||||
if ( n >= poll_keyboard_event_count ) {
|
||||
return 0;
|
||||
}
|
||||
key = poll_events_keyboard[n].key;
|
||||
state = poll_events_keyboard[n].state;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Sys_EndKeyboardInputEvents( void ) {
|
||||
//isn't this were it's supposed to be, was missing some key strokes with it set below
|
||||
poll_keyboard_event_count = 0;
|
||||
}
|
||||
|
||||
int Sys_PollMouseInputEvents( void ) {
|
||||
#if 0 //moved to the Sys_End functions
|
||||
poll_keyboard_event_count = 0;
|
||||
poll_mouse_event_count = 0;
|
||||
#endif
|
||||
|
||||
// that's OS specific, implemented in osx/ and linux/
|
||||
Posix_PollInput( );
|
||||
|
||||
return poll_mouse_event_count;
|
||||
}
|
||||
|
||||
int Sys_ReturnMouseInputEvent( const int n, int &action, int &value )
|
||||
{
|
||||
if ( n>=poll_mouse_event_count ) {
|
||||
return 0;
|
||||
}
|
||||
action = poll_events_mouse[ n ].action;
|
||||
value = poll_events_mouse[ n ].value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Sys_EndMouseInputEvents( void ) {
|
||||
// moved out of the Sys_PollMouseInputEvents
|
||||
poll_mouse_event_count = 0;
|
||||
}
|
||||
1053
neo/sys/posix/posix_main.cpp
Normal file
1053
neo/sys/posix/posix_main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
784
neo/sys/posix/posix_net.cpp
Normal file
784
neo/sys/posix/posix_net.cpp
Normal file
@@ -0,0 +1,784 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <net/if.h>
|
||||
#if MACOS_X
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include "../../idlib/precompiled.h"
|
||||
|
||||
idPort clientPort, serverPort;
|
||||
|
||||
idCVar net_ip( "net_ip", "localhost", CVAR_SYSTEM, "local IP address" );
|
||||
idCVar net_port( "net_port", "", CVAR_SYSTEM | CVAR_INTEGER, "local IP port number" );
|
||||
|
||||
typedef struct {
|
||||
unsigned long ip;
|
||||
unsigned long mask;
|
||||
} net_interface;
|
||||
|
||||
#define MAX_INTERFACES 32
|
||||
int num_interfaces = 0;
|
||||
net_interface netint[MAX_INTERFACES];
|
||||
|
||||
/*
|
||||
=============
|
||||
NetadrToSockadr
|
||||
=============
|
||||
*/
|
||||
static void NetadrToSockadr( const netadr_t * a, struct sockaddr_in *s ) {
|
||||
memset(s, 0, sizeof(*s));
|
||||
|
||||
if ( a->type == NA_BROADCAST ) {
|
||||
s->sin_family = AF_INET;
|
||||
|
||||
s->sin_port = htons( (short)a->port );
|
||||
*(int *) &s->sin_addr = -1;
|
||||
} else if ( a->type == NA_IP || a->type == NA_LOOPBACK ) {
|
||||
s->sin_family = AF_INET;
|
||||
|
||||
*(int *) &s->sin_addr = *(int *) &a->ip;
|
||||
s->sin_port = htons( (short)a->port );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SockadrToNetadr
|
||||
=============
|
||||
*/
|
||||
static void SockadrToNetadr(struct sockaddr_in *s, netadr_t * a) {
|
||||
unsigned int ip = *(int *)&s->sin_addr;
|
||||
*(int *)&a->ip = ip;
|
||||
a->port = ntohs( s->sin_port );
|
||||
// we store in network order, that loopback test is host order..
|
||||
ip = ntohl( ip );
|
||||
if ( ip == INADDR_LOOPBACK ) {
|
||||
a->type = NA_LOOPBACK;
|
||||
} else {
|
||||
a->type = NA_IP;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
ExtractPort
|
||||
=============
|
||||
*/
|
||||
static bool ExtractPort( const char *src, char *buf, int bufsize, int *port ) {
|
||||
char *p;
|
||||
strncpy( buf, src, bufsize );
|
||||
p = buf; p += Min( bufsize - 1, (int)strlen( src ) ); *p = '\0';
|
||||
p = strchr( buf, ':' );
|
||||
if ( !p ) {
|
||||
return false;
|
||||
}
|
||||
*p = '\0';
|
||||
*port = strtol( p+1, NULL, 10 );
|
||||
if ( ( *port == 0 && errno == EINVAL ) ||
|
||||
( ( *port == LONG_MIN || *port == LONG_MAX ) && errno == ERANGE ) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
StringToSockaddr
|
||||
=============
|
||||
*/
|
||||
static bool StringToSockaddr( const char *s, struct sockaddr_in *sadr, bool doDNSResolve ) {
|
||||
struct hostent *h;
|
||||
char buf[256];
|
||||
int port;
|
||||
|
||||
memset( sadr, 0, sizeof( *sadr ) );
|
||||
sadr->sin_family = AF_INET;
|
||||
|
||||
sadr->sin_port = 0;
|
||||
|
||||
if (s[0] >= '0' && s[0] <= '9') {
|
||||
if ( !inet_aton( s, &sadr->sin_addr ) ) {
|
||||
// check for port
|
||||
if ( !ExtractPort( s, buf, sizeof( buf ), &port ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( !inet_aton( buf, &sadr->sin_addr ) ) {
|
||||
return false;
|
||||
}
|
||||
sadr->sin_port = htons( port );
|
||||
}
|
||||
} else if ( doDNSResolve ) {
|
||||
// try to remove the port first, otherwise the DNS gets confused into multiple timeouts
|
||||
// failed or not failed, buf is expected to contain the appropriate host to resolve
|
||||
if ( ExtractPort( s, buf, sizeof( buf ), &port ) ) {
|
||||
sadr->sin_port = htons( port );
|
||||
}
|
||||
if ( !( h = gethostbyname( buf ) ) ) {
|
||||
return false;
|
||||
}
|
||||
*(int *) &sadr->sin_addr =
|
||||
*(int *) h->h_addr_list[0];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Sys_StringToAdr
|
||||
=============
|
||||
*/
|
||||
bool Sys_StringToNetAdr( const char *s, netadr_t * a, bool doDNSResolve ) {
|
||||
struct sockaddr_in sadr;
|
||||
|
||||
if ( !StringToSockaddr( s, &sadr, doDNSResolve ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SockadrToNetadr( &sadr, a );
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Sys_NetAdrToString
|
||||
=============
|
||||
*/
|
||||
const char *Sys_NetAdrToString( const netadr_t a ) {
|
||||
static char s[64];
|
||||
|
||||
if ( a.type == NA_LOOPBACK ) {
|
||||
if ( a.port ) {
|
||||
idStr::snPrintf( s, sizeof(s), "localhost:%i", a.port );
|
||||
} else {
|
||||
idStr::snPrintf( s, sizeof(s), "localhost" );
|
||||
}
|
||||
} else if ( a.type == NA_IP ) {
|
||||
idStr::snPrintf( s, sizeof(s), "%i.%i.%i.%i:%i",
|
||||
a.ip[0], a.ip[1], a.ip[2], a.ip[3], a.port );
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_IsLANAddress
|
||||
==================
|
||||
*/
|
||||
bool Sys_IsLANAddress( const netadr_t adr ) {
|
||||
int i;
|
||||
unsigned long *p_ip;
|
||||
unsigned long ip;
|
||||
|
||||
#if ID_NOLANADDRESS
|
||||
common->Printf( "Sys_IsLANAddress: ID_NOLANADDRESS\n" );
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if ( adr.type == NA_LOOPBACK ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( adr.type != NA_IP ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !num_interfaces ) {
|
||||
return false; // well, if there's no networking, there are no LAN addresses, right
|
||||
}
|
||||
|
||||
for ( i = 0; i < num_interfaces; i++ ) {
|
||||
p_ip = (unsigned long *)&adr.ip[0];
|
||||
ip = ntohl( *p_ip );
|
||||
if( ( netint[i].ip & netint[i].mask ) == ( ip & netint[i].mask ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Sys_CompareNetAdrBase
|
||||
|
||||
Compares without the port
|
||||
===================
|
||||
*/
|
||||
bool Sys_CompareNetAdrBase( const netadr_t a, const netadr_t b ) {
|
||||
if ( a.type != b.type ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( a.type == NA_LOOPBACK ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( a.type == NA_IP ) {
|
||||
if ( a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
common->Printf( "Sys_CompareNetAdrBase: bad address type\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
NET_InitNetworking
|
||||
====================
|
||||
*/
|
||||
void Sys_InitNetworking(void)
|
||||
{
|
||||
// haven't been able to clearly pinpoint which standards or RFCs define SIOCGIFCONF, SIOCGIFADDR, SIOCGIFNETMASK ioctls
|
||||
// it seems fairly widespread, in Linux kernel ioctl, and in BSD .. so let's assume it's always available on our targets
|
||||
|
||||
#if MACOS_X
|
||||
unsigned int ip, mask;
|
||||
struct ifaddrs *ifap, *ifp;
|
||||
|
||||
num_interfaces = 0;
|
||||
|
||||
if( getifaddrs( &ifap ) < 0 ) {
|
||||
common->FatalError( "InitNetworking: SIOCGIFCONF error - %s\n", strerror( errno ) );
|
||||
return;
|
||||
}
|
||||
|
||||
for( ifp = ifap; ifp; ifp = ifp->ifa_next ) {
|
||||
if ( ifp->ifa_addr->sa_family != AF_INET )
|
||||
continue;
|
||||
|
||||
if ( !( ifp->ifa_flags & IFF_UP ) )
|
||||
continue;
|
||||
|
||||
if ( !ifp->ifa_addr )
|
||||
continue;
|
||||
|
||||
if ( !ifp->ifa_netmask )
|
||||
continue;
|
||||
|
||||
ip = ntohl( *( unsigned long *)&ifp->ifa_addr->sa_data[2] );
|
||||
mask = ntohl( *( unsigned long *)&ifp->ifa_netmask->sa_data[2] );
|
||||
|
||||
if ( ip == INADDR_LOOPBACK ) {
|
||||
common->Printf( "loopback\n" );
|
||||
} else {
|
||||
common->Printf( "IP: %d.%d.%d.%d\n",
|
||||
(unsigned char)ifp->ifa_addr->sa_data[2],
|
||||
(unsigned char)ifp->ifa_addr->sa_data[3],
|
||||
(unsigned char)ifp->ifa_addr->sa_data[4],
|
||||
(unsigned char)ifp->ifa_addr->sa_data[5] );
|
||||
common->Printf( "NetMask: %d.%d.%d.%d\n",
|
||||
(unsigned char)ifp->ifa_netmask->sa_data[2],
|
||||
(unsigned char)ifp->ifa_netmask->sa_data[3],
|
||||
(unsigned char)ifp->ifa_netmask->sa_data[4],
|
||||
(unsigned char)ifp->ifa_netmask->sa_data[5] );
|
||||
}
|
||||
netint[ num_interfaces ].ip = ip;
|
||||
netint[ num_interfaces ].mask = mask;
|
||||
num_interfaces++;
|
||||
}
|
||||
#else
|
||||
int s;
|
||||
char buf[ MAX_INTERFACES*sizeof( ifreq ) ];
|
||||
ifconf ifc;
|
||||
ifreq *ifr;
|
||||
int ifindex;
|
||||
unsigned int ip, mask;
|
||||
|
||||
num_interfaces = 0;
|
||||
|
||||
s = socket( AF_INET, SOCK_DGRAM, 0 );
|
||||
ifc.ifc_len = MAX_INTERFACES*sizeof( ifreq );
|
||||
ifc.ifc_buf = buf;
|
||||
if ( ioctl( s, SIOCGIFCONF, &ifc ) < 0 ) {
|
||||
common->FatalError( "InitNetworking: SIOCGIFCONF error - %s\n", strerror( errno ) );
|
||||
return;
|
||||
}
|
||||
ifindex = 0;
|
||||
while ( ifindex < ifc.ifc_len ) {
|
||||
common->Printf( "found interface %s - ", ifc.ifc_buf + ifindex );
|
||||
// find the type - ignore interfaces for which we can find we can't get IP and mask ( not configured )
|
||||
ifr = (ifreq*)( ifc.ifc_buf + ifindex );
|
||||
if ( ioctl( s, SIOCGIFADDR, ifr ) < 0 ) {
|
||||
common->Printf( "SIOCGIFADDR failed: %s\n", strerror( errno ) );
|
||||
} else {
|
||||
if ( ifr->ifr_addr.sa_family != AF_INET ) {
|
||||
common->Printf( "not AF_INET\n" );
|
||||
} else {
|
||||
ip = ntohl( *( unsigned long *)&ifr->ifr_addr.sa_data[2] );
|
||||
if ( ip == INADDR_LOOPBACK ) {
|
||||
common->Printf( "loopback\n" );
|
||||
} else {
|
||||
common->Printf( "%d.%d.%d.%d",
|
||||
(unsigned char)ifr->ifr_addr.sa_data[2],
|
||||
(unsigned char)ifr->ifr_addr.sa_data[3],
|
||||
(unsigned char)ifr->ifr_addr.sa_data[4],
|
||||
(unsigned char)ifr->ifr_addr.sa_data[5] );
|
||||
}
|
||||
if ( ioctl( s, SIOCGIFNETMASK, ifr ) < 0 ) {
|
||||
common->Printf( " SIOCGIFNETMASK failed: %s\n", strerror( errno ) );
|
||||
} else {
|
||||
mask = ntohl( *( unsigned long *)&ifr->ifr_addr.sa_data[2] );
|
||||
if ( ip != INADDR_LOOPBACK ) {
|
||||
common->Printf( "/%d.%d.%d.%d\n",
|
||||
(unsigned char)ifr->ifr_addr.sa_data[2],
|
||||
(unsigned char)ifr->ifr_addr.sa_data[3],
|
||||
(unsigned char)ifr->ifr_addr.sa_data[4],
|
||||
(unsigned char)ifr->ifr_addr.sa_data[5] );
|
||||
}
|
||||
netint[ num_interfaces ].ip = ip;
|
||||
netint[ num_interfaces ].mask = mask;
|
||||
num_interfaces++;
|
||||
}
|
||||
}
|
||||
}
|
||||
ifindex += sizeof( ifreq );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
IPSocket
|
||||
====================
|
||||
*/
|
||||
static int IPSocket( const char *net_interface, int port, netadr_t *bound_to = NULL ) {
|
||||
int newsocket;
|
||||
struct sockaddr_in address;
|
||||
int i = 1;
|
||||
|
||||
if ( net_interface ) {
|
||||
common->Printf( "Opening IP socket: %s:%i\n", net_interface, port );
|
||||
} else {
|
||||
common->Printf( "Opening IP socket: localhost:%i\n", port );
|
||||
}
|
||||
|
||||
if ( ( newsocket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == -1 ) {
|
||||
common->Printf( "ERROR: IPSocket: socket: %s", strerror( errno ) );
|
||||
return 0;
|
||||
}
|
||||
// make it non-blocking
|
||||
int on = 1;
|
||||
if ( ioctl( newsocket, FIONBIO, &on ) == -1 ) {
|
||||
common->Printf( "ERROR: IPSocket: ioctl FIONBIO:%s\n",
|
||||
strerror( errno ) );
|
||||
return 0;
|
||||
}
|
||||
// make it broadcast capable
|
||||
if ( setsockopt( newsocket, SOL_SOCKET, SO_BROADCAST, (char *) &i, sizeof(i) ) == -1 ) {
|
||||
common->Printf( "ERROR: IPSocket: setsockopt SO_BROADCAST:%s\n", strerror( errno ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( !net_interface || !net_interface[ 0 ]
|
||||
|| !idStr::Icmp( net_interface, "localhost" ) ) {
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
} else {
|
||||
StringToSockaddr( net_interface, &address, true );
|
||||
}
|
||||
|
||||
if ( port == PORT_ANY ) {
|
||||
address.sin_port = 0;
|
||||
} else {
|
||||
address.sin_port = htons((short) port);
|
||||
}
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
|
||||
if ( bind( newsocket, (const struct sockaddr *)&address, sizeof( address ) ) == -1 ) {
|
||||
common->Printf( "ERROR: IPSocket: bind: %s\n", strerror( errno ) );
|
||||
close( newsocket );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( bound_to ) {
|
||||
unsigned int len = sizeof( address );
|
||||
if ( (unsigned int)(getsockname( newsocket, (struct sockaddr *)&address, (socklen_t*)&len )) == -1 ) {
|
||||
common->Printf( "ERROR: IPSocket: getsockname: %s\n", strerror( errno ) );
|
||||
close( newsocket );
|
||||
return 0;
|
||||
}
|
||||
SockadrToNetadr( &address, bound_to );
|
||||
}
|
||||
|
||||
return newsocket;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::idPort
|
||||
==================
|
||||
*/
|
||||
idPort::idPort() {
|
||||
netSocket = 0;
|
||||
memset( &bound_to, 0, sizeof( bound_to ) );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::~idPort
|
||||
==================
|
||||
*/
|
||||
idPort::~idPort() {
|
||||
Close();
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::Close
|
||||
==================
|
||||
*/
|
||||
void idPort::Close() {
|
||||
if ( netSocket ) {
|
||||
close(netSocket);
|
||||
netSocket = 0;
|
||||
memset( &bound_to, 0, sizeof( bound_to ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::GetPacket
|
||||
==================
|
||||
*/
|
||||
bool idPort::GetPacket( netadr_t &net_from, void *data, int &size, int maxSize ) {
|
||||
int ret;
|
||||
struct sockaddr_in from;
|
||||
int fromlen;
|
||||
|
||||
if ( !netSocket ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fromlen = sizeof( from );
|
||||
ret = recvfrom( netSocket, data, maxSize, 0, (struct sockaddr *) &from, (socklen_t *) &fromlen );
|
||||
|
||||
if ( ret == -1 ) {
|
||||
if (errno == EWOULDBLOCK || errno == ECONNREFUSED) {
|
||||
// those commonly happen, don't verbose
|
||||
return false;
|
||||
}
|
||||
common->DPrintf( "idPort::GetPacket recvfrom(): %s\n", strerror( errno ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
assert( ret < maxSize );
|
||||
|
||||
SockadrToNetadr( &from, &net_from );
|
||||
size = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::GetPacketBlocking
|
||||
==================
|
||||
*/
|
||||
bool idPort::GetPacketBlocking( netadr_t &net_from, void *data, int &size, int maxSize, int timeout ) {
|
||||
fd_set set;
|
||||
struct timeval tv;
|
||||
int ret;
|
||||
|
||||
if ( !netSocket ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( timeout < 0 ) {
|
||||
return GetPacket( net_from, data, size, maxSize );
|
||||
}
|
||||
|
||||
FD_ZERO( &set );
|
||||
FD_SET( netSocket, &set );
|
||||
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = ( timeout % 1000 ) * 1000;
|
||||
ret = select( netSocket+1, &set, NULL, NULL, &tv );
|
||||
if ( ret == -1 ) {
|
||||
if ( errno == EINTR ) {
|
||||
common->DPrintf( "idPort::GetPacketBlocking: select EINTR\n" );
|
||||
return false;
|
||||
} else {
|
||||
common->Error( "idPort::GetPacketBlocking: select failed: %s\n", strerror( errno ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ret == 0 ) {
|
||||
// timed out
|
||||
return false;
|
||||
}
|
||||
struct sockaddr_in from;
|
||||
int fromlen;
|
||||
fromlen = sizeof( from );
|
||||
ret = recvfrom( netSocket, data, maxSize, 0, (struct sockaddr *)&from, (socklen_t *)&fromlen );
|
||||
if ( ret == -1 ) {
|
||||
// there should be no blocking errors once select declares things are good
|
||||
common->DPrintf( "idPort::GetPacketBlocking: %s\n", strerror( errno ) );
|
||||
return false;
|
||||
}
|
||||
assert( ret < maxSize );
|
||||
SockadrToNetadr( &from, &net_from );
|
||||
size = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::SendPacket
|
||||
==================
|
||||
*/
|
||||
void idPort::SendPacket( const netadr_t to, const void *data, int size ) {
|
||||
int ret;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if ( to.type == NA_BAD ) {
|
||||
common->Warning( "idPort::SendPacket: bad address type NA_BAD - ignored" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !netSocket ) {
|
||||
return;
|
||||
}
|
||||
|
||||
NetadrToSockadr( &to, &addr );
|
||||
|
||||
ret = sendto( netSocket, data, size, 0, (struct sockaddr *) &addr, sizeof(addr) );
|
||||
if ( ret == -1 ) {
|
||||
common->Printf( "idPort::SendPacket ERROR: to %s: %s\n", Sys_NetAdrToString( to ), strerror( errno ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPort::InitForPort
|
||||
==================
|
||||
*/
|
||||
bool idPort::InitForPort( int portNumber ) {
|
||||
netSocket = IPSocket( net_ip.GetString(), portNumber, &bound_to );
|
||||
if ( netSocket <= 0 ) {
|
||||
netSocket = 0;
|
||||
memset( &bound_to, 0, sizeof( bound_to ) );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
idTCP::idTCP
|
||||
==================
|
||||
*/
|
||||
idTCP::idTCP() {
|
||||
fd = 0;
|
||||
memset(&address, 0, sizeof(address));
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idTCP::~idTCP
|
||||
==================
|
||||
*/
|
||||
idTCP::~idTCP() {
|
||||
Close();
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idTCP::Init
|
||||
==================
|
||||
*/
|
||||
bool idTCP::Init( const char *host, short port ) {
|
||||
struct sockaddr_in sadr;
|
||||
if ( !Sys_StringToNetAdr( host, &address, true ) ) {
|
||||
common->Printf( "Couldn't resolve server name \"%s\"\n", host );
|
||||
return false;
|
||||
}
|
||||
address.type = NA_IP;
|
||||
if (!address.port) {
|
||||
address.port = port;
|
||||
}
|
||||
common->Printf( "\"%s\" resolved to %i.%i.%i.%i:%i\n", host,
|
||||
address.ip[0], address.ip[1], address.ip[2], address.ip[3], address.port );
|
||||
NetadrToSockadr(&address, &sadr);
|
||||
|
||||
if (fd) {
|
||||
common->Warning("idTCP::Init: already initialized?\n");
|
||||
}
|
||||
|
||||
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
fd = 0;
|
||||
common->Printf("ERROR: idTCP::Init: socket: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( connect( fd, (const sockaddr *)&sadr, sizeof( sadr ) ) == -1 ) {
|
||||
common->Printf( "ERROR: idTCP::Init: connect: %s\n", strerror( errno ) );
|
||||
close( fd );
|
||||
fd = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int status;
|
||||
if ((status = fcntl(fd, F_GETFL, 0)) != -1) {
|
||||
status |= O_NONBLOCK; /* POSIX */
|
||||
status = fcntl(fd, F_SETFL, status);
|
||||
}
|
||||
if (status == -1) {
|
||||
common->Printf("ERROR: idTCP::Init: fcntl / O_NONBLOCK: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
fd = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
common->DPrintf("Opened TCP connection\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idTCP::Close
|
||||
==================
|
||||
*/
|
||||
void idTCP::Close() {
|
||||
if (fd) {
|
||||
close(fd);
|
||||
}
|
||||
fd = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idTCP::Read
|
||||
==================
|
||||
*/
|
||||
int idTCP::Read(void *data, int size) {
|
||||
int nbytes;
|
||||
|
||||
if (!fd) {
|
||||
common->Printf("idTCP::Read: not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(_GNU_SOURCE)
|
||||
// handle EINTR interrupted system call with TEMP_FAILURE_RETRY - this is probably GNU libc specific
|
||||
if ( ( nbytes = TEMP_FAILURE_RETRY( read( fd, data, size ) ) ) == -1 ) {
|
||||
#else
|
||||
do {
|
||||
nbytes = read( fd, data, size );
|
||||
} while ( nbytes == -1 && errno == EINTR );
|
||||
if ( nbytes == -1 ) {
|
||||
#endif
|
||||
if (errno == EAGAIN) {
|
||||
return 0;
|
||||
}
|
||||
common->Printf("ERROR: idTCP::Read: %s\n", strerror(errno));
|
||||
Close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// a successful read of 0 bytes indicates remote has closed the connection
|
||||
if ( nbytes == 0 ) {
|
||||
common->DPrintf( "idTCP::Read: read 0 bytes - assume connection closed\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idTCP::Write
|
||||
==================
|
||||
*/
|
||||
|
||||
static void got_SIGPIPE( int signum ) {
|
||||
common->Printf( "idTCP: SIGPIPE\n" );
|
||||
}
|
||||
|
||||
int idTCP::Write(void *data, int size) {
|
||||
int nbytes;
|
||||
|
||||
if ( !fd ) {
|
||||
common->Printf( "idTCP::Write: not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sigaction bak_action;
|
||||
struct sigaction action;
|
||||
|
||||
action.sa_handler = got_SIGPIPE;
|
||||
sigemptyset( &action.sa_mask );
|
||||
action.sa_flags = 0;
|
||||
|
||||
if ( sigaction( SIGPIPE, &action, &bak_action ) != 0 ) {
|
||||
common->Printf( "ERROR: idTCP::Write: failed to set temporary SIGPIPE handler\n" );
|
||||
Close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(_GNU_SOURCE)
|
||||
// handle EINTR interrupted system call with TEMP_FAILURE_RETRY - this is probably GNU libc specific
|
||||
if ( ( nbytes = TEMP_FAILURE_RETRY ( write( fd, data, size ) ) ) == -1 ) {
|
||||
#else
|
||||
do {
|
||||
nbytes = write( fd, data, size );
|
||||
} while ( nbytes == -1 && errno == EINTR );
|
||||
if ( nbytes == -1 ) {
|
||||
#endif
|
||||
common->Printf( "ERROR: idTCP::Write: %s\n", strerror( errno ) );
|
||||
Close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( sigaction( SIGPIPE, &bak_action, NULL ) != 0 ) {
|
||||
common->Printf( "ERROR: idTCP::Write: failed to reset SIGPIPE handler\n" );
|
||||
Close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
66
neo/sys/posix/posix_public.h
Normal file
66
neo/sys/posix/posix_public.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __SYS_POSIX__
|
||||
#define __SYS_POSIX__
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
void Posix_QueEvent( sysEventType_t type, int value, int value2, int ptrLength, void *ptr );
|
||||
const char* Posix_Cwd( void );
|
||||
|
||||
// called first thing. does InitSigs and various things
|
||||
void Posix_EarlyInit( );
|
||||
// called after common has been initialized
|
||||
void Posix_LateInit( );
|
||||
|
||||
void Posix_InitPThreads( );
|
||||
void Posix_InitSigs( );
|
||||
void Posix_ClearSigs( );
|
||||
|
||||
void Posix_Exit( int ret );
|
||||
void Posix_SetExit(int ret); // override the exit code
|
||||
void Posix_SetExitSpawn( const char *exeName ); // set the process to be spawned when we quit
|
||||
|
||||
void Posix_StartAsyncThread( void );
|
||||
extern xthreadInfo asyncThread;
|
||||
|
||||
bool Posix_AddKeyboardPollEvent( int key, bool state );
|
||||
bool Posix_AddMousePollEvent( int action, int value );
|
||||
|
||||
void Posix_PollInput( void );
|
||||
void Posix_InitConsoleInput( void );
|
||||
void Posix_Shutdown( void );
|
||||
|
||||
void Sys_FPE_handler( int signum, siginfo_t *info, void *context );
|
||||
void Sys_DoStartProcess( const char *exeName, bool dofork = true ); // if not forking, current process gets replaced
|
||||
|
||||
void Sys_AsyncThread( void );
|
||||
|
||||
#endif
|
||||
|
||||
166
neo/sys/posix/posix_signal.cpp
Normal file
166
neo/sys/posix/posix_signal.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../../idlib/precompiled.h"
|
||||
#include "posix_public.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
const int siglist[] = {
|
||||
SIGHUP,
|
||||
SIGQUIT,
|
||||
SIGILL,
|
||||
SIGTRAP,
|
||||
SIGIOT,
|
||||
SIGBUS,
|
||||
SIGFPE,
|
||||
SIGSEGV,
|
||||
SIGPIPE,
|
||||
SIGABRT,
|
||||
// SIGTTIN,
|
||||
// SIGTTOU,
|
||||
-1
|
||||
};
|
||||
|
||||
const char *signames[] = {
|
||||
"SIGHUP",
|
||||
"SIGQUIT",
|
||||
"SIGILL",
|
||||
"SIGTRAP",
|
||||
"SIGIOT",
|
||||
"SIGBUS",
|
||||
"SIGFPE",
|
||||
"SIGSEGV",
|
||||
"SIGPIPE",
|
||||
"SIGABRT",
|
||||
// "SIGTTIN",
|
||||
// "SIGTTOUT"
|
||||
};
|
||||
|
||||
static char fatalError[ 1024 ];
|
||||
|
||||
/*
|
||||
================
|
||||
Posix_ClearSigs
|
||||
================
|
||||
*/
|
||||
void Posix_ClearSigs( ) {
|
||||
struct sigaction action;
|
||||
int i;
|
||||
|
||||
/* Set up the structure */
|
||||
action.sa_handler = SIG_DFL;
|
||||
sigemptyset( &action.sa_mask );
|
||||
action.sa_flags = 0;
|
||||
|
||||
i = 0;
|
||||
while ( siglist[ i ] != -1 ) {
|
||||
if ( sigaction( siglist[ i ], &action, NULL ) != 0 ) {
|
||||
Sys_Printf( "Failed to reset %s handler: %s\n", signames[ i ], strerror( errno ) );
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
sig_handler
|
||||
================
|
||||
*/
|
||||
static void sig_handler( int signum, siginfo_t *info, void *context ) {
|
||||
static bool double_fault = false;
|
||||
|
||||
if ( double_fault ) {
|
||||
Sys_Printf( "double fault %s, bailing out\n", strsignal( signum ) );
|
||||
Posix_Exit( signum );
|
||||
}
|
||||
|
||||
double_fault = true;
|
||||
|
||||
// NOTE: see sigaction man page, could verbose the whole siginfo_t and print human readable si_code
|
||||
Sys_Printf( "signal caught: %s\nsi_code %d\n", strsignal( signum ), info->si_code );
|
||||
|
||||
#ifndef ID_BT_STUB
|
||||
Sys_Printf( "callstack:\n%s", Sys_GetCallStackCurStr( 30 ) );
|
||||
#endif
|
||||
|
||||
if ( fatalError[ 0 ] ) {
|
||||
Sys_Printf( "Was in fatal error shutdown: %s\n", fatalError );
|
||||
}
|
||||
|
||||
Sys_Printf( "Trying to exit gracefully..\n" );
|
||||
|
||||
Posix_SetExit( signum );
|
||||
|
||||
common->Quit();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Posix_InitSigs
|
||||
================
|
||||
*/
|
||||
void Posix_InitSigs( ) {
|
||||
struct sigaction action;
|
||||
int i;
|
||||
|
||||
fatalError[0] = '\0';
|
||||
|
||||
/* Set up the structure */
|
||||
action.sa_sigaction = sig_handler;
|
||||
sigemptyset( &action.sa_mask );
|
||||
action.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||
|
||||
i = 0;
|
||||
while ( siglist[ i ] != -1 ) {
|
||||
if ( siglist[ i ] == SIGFPE ) {
|
||||
action.sa_sigaction = Sys_FPE_handler;
|
||||
if ( sigaction( siglist[ i ], &action, NULL ) != 0 ) {
|
||||
Sys_Printf( "Failed to set SIGFPE handler: %s\n", strerror( errno ) );
|
||||
}
|
||||
action.sa_sigaction = sig_handler;
|
||||
} else if ( sigaction( siglist[ i ], &action, NULL ) != 0 ) {
|
||||
Sys_Printf( "Failed to set %s handler: %s\n", signames[ i ], strerror( errno ) );
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// if the process is backgrounded (running non interactively)
|
||||
// then SIGTTIN or SIGTOU could be emitted, if not caught, turns into a SIGSTP
|
||||
signal( SIGTTIN, SIG_IGN );
|
||||
signal( SIGTTOU, SIG_IGN );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_SetFatalError
|
||||
==================
|
||||
*/
|
||||
void Sys_SetFatalError( const char *error ) {
|
||||
strncpy( fatalError, error, sizeof( fatalError ) );
|
||||
}
|
||||
292
neo/sys/posix/posix_threads.cpp
Normal file
292
neo/sys/posix/posix_threads.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <pwd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "../../idlib/precompiled.h"
|
||||
#include "posix_public.h"
|
||||
|
||||
#if defined(_DEBUG)
|
||||
// #define ID_VERBOSE_PTHREADS
|
||||
#endif
|
||||
|
||||
/*
|
||||
======================================================
|
||||
locks
|
||||
======================================================
|
||||
*/
|
||||
|
||||
// we use an extra lock for the local stuff
|
||||
const int MAX_LOCAL_CRITICAL_SECTIONS = MAX_CRITICAL_SECTIONS + 1;
|
||||
static pthread_mutex_t global_lock[ MAX_LOCAL_CRITICAL_SECTIONS ];
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_EnterCriticalSection
|
||||
==================
|
||||
*/
|
||||
void Sys_EnterCriticalSection( int index ) {
|
||||
assert( index >= 0 && index < MAX_LOCAL_CRITICAL_SECTIONS );
|
||||
#ifdef ID_VERBOSE_PTHREADS
|
||||
if ( pthread_mutex_trylock( &global_lock[index] ) == EBUSY ) {
|
||||
Sys_Printf( "busy lock %d in thread '%s'\n", index, Sys_GetThreadName() );
|
||||
if ( pthread_mutex_lock( &global_lock[index] ) == EDEADLK ) {
|
||||
Sys_Printf( "FATAL: DEADLOCK %d, in thread '%s'\n", index, Sys_GetThreadName() );
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_mutex_lock( &global_lock[index] );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_LeaveCriticalSection
|
||||
==================
|
||||
*/
|
||||
void Sys_LeaveCriticalSection( int index ) {
|
||||
assert( index >= 0 && index < MAX_LOCAL_CRITICAL_SECTIONS );
|
||||
#ifdef ID_VERBOSE_PTHREADS
|
||||
if ( pthread_mutex_unlock( &global_lock[index] ) == EPERM ) {
|
||||
Sys_Printf( "FATAL: NOT LOCKED %d, in thread '%s'\n", index, Sys_GetThreadName() );
|
||||
}
|
||||
#else
|
||||
pthread_mutex_unlock( &global_lock[index] );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
======================================================
|
||||
wait and trigger events
|
||||
we use a single lock to manipulate the conditions, MAX_LOCAL_CRITICAL_SECTIONS-1
|
||||
|
||||
the semantics match the win32 version. signals raised while no one is waiting stay raised until a wait happens (which then does a simple pass-through)
|
||||
|
||||
NOTE: we use the same mutex for all the events. I don't think this would become much of a problem
|
||||
cond_wait unlocks atomically with setting the wait condition, and locks it back before exiting the function
|
||||
the potential for time wasting lock waits is very low
|
||||
======================================================
|
||||
*/
|
||||
|
||||
pthread_cond_t event_cond[ MAX_TRIGGER_EVENTS ];
|
||||
bool signaled[ MAX_TRIGGER_EVENTS ];
|
||||
bool waiting[ MAX_TRIGGER_EVENTS ];
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_WaitForEvent
|
||||
==================
|
||||
*/
|
||||
void Sys_WaitForEvent( int index ) {
|
||||
assert( index >= 0 && index < MAX_TRIGGER_EVENTS );
|
||||
Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
|
||||
assert( !waiting[ index ] ); // WaitForEvent from multiple threads? that wouldn't be good
|
||||
if ( signaled[ index ] ) {
|
||||
// emulate windows behaviour: signal has been raised already. clear and keep going
|
||||
signaled[ index ] = false;
|
||||
} else {
|
||||
waiting[ index ] = true;
|
||||
pthread_cond_wait( &event_cond[ index ], &global_lock[ MAX_LOCAL_CRITICAL_SECTIONS - 1 ] );
|
||||
waiting[ index ] = false;
|
||||
}
|
||||
Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_TriggerEvent
|
||||
==================
|
||||
*/
|
||||
void Sys_TriggerEvent( int index ) {
|
||||
assert( index >= 0 && index < MAX_TRIGGER_EVENTS );
|
||||
Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
|
||||
if ( waiting[ index ] ) {
|
||||
pthread_cond_signal( &event_cond[ index ] );
|
||||
} else {
|
||||
// emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going
|
||||
signaled[ index ] = true;
|
||||
}
|
||||
Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
|
||||
}
|
||||
|
||||
/*
|
||||
======================================================
|
||||
thread create and destroy
|
||||
======================================================
|
||||
*/
|
||||
|
||||
// not a hard limit, just what we keep track of for debugging
|
||||
#define MAX_THREADS 10
|
||||
xthreadInfo *g_threads[MAX_THREADS];
|
||||
|
||||
int g_thread_count = 0;
|
||||
|
||||
typedef void *(*pthread_function_t) (void *);
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_CreateThread
|
||||
==================
|
||||
*/
|
||||
void Sys_CreateThread( xthread_t function, void *parms, xthreadPriority priority, xthreadInfo& info, const char *name, xthreadInfo **threads, int *thread_count ) {
|
||||
Sys_EnterCriticalSection( );
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init( &attr );
|
||||
if ( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) != 0 ) {
|
||||
common->Error( "ERROR: pthread_attr_setdetachstate %s failed\n", name );
|
||||
}
|
||||
if ( pthread_create( ( pthread_t* )&info.threadHandle, &attr, ( pthread_function_t )function, parms ) != 0 ) {
|
||||
common->Error( "ERROR: pthread_create %s failed\n", name );
|
||||
}
|
||||
pthread_attr_destroy( &attr );
|
||||
info.name = name;
|
||||
if ( *thread_count < MAX_THREADS ) {
|
||||
threads[ ( *thread_count )++ ] = &info;
|
||||
} else {
|
||||
common->DPrintf( "WARNING: MAX_THREADS reached\n" );
|
||||
}
|
||||
Sys_LeaveCriticalSection( );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_DestroyThread
|
||||
==================
|
||||
*/
|
||||
void Sys_DestroyThread( xthreadInfo& info ) {
|
||||
// the target thread must have a cancelation point, otherwise pthread_cancel is useless
|
||||
assert( info.threadHandle );
|
||||
if ( pthread_cancel( ( pthread_t )info.threadHandle ) != 0 ) {
|
||||
common->Error( "ERROR: pthread_cancel %s failed\n", info.name );
|
||||
}
|
||||
if ( pthread_join( ( pthread_t )info.threadHandle, NULL ) != 0 ) {
|
||||
common->Error( "ERROR: pthread_join %s failed\n", info.name );
|
||||
}
|
||||
info.threadHandle = 0;
|
||||
Sys_EnterCriticalSection( );
|
||||
for( int i = 0 ; i < g_thread_count ; i++ ) {
|
||||
if ( &info == g_threads[ i ] ) {
|
||||
g_threads[ i ] = NULL;
|
||||
int j;
|
||||
for( j = i+1 ; j < g_thread_count ; j++ ) {
|
||||
g_threads[ j-1 ] = g_threads[ j ];
|
||||
}
|
||||
g_threads[ j-1 ] = NULL;
|
||||
g_thread_count--;
|
||||
Sys_LeaveCriticalSection( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
Sys_LeaveCriticalSection( );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_GetThreadName
|
||||
find the name of the calling thread
|
||||
==================
|
||||
*/
|
||||
const char* Sys_GetThreadName( int *index ) {
|
||||
Sys_EnterCriticalSection( );
|
||||
pthread_t thread = pthread_self();
|
||||
for( int i = 0 ; i < g_thread_count ; i++ ) {
|
||||
if ( thread == (pthread_t)g_threads[ i ]->threadHandle ) {
|
||||
if ( index ) {
|
||||
*index = i;
|
||||
}
|
||||
Sys_LeaveCriticalSection( );
|
||||
return g_threads[ i ]->name;
|
||||
}
|
||||
}
|
||||
if ( index ) {
|
||||
*index = -1;
|
||||
}
|
||||
Sys_LeaveCriticalSection( );
|
||||
return "main";
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================
|
||||
Async Thread
|
||||
=========================================================
|
||||
*/
|
||||
|
||||
xthreadInfo asyncThread;
|
||||
|
||||
/*
|
||||
=================
|
||||
Posix_StartAsyncThread
|
||||
=================
|
||||
*/
|
||||
void Posix_StartAsyncThread() {
|
||||
if ( asyncThread.threadHandle == 0 ) {
|
||||
Sys_CreateThread( (xthread_t)Sys_AsyncThread, NULL, THREAD_NORMAL, asyncThread, "Async", g_threads, &g_thread_count );
|
||||
} else {
|
||||
common->Printf( "Async thread already running\n" );
|
||||
}
|
||||
common->Printf( "Async thread started\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Posix_InitPThreads
|
||||
==================
|
||||
*/
|
||||
void Posix_InitPThreads( ) {
|
||||
int i;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
// init critical sections
|
||||
for ( i = 0; i < MAX_LOCAL_CRITICAL_SECTIONS; i++ ) {
|
||||
pthread_mutexattr_init( &attr );
|
||||
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
|
||||
pthread_mutex_init( &global_lock[i], &attr );
|
||||
pthread_mutexattr_destroy( &attr );
|
||||
}
|
||||
|
||||
// init event sleep/triggers
|
||||
for ( i = 0; i < MAX_TRIGGER_EVENTS; i++ ) {
|
||||
pthread_cond_init( &event_cond[ i ], NULL );
|
||||
signaled[i] = false;
|
||||
waiting[i] = false;
|
||||
}
|
||||
|
||||
// init threads table
|
||||
for ( i = 0; i < MAX_THREADS; i++ ) {
|
||||
g_threads[ i ] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user