3 Copyright (c) Scott Gasch
11 FreeBSD/Linux/OSX system dependent code.
19 $Id: unix.c 200 2006-07-05 20:05:07Z scott $
25 #include <sys/types.h>
29 #include <sys/select.h>
36 #define SYS_MAX_HEAP_ALLOC_SIZE_BYTES 0xfff
37 #define HEAP 0x48656170
38 #define MMAP 0x4d6d6170
40 pthread_mutex_t g_SystemLock;
41 #define LOCK_SYSTEM pthread_mutex_lock(&g_SystemLock)
42 #define UNLOCK_SYSTEM pthread_mutex_unlock(&g_SystemLock)
45 ULONG g_uTotalAlloced = 0;
47 #define ALLOC_HASH_SIZE 0x4000
49 typedef struct _ALLOC_RECORD
54 ALLOC_RECORD g_AllocHash[ALLOC_HASH_SIZE];
56 #define PTR_TO_ALLOC_HASH(x) ((((ULONG)(x)) >> 3) & (ALLOC_HASH_SIZE - 1))
59 GetHeapMemoryUsage(void)
61 Trace("There are now %u bytes outstanding.\n", g_uTotalAlloced);
62 return(g_uTotalAlloced);
66 ReleaseAllocHashEntry(void *p)
68 ULONG u = PTR_TO_ALLOC_HASH(p);
72 while(g_AllocHash[u].p != p)
75 u &= (ALLOC_HASH_SIZE - 1);
78 g_AllocHash[u].p = NULL;
79 g_uTotalAlloced -= g_AllocHash[u].uSize;
82 // Trace("Freed %u bytes at %p.\n", g_AllocHash[u].uSize, p);
86 MarkAllocHashEntry(void *p, ULONG uSize)
88 ULONG u = PTR_TO_ALLOC_HASH(p);
90 if (uSize > (1024 * 1024 * 30)) return;
93 while(g_AllocHash[u].p != NULL)
96 u &= (ALLOC_HASH_SIZE - 1);
99 g_AllocHash[u].uSize = uSize;
100 g_uTotalAlloced += uSize;
103 // Trace("Alloc %u bytes at %p.\n", g_AllocHash[u].uSize, p);
110 typedef struct _SYSTEM_THREAD_TABLE_ENTRY
113 ULONG uWrapperHandle;
117 THREAD_ENTRYPOINT *pEntry;
120 SYSTEM_THREAD_TABLE_ENTRY;
121 DLIST_ENTRY g_SystemThreadList;
122 ULONG g_uNextThreadHandle = 6000;
125 SystemGetTimeString(void)
130 Get the current time string from the OS. Not thread safe.
143 time_t t = time(NULL);
147 snprintf(buf, ARRAY_LENGTH(buf),
148 "%u:%02u:%02u", p->tm_hour, p->tm_min, p->tm_sec);
153 SystemGetDateString(void)
158 Get the current date string from the OS. Not thread safe.
171 time_t t = time(NULL);
175 snprintf(buf, ARRAY_LENGTH(buf),
176 "%u.%02u.%02u", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday);
184 static SYSTEM_THREAD_TABLE_ENTRY *
185 _SystemGetThreadTableEntry(ULONG uThreadHandle)
190 Support routine for thread functionality wrapper. Given a
191 uThreadHandle look up its entry in the global table.
199 SYSTEM_THREAD_TABLE_ENTRY *
204 SYSTEM_THREAD_TABLE_ENTRY *q;
207 p = g_SystemThreadList.pFlink;
208 while(p != &g_SystemThreadList)
210 q = CONTAINING_STRUCT(p, SYSTEM_THREAD_TABLE_ENTRY, links);
211 if (q->uWrapperHandle == uThreadHandle)
224 SystemThreadEntryPoint(void *pParam)
229 The first code that any newly created thread executes.
241 SYSTEM_THREAD_TABLE_ENTRY *p = (SYSTEM_THREAD_TABLE_ENTRY *)pParam;
248 uParam = p->uThreadParam;
249 i = (int)(*(p->pEntry))(uParam); // call thread's user-supplied entry
254 SystemCreateThread(THREAD_ENTRYPOINT *pEntry, ULONG uParam, ULONG *puHandle)
259 Wraps the OS/Library dependent thread creation call.
263 THREAD_ENTRYPOINT *pEntry : where should the new thread start?
264 ULONG uParam : parameter to pass to the new thread
265 ULONG *puHandle : "handle" to the new thread
273 SYSTEM_THREAD_TABLE_ENTRY *p;
275 p = malloc(sizeof(SYSTEM_THREAD_TABLE_ENTRY));
284 p->uThreadParam = uParam;
285 if (0 != pthread_create(&(p->thread),
287 SystemThreadEntryPoint,
293 *puHandle = p->uWrapperHandle = g_uNextThreadHandle;
294 g_uNextThreadHandle++;
295 InsertHeadList(&g_SystemThreadList, &(p->links));
302 SystemWaitForThreadToExit(ULONG uThreadHandle)
307 Blocks until the thread whose handle is provieded exits.
319 SYSTEM_THREAD_TABLE_ENTRY *q = _SystemGetThreadTableEntry(uThreadHandle);
321 pthread_join(q->thread, NULL);
327 SystemGetThreadExitCode(ULONG uThreadHandle, ULONG *puCode)
332 Get the exit value of an already-exited thread.
345 SYSTEM_THREAD_TABLE_ENTRY *q = _SystemGetThreadTableEntry(uThreadHandle);
348 pthread_join(q->thread, &p);
349 *puCode = ((ULONG)p);
355 SystemDeleteThread(ULONG uThreadHandle)
372 SYSTEM_THREAD_TABLE_ENTRY *q = _SystemGetThreadTableEntry(uThreadHandle);
376 pthread_kill(q->thread, SIGKILL);
378 RemoveEntryList(&(q->links));
389 SystemDebugBreakpoint(void)
394 A hardcoded breakpoint.
410 SystemReadTimeStampCounter(void)
415 Read the processor's timestamp counter.
435 SystemTimeStamp(void)
440 Return a timestamp to the caller.
455 // Number of seconds and microseconds since the Epoch.
457 if (0 != gettimeofday(&tv, NULL))
459 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
460 NULL, "gettimeofday", (void *)errno, NULL,
463 return((double)tv.tv_sec + (double)tv.tv_usec * 1.0e-6);
468 SystemDoesFileExist(CHAR *szFilename)
473 Determine if a file exists (non-atomically).
486 int i = stat(szFilename, &sb);
488 if ((i == -1) && (errno == ENOENT)) return(FALSE);
499 This code is called by SystemDependentInitialization. Its job is to
500 determine whether this process is running with administrative powers.
521 SystemDeferExecution(ULONG dwMs)
538 struct timeval t = { 0, dwMs * 1000 };
539 if (select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &t) < 0)
541 if (usleep(dwMs * 1000) < 0)
543 Bug("SystemDeferExecution: I have insomnia...\n");
550 _SystemCallMmap(ULONG dwSizeBytes)
569 PROT_READ | PROT_WRITE,
573 if (MAP_FAILED == pMem)
575 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
576 NULL, "mmap", (void *)errno, (void *)dwSizeBytes,
579 (void)madvise(pMem, dwSizeBytes, MADV_RANDOM | MADV_WILLNEED);
585 _SystemCallMunmap(void *pMem)
590 Wrapper around munmap
602 if (0 != munmap(pMem, (size_t)-1))
604 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
605 NULL, "munmap", (void *)errno, pMem,
611 static void *_SystemCallMalloc(ULONG dwSizeBytes)
616 Wrapper around malloc.
628 void *p = malloc(dwSizeBytes);
631 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
632 NULL, "malloc", (void *)errno, (void *)dwSizeBytes,
635 memset(p, 0, dwSizeBytes);
641 _SystemCallFree(void *p)
662 SystemStrDup(char *p)
667 Implements strdup functionality.
679 char *q = SystemAllocateMemory(strlen(p) + 1);
685 SystemAllocateMemory(ULONG dwSizeBytes)
690 Allocate some memory.
704 if (0) // (dwSizeBytes > SYS_MAX_HEAP_ALLOC_SIZE_BYTES)
706 p = _SystemCallMmap(dwSizeBytes + sizeof(DWORD));
709 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
710 NULL, "mmap", (void *)errno, NULL,
718 p = _SystemCallMalloc(dwSizeBytes + sizeof(DWORD));
721 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
722 NULL, "malloc", (void *)errno, NULL,
729 MarkAllocHashEntry(p, dwSizeBytes);
736 SystemFreeMemory(void *pMemory)
741 Free an allocation previously allocated by SystemAllocMemory.
757 ReleaseAllocHashEntry(pMemory);
765 _SystemCallMunmap(p);
770 UtilPanic(SHOULD_NOT_GET_HERE,
771 NULL, NULL, NULL, NULL,
779 SystemAllocateLockedMemory(ULONG dwSizeBytes)
784 Allocate non-pagable memory (if possible).
796 void *p = SystemAllocateMemory(dwSizeBytes);
799 if (0 != mlock(p, dwSizeBytes))
802 // Note: don't panic the program over this... do dump a
803 // warning though as it should not happen.
805 Bug("WARNING: mlock for buffer at 0x%p (%u bytes) failed, "
806 "errno=%d\n\n", p, dwSizeBytes, errno);
814 SystemMakeMemoryReadOnly(void *pMemory,
820 Make some memory region read-only (if possible).
833 if (0 != mprotect(pMemory, dwSizeBytes, PROT_READ))
835 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
836 NULL, "mprotect", (void *)errno, (void *)PROT_READ,
845 SystemMakeMemoryReadWrite(void *pMemory, ULONG dwSizeBytes)
850 Make a region of memory read/write.
863 if (0 != mprotect(pMemory, dwSizeBytes, PROT_READ | PROT_WRITE))
865 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
869 (void *)(PROT_READ | PROT_WRITE),
878 SystemCopyFile(CHAR *szSource, CHAR *szDest)
883 Make a copy of a file.
897 CHAR *x = "/bin/cp -f ";
898 ULONG uLen = strlen(szSource) + strlen(szDest) + strlen(x) + 2;
900 buf = SystemAllocateMemory(uLen);
902 "%s%s %s", x, szSource, szDest);
903 if (0 == system(buf))
905 SystemFreeMemory(buf);
908 SystemFreeMemory(buf);
914 SystemDeleteFile(CHAR *szFile)
931 if (0 == unlink(szFile))
939 #define MAX_LOCKS (8)
940 typedef struct _UNIX_LOCK_ENTRY
942 pthread_mutex_t lock;
945 UNIX_LOCK_ENTRY g_rgLockTable[MAX_LOCKS];
948 SystemCreateLock(void)
953 for (u = 0; u < MAX_LOCKS; u++)
955 if (FALSE == g_rgLockTable[u].fInUse)
957 pthread_mutex_init(&(g_rgLockTable[u].lock), NULL);
958 g_rgLockTable[u].fInUse = TRUE;
962 u = (ULONG)-1; // no free slot
970 SystemDeleteLock(ULONG u)
973 if ((u < MAX_LOCKS) && (g_rgLockTable[u].fInUse == TRUE))
975 pthread_mutex_destroy(&(g_rgLockTable[u].lock));
976 g_rgLockTable[u].fInUse = FALSE;
985 SystemBlockingWaitForLock(ULONG u)
987 pthread_mutex_t *p = NULL;
989 if ((u < MAX_LOCKS) && (g_rgLockTable[u].fInUse == TRUE))
991 p = &(g_rgLockTable[u].lock);
996 pthread_mutex_lock(p);
1004 SystemReleaseLock(ULONG u)
1006 pthread_mutex_t *p = NULL;
1008 if ((u < MAX_LOCKS) && (g_rgLockTable[u].fInUse == TRUE))
1010 p = &(g_rgLockTable[u].lock);
1015 pthread_mutex_unlock(p);
1023 int g_rgSemaphores[MAX_SEM];
1025 ULONG g_rgSemValues[MAX_SEM];
1029 SystemCreateSemaphore(ULONG uValue)
1034 struct semid_ds *buf;
1035 unsigned short *array;
1038 if (uValue > MAX_USHORT) return((ULONG)-1);
1040 for (u = 0; u < MAX_SEM; u++)
1042 if (g_rgSemaphores[u] < 0)
1044 g_rgSemaphores[u] = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
1045 if (g_rgSemaphores[u] < 0)
1051 if (semctl(g_rgSemaphores[u], 0, SETVAL, value) < 0)
1053 (void)SystemDeleteSemaphore(u);
1058 g_rgSemValues[u] = uValue;
1059 Trace("Semaphore #%u created, initial value is %u\n",
1072 SystemDeleteSemaphore(ULONG u) {
1075 struct semid_ds *buf;
1076 unsigned short *array;
1080 if ((u < MAX_SEM) && (g_rgSemaphores[u] >= 0))
1082 if (semctl(g_rgSemaphores[u], 0, IPC_RMID, value) < 0)
1086 g_rgSemaphores[u] = -1;
1093 SystemReleaseSemaphoreResource(ULONG u)
1095 struct sembuf operation;
1098 if ((u < MAX_SEM) && (g_rgSemaphores[u] >= 0))
1100 operation.sem_num = u;
1101 operation.sem_op = +1;
1102 operation.sem_flg = 0;
1103 if (semop(g_rgSemaphores[u], &operation, 1) < 0)
1105 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
1106 NULL, "semop", (void *)errno, (void *)0,
1107 __FILE__, __LINE__);
1110 g_rgSemValues[u] += 1;
1111 Trace("Semaphore #%u value incremented to %u\n", u,
1119 SystemObtainSemaphoreResource(ULONG u)
1121 struct sembuf operation;
1124 if ((u < MAX_SEM) && (g_rgSemaphores[u] >= 0))
1126 operation.sem_num = u;
1127 operation.sem_op = -1;
1128 operation.sem_flg = 0;
1130 if (semop(g_rgSemaphores[u], &operation, 1) < 0)
1132 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
1133 NULL, "semop", (void *)errno, (void *)1,
1134 __FILE__, __LINE__);
1137 if (g_rgSemValues[u] > 0)
1139 g_rgSemValues[u] -= 1;
1140 Trace("Semaphore #%u value decremented to %u\n", u,
1151 SystemDependentInitialization(void)
1154 Routine description:
1156 This code is called to initialize operating system dependent code.
1157 It is unsynchronized. It should only ever be called once. It
1158 should definitely never be called by two threads at once!
1166 FLAG : TRUE on success, FALSE otherwise
1172 static FLAG fInit = FALSE;
1176 Bug("SystemDependentInitialization code called more than once!\n");
1182 memset(g_AllocHash, 0, sizeof(g_AllocHash));
1184 for (u = 0; u < MAX_SEM; u++)
1186 g_rgSemaphores[u] = -1;
1188 InitializeListHead(&g_SystemThreadList);
1189 pthread_mutex_init(&g_SystemLock, NULL);