blob: d4e7d127c74e8d99273781e8577afb49f88d81be [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* Machine and OS dependend definitions.
* This module implements host OS abstraction layer that helps make
* agent code portable between Linux, Windows, VxWorks and potentially other OSes.
*/
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include "mdep.h"
pthread_attr_t pthread_create_attr;
#ifdef WIN32
#include <fcntl.h>
#include <shlobj.h>
#define ERR_SOCKET (-1)
#define ERR_WIN32 (-2)
/*********************************************************************
Support of pthreads on Windows is implemented according to
reccomendations from the paper:
Strategies for Implementing POSIX Condition Variables on Win32
C++ Report, SIGS, Vol. 10, No. 5, June, 1998
Douglas C. Schmidt and Irfan Pyarali
Department of Computer Science
Washington University, St. Louis, Missouri
**********************************************************************/
void pthread_mutex_init(pthread_mutex_t * mutex, void * attr) {
assert(attr == NULL);
*mutex = CreateMutex(NULL, FALSE, NULL);
if (*mutex == NULL) {
fprintf(stderr, "Can't create mutex: error %d\n", GetLastError());
exit(1);
}
}
void pthread_mutex_lock(pthread_mutex_t * mutex) {
assert(mutex != NULL);
assert(*mutex != NULL);
WaitForSingleObject(*mutex, INFINITE);
}
void pthread_mutex_unlock(pthread_mutex_t * mutex) {
assert(mutex != NULL);
assert(*mutex != NULL);
ReleaseMutex(*mutex);
}
void pthread_cond_init(pthread_cond_t * cond, void * attr) {
assert(attr == NULL);
cond->waiters_count = 0;
cond->was_broadcast = 0;
cond->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
InitializeCriticalSection(&cond->waiters_count_lock);
cond->waiters_done = CreateEvent(NULL, FALSE, FALSE, NULL);
}
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) {
DWORD res = 0;
int last_waiter = 0;
EnterCriticalSection(&cond->waiters_count_lock);
cond->waiters_count++;
LeaveCriticalSection(&cond->waiters_count_lock);
// This call atomically releases the mutex and waits on the
// semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
// are called by another thread.
res = SignalObjectAndWait(*mutex, cond->sema, INFINITE, FALSE);
// Reacquire lock to avoid race conditions.
EnterCriticalSection(&cond->waiters_count_lock);
// We're no longer waiting...
cond->waiters_count--;
// Check to see if we're the last waiter after <pthread_cond_broadcast>.
last_waiter = cond->was_broadcast && cond->waiters_count == 0;
LeaveCriticalSection(&cond->waiters_count_lock);
// If we're the last waiter thread during this particular broadcast
// then let all the other threads proceed.
if (last_waiter) {
// This call atomically signals the <waiters_done_> event and waits until
// it can acquire the <mutex>. This is required to ensure fairness.
SignalObjectAndWait(cond->waiters_done, *mutex, INFINITE, FALSE);
}
else {
// Always regain the external mutex since that's the guarantee we
// give to our callers.
WaitForSingleObject(*mutex, INFINITE);
}
assert(res == WAIT_OBJECT_0);
return 0;
}
int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * timeout) {
DWORD res = 0;
int last_waiter = 0;
EnterCriticalSection(&cond->waiters_count_lock);
cond->waiters_count++;
LeaveCriticalSection(&cond->waiters_count_lock);
// This call atomically releases the mutex and waits on the
// semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
// are called by another thread.
res = SignalObjectAndWait(*mutex, cond->sema, timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000, FALSE);
// Reacquire lock to avoid race conditions.
EnterCriticalSection(&cond->waiters_count_lock);
// We're no longer waiting...
cond->waiters_count--;
// Check to see if we're the last waiter after <pthread_cond_broadcast>.
last_waiter = cond->was_broadcast && cond->waiters_count == 0;
LeaveCriticalSection(&cond->waiters_count_lock);
// If we're the last waiter thread during this particular broadcast
// then let all the other threads proceed.
if (last_waiter) {
// This call atomically signals the <waiters_done> event and waits until
// it can acquire the <mutex>. This is required to ensure fairness.
SignalObjectAndWait(cond->waiters_done, *mutex, INFINITE, FALSE);
}
else {
// Always regain the external mutex since that's the guarantee we
// give to our callers.
WaitForSingleObject(*mutex, INFINITE);
}
if (res == WAIT_TIMEOUT) return errno = ETIMEDOUT;
assert(res == WAIT_OBJECT_0);
return 0;
}
void pthread_cond_signal(pthread_cond_t *cond) {
int have_waiters = 0;
EnterCriticalSection(&cond->waiters_count_lock);
have_waiters = cond->waiters_count > 0;
LeaveCriticalSection(&cond->waiters_count_lock);
// If there aren't any waiters, then this is a no-op.
if (have_waiters) ReleaseSemaphore(cond->sema, 1, 0);
}
void pthread_cond_broadcast(pthread_cond_t *cond) {
int have_waiters = 0;
// This is needed to ensure that <waiters_count_> and <was_broadcast_> are
// consistent relative to each other.
EnterCriticalSection(&cond->waiters_count_lock);
if (cond->waiters_count > 0) {
// We are broadcasting, even if there is just one waiter...
// Record that we are broadcasting, which helps optimize
// <pthread_cond_wait> for the non-broadcast case.
cond->was_broadcast = 1;
have_waiters = 1;
}
if (have_waiters) {
// Wake up all the waiters atomically.
ReleaseSemaphore(cond->sema, cond->waiters_count, 0);
LeaveCriticalSection(&cond->waiters_count_lock);
// Wait for all the awakened threads to acquire the counting
// semaphore.
WaitForSingleObject(cond->waiters_done, INFINITE);
// This assignment is okay, even without the <waiters_count_lock_> held
// because no other waiter threads can wake up to access it.
cond->was_broadcast = 0;
}
else {
LeaveCriticalSection(&cond->waiters_count_lock);
}
}
typedef struct ThreadArgs ThreadArgs;
struct ThreadArgs {
void * (*start)(void *);
void * args;
};
static void start_thread(void * x) {
ThreadArgs a = *(ThreadArgs *)x;
free(x);
ExitThread((DWORD)a.start(a.args));
}
int pthread_create(pthread_t * thread, pthread_attr_t * attr,
void * (*start)(void *), void * args) {
unsigned long r;
ThreadArgs * a;
a = (ThreadArgs *)malloc(sizeof(ThreadArgs));
a->start = start;
a->args = args;
r = _beginthread(start_thread, 0, a);
if (r == (unsigned long)-1) {
int error = errno;
free(a);
errno = error;
return error;
}
*thread = (HANDLE)r;
return 0;
}
int pthread_join(pthread_t thread, void **value_ptr) {
if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) {
return EINVAL;
}
if (!GetExitCodeThread(thread, (LPDWORD)value_ptr)) {
return EINVAL;
}
CloseHandle(thread);
return 0;
}
pthread_t pthread_self(void) {
return GetCurrentThread();
}
static __int64 file_time_to_unix_time (const FILETIME * ft) {
__int64 res = (__int64)ft->dwHighDateTime << 32;
res |= ft->dwLowDateTime;
res /= 10; /* from 100 nano-sec periods to usec */
res -= 11644473600000000u; /* from Win epoch to Unix epoch */
return res;
}
int clock_gettime(clockid_t clock_id, struct timespec * tp) {
FILETIME ft;
__int64 tim;
assert(clock_id == CLOCK_REALTIME);
if (!tp) {
errno = EINVAL;
return -1;
}
GetSystemTimeAsFileTime(&ft);
tim = file_time_to_unix_time(&ft);
tp->tv_sec = (long)(tim / 1000000L);
tp->tv_nsec = (long)(tim % 1000000L) * 1000;
return 0;
}
void usleep(useconds_t useconds) {
Sleep(useconds / 1000);
}
void perror(const char * msg) {
int error = errno;
if (error == ERR_SOCKET) {
fprintf(stderr, "%s: socket error 0x%08x\n", msg, WSAGetLastError());
}
else if (error == ERR_WIN32) {
fprintf(stderr, "%s: Win32 error 0x%08x\n", msg, GetLastError());
}
else {
fprintf(stderr, "%s: %s\n", msg, strerror(error));
}
}
#undef bind
int wsa_bind(int socket, const struct sockaddr * addr, int addr_size) {
int res = 0;
SetLastError(0);
WSASetLastError(0);
res = bind(socket, addr, addr_size);
if (res != 0) {
errno = ERR_SOCKET;
return -1;
}
return 0;
}
#undef socket
int wsa_socket(int af, int type, int protocol) {
int res = 0;
SetLastError(0);
WSASetLastError(0);
res = socket(af, type, protocol);
if (res < 0) {
errno = ERR_SOCKET;
return -1;
}
return res;
}
int truncate(const char * path, int64 size) {
int res = 0;
int f = _open(path, _O_RDWR | _O_BINARY);
if (f < 0) return -1;
res = ftruncate(f, size);
_close(f);
return res;
}
int ftruncate(int fd, int64 size) {
int64 cur, pos;
BOOL ret = FALSE;
HANDLE handle = (HANDLE)_get_osfhandle(fd);
if (handle == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
/* save the current file pointer */
cur = _lseeki64(fd, 0, SEEK_CUR);
if (cur >= 0) {
pos = _lseeki64(fd, size, SEEK_SET);
if (pos >= 0) {
ret = SetEndOfFile(handle);
if (!ret) errno = EBADF;
}
/* restore the file pointer */
_lseeki64(fd, cur, SEEK_SET);
}
return ret ? 0 : -1;
}
DIR * opendir(const char *path) {
DIR * d = (DIR *)malloc(sizeof(DIR));
if (!d) { errno = ENOMEM; return 0; }
strcpy(d->path, path);
strcat(d->path, "/*.*");
d->hdl = -1;
return d;
}
struct dirent * readdir(DIR *d) {
static struct dirent de;
if (d->hdl < 0) {
d->hdl = _findfirsti64(d->path, &d->blk);
if (d->hdl < 0) {
if (errno == ENOENT) errno = 0;
return 0;
}
}
else {
int r = _findnexti64(d->hdl, &d->blk);
if (r < 0) {
if (errno == ENOENT) errno = 0;
return 0;
}
}
strcpy(de.d_name, d->blk.name);
de.d_size = d->blk.size;
de.d_atime = d->blk.time_access;
de.d_ctime = d->blk.time_create;
de.d_wtime = d->blk.time_write;
return &de;
}
int closedir(DIR * d) {
int r = 0;
if (!d) {
errno = EBADF;
return -1;
}
if (d->hdl >= 0) r = _findclose(d->hdl);
free(d);
return r;
}
char * canonicalize_file_name(const char * path) {
char buf[MAX_PATH];
char * basename;
int i = 0;
DWORD len = GetFullPathName(path, sizeof(buf), buf, &basename);
if (len == 0) {
errno = ENOENT;
return NULL;
}
if (len > MAX_PATH - 1) {
errno = ENAMETOOLONG;
return NULL;
}
while (buf[i] != 0) {
if (buf[i] == '\\') buf[i] = '/';
i++;
}
return strdup(buf);
}
int getuid(void) {
/* Windows user is always a superuser :) */
return 0;
}
int geteuid(void) {
return 0;
}
int getgid(void) {
return 0;
}
int getegid(void) {
return 0;
}
char * get_os_name(void) {
static char str[256];
OSVERSIONINFOEX info;
memset(&info, 0, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx((OSVERSIONINFO *)&info);
switch (info.dwMajorVersion) {
case 4:
return "Windows NT";
case 5:
switch (info.dwMinorVersion) {
case 0: return "Windows 2000";
case 1: return "Windows XP";
case 2: return "Windows Server 2003";
}
break;
case 6:
return "Windows Vista";
}
snprintf(str, sizeof(str), "Windows %d.%d", info.dwMajorVersion, info.dwMajorVersion);
return str;
}
char * get_user_home(void) {
static char buf[MAX_PATH];
if (buf[0] != 0) return buf;
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, buf))) return buf;
return NULL;
}
void ini_mdep(void) {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
fprintf(stderr, "Couldn't access winsock.dll.\n");
exit(1);
}
/* Confirm that the Windows Sockets DLL supports 1.1.*/
/* Note that if the DLL supports versions greater */
/* than 1.1 in addition to 1.1, it will still return */
/* 1.1 in wVersion since that is the version we */
/* requested. */
if (LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1) {
fprintf(stderr, "Unacceptable version of winsock.dll.\n");
WSACleanup();
exit(1);
}
}
#elif defined(_WRS_KERNEL)
void usleep(useconds_t useconds) {
struct timespec tv;
tv.tv_sec = useconds / 1000000;
tv.tv_nsec = (useconds % 1000000) * 1000;
nanosleep(&tv, NULL);
}
int truncate(char * path, int64 size) {
int f = open(path, O_RDWR, 0);
if (f < 0) return -1;
if (ftruncate(f, size) < 0) {
int err = errno;
close(f);
errno = err;
return -1;
}
return close(f);
}
char * canonicalize_file_name(const char * path) {
char buf[PATH_MAX];
int i = 0, j = 0;
if (path[0] == '.' && (path[1] == '/' || path[1] == '\\' || path[1] == 0)) {
getcwd(buf, sizeof(buf));
j = strlen(buf);
if (j == 1 && buf[0] == '/') j = 0;
i = 1;
}
else if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\\' || path[2] == 0)) {
getcwd(buf, sizeof(buf));
j = strlen(buf);
while (j > 0 && buf[j - 1] != '/') j--;
if (j > 0 && buf[j - 1] == '/') j--;
i = 2;
}
while (path[i] && j < PATH_MAX - 1) {
char ch = path[i];
if (ch == '\\') ch = '/';
if (ch == '/') {
if (path[i + 1] == '/' || path[i + 1] == '\\') {
i++;
continue;
}
if (path[i + 1] == '.') {
if (path[i + 2] == 0) {
break;
}
if (path[i + 2] == '/' || path[i + 2] == '\\') {
i += 2;
continue;
}
if ((j == 0 || buf[0] == '/') && path[i + 2] == '.') {
if (path[i + 3] == '/' || path[i + 3] == '\\' || path[i + 3] == 0) {
while (j > 0 && buf[j - 1] != '/') j--;
if (j > 0 && buf[j - 1] == '/') j--;
i += 3;
continue;
}
}
}
}
buf[j++] = ch;
i++;
}
if (j == 0 && path[0] != 0) buf[j++] = '/';
buf[j] = 0;
return strdup(buf);
}
int getuid(void) {
return 0;
}
int geteuid(void) {
return 0;
}
int getgid(void) {
return 0;
}
int getegid(void) {
return 0;
}
char * get_os_name(void) {
static char str[256];
snprintf(str, sizeof(str), "VxWorks %s", kernelVersion());
return str;
}
char * get_user_home(void) {
return "/";
}
void ini_mdep(void) {
pthread_attr_init(&pthread_create_attr);
pthread_attr_setstacksize(&pthread_create_attr, 0x4000);
pthread_attr_setname(&pthread_create_attr, "tTcf");
}
#else
#include <pwd.h>
#include <sys/utsname.h>
#include <asm/unistd.h>
char * get_os_name(void) {
static char str[256];
struct utsname info;
memset(&info, 0, sizeof(info));
uname(&info);
assert(strlen(info.sysname) + strlen(info.release) < sizeof(str));
snprintf(str, sizeof(str), "%s %s", info.sysname, info.release);
return str;
}
char * get_user_home(void) {
static char buf[PATH_MAX];
if (buf[0] == 0) {
struct passwd * pwd = getpwuid(getuid());
if (pwd == NULL) return NULL;
strcpy(buf, pwd->pw_dir);
}
return buf;
}
int tkill(pid_t pid, int signal) {
return syscall(__NR_tkill, pid, signal);
}
void ini_mdep(void) {
pthread_attr_init(&pthread_create_attr);
pthread_attr_setstacksize(&pthread_create_attr, 0x8000);
}
#endif