/SDK/core/Remotery.c
C | 5720 lines | 3589 code | 1299 blank | 832 comment | 907 complexity | 52f5f7e2b8cf777215495d273d64de3d MD5 | raw file
Possible License(s): BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- //
- // Copyright 2014 Celtoys Ltd
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- /*
- @Contents:
- @DEPS: External Dependencies
- @TIMERS: Platform-specific timers
- @TLS: Thread-Local Storage
- @ATOMIC: Atomic Operations
- @VMBUFFER: Mirror Buffer using Virtual Memory for auto-wrap
- @THREADS: Threads
- @SAFEC: Safe C Library excerpts
- @OBJALLOC: Reusable Object Allocator
- @DYNBUF: Dynamic Buffer
- @SOCKETS: Sockets TCP/IP Wrapper
- @SHA1: SHA-1 Cryptographic Hash Function
- @BASE64: Base-64 encoder
- @MURMURHASH: Murmur-Hash 3
- @WEBSOCKETS: WebSockets
- @MESSAGEQ: Multiple producer, single consumer message queue
- @NETWORK: Network Server
- @JSON: Basic, text-based JSON serialisation
- @SAMPLE: Base Sample Description (CPU by default)
- @SAMPLETREE: A tree of samples with their allocator
- @TSAMPLER: Per-Thread Sampler
- @REMOTERY: Remotery
- @CUDA: CUDA event sampling
- @D3D11: Direct3D 11 event sampling
- @OPENGL: OpenGL event sampling
- */
- #include "Remotery.h"
- #pragma comment(lib, "ws2_32.lib")
- #ifdef RMT_ENABLED
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @DEPS: External Dependencies
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- //
- // Required CRT dependencies
- //
- #ifdef RMT_USE_TINYCRT
- #include <TinyCRT/TinyCRT.h>
- #include <TinyCRT/TinyWinsock.h>
- #define CreateFileMapping CreateFileMappingA
- #else
- #ifdef RMT_PLATFORM_MACOS
- #include <mach/mach_time.h>
- #include <mach/vm_map.h>
- #include <mach/mach.h>
- #include <sys/time.h>
- #else
- #include <malloc.h>
- #endif
- #include <assert.h>
- #ifdef RMT_PLATFORM_WINDOWS
- #include <winsock2.h>
- #include <intrin.h>
- #undef min
- #undef max
- #endif
- #ifdef RMT_PLATFORM_LINUX
- #include <time.h>
- #endif
- #if defined(RMT_PLATFORM_POSIX)
- #include <stdlib.h>
- #include <pthread.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/mman.h>
- #include <netinet/in.h>
- #include <fcntl.h>
- #include <errno.h>
- #endif
- #endif
- #ifdef __cplusplus
- #define RMT_UNREFERENCED_PARAMETER( x ) ( &reinterpret_cast< const int& >( x ) )
- #else
- #define RMT_UNREFERENCED_PARAMETER( x ) ( (void)x )
- #endif
- #ifdef RMT_USE_CUDA
- #include <cuda.h>
- #endif
- rmtU64 min(rmtS64 a, rmtS64 b)
- {
- return a < b ? a : b;
- }
- rmtU64 max(rmtS64 a, rmtS64 b)
- {
- return a > b ? a : b;
- }
- // Config
- // TODO: Expose to user
- // How long to sleep between server updates, hopefully trying to give
- // a little CPU back to other threads.
- static const rmtU32 MS_SLEEP_BETWEEN_SERVER_UPDATES = 10;
- // Will be rounded to page granularity of 64k
- static const rmtU32 MESSAGE_QUEUE_SIZE_BYTES = 64 * 1024;
- // If the user continuously pushes to the message queue, the server network
- // code won't get a chance to update unless there's an upper-limit on how
- // many messages can be consumed per loop.
- static const rmtU32 MAX_NB_MESSAGES_PER_UPDATE = 100;
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @TIMERS: Platform-specific timers
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- //
- // Get millisecond timer value that has only one guarantee: multiple calls are consistently comparable.
- // On some platforms, even though this returns milliseconds, the timer may be far less accurate.
- //
- static rmtU32 msTimer_Get()
- {
- #ifdef RMT_PLATFORM_WINDOWS
- return (rmtU32)GetTickCount();
- #else
- clock_t time = clock();
- rmtU32 msTime = (rmtU32) (time / (CLOCKS_PER_SEC / 1000));
- return msTime;
- #endif
- }
- //
- // Micro-second accuracy high performance counter
- //
- #ifndef RMT_PLATFORM_WINDOWS
- typedef rmtU64 LARGE_INTEGER;
- #endif
- typedef struct
- {
- LARGE_INTEGER counter_start;
- double counter_scale;
- } usTimer;
- static void usTimer_Init(usTimer* timer)
- {
- #if defined(RMT_PLATFORM_WINDOWS)
- LARGE_INTEGER performance_frequency;
- assert(timer != NULL);
- // Calculate the scale from performance counter to microseconds
- QueryPerformanceFrequency(&performance_frequency);
- timer->counter_scale = 1000000.0 / performance_frequency.QuadPart;
- // Record the offset for each read of the counter
- QueryPerformanceCounter(&timer->counter_start);
- #elif defined(RMT_PLATFORM_MACOS)
- mach_timebase_info_data_t nsScale;
- mach_timebase_info( &nsScale );
- const double ns_per_us = 1.0e3;
- timer->counter_scale = (double)(nsScale.numer) / ((double)nsScale.denom * ns_per_us);
- timer->counter_start = mach_absolute_time();
- #elif defined(RMT_PLATFORM_LINUX)
- struct timespec tv;
- clock_gettime(CLOCK_REALTIME, &tv);
- timer->counter_start = tv.tv_nsec;
- #endif
- }
- static rmtU64 usTimer_Get(usTimer* timer)
- {
- #if defined(RMT_PLATFORM_WINDOWS)
- LARGE_INTEGER performance_count;
- assert(timer != NULL);
- // Read counter and convert to microseconds
- QueryPerformanceCounter(&performance_count);
- return (rmtU64)((performance_count.QuadPart - timer->counter_start.QuadPart) * timer->counter_scale);
- #elif defined(RMT_PLATFORM_MACOS)
- rmtU64 curr_time = mach_absolute_time();
- return (rmtU64)((curr_time - timer->counter_start) * timer->counter_scale);
- #elif defined(RMT_PLATFORM_LINUX)
- struct timespec tv;
- clock_gettime(CLOCK_REALTIME, &tv);
- return tv.tv_nsec - timer->counter_start;
- #endif
- }
- static void msSleep(rmtU32 time_ms)
- {
- #ifdef RMT_PLATFORM_WINDOWS
- Sleep(time_ms);
- #elif defined(RMT_PLATFORM_POSIX)
- usleep(time_ms * 1000);
- #endif
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @TLS: Thread-Local Storage
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- #define TLS_INVALID_HANDLE 0xFFFFFFFF
- #if defined(RMT_PLATFORM_WINDOWS)
- typedef rmtU32 rmtTLS;
- #else
- typedef pthread_key_t rmtTLS;
- #endif
- static enum rmtError tlsAlloc(rmtTLS* handle)
- {
- assert(handle != NULL);
- #if defined(RMT_PLATFORM_WINDOWS)
- *handle = (rmtTLS)TlsAlloc();
- if (*handle == TLS_OUT_OF_INDEXES)
- {
- *handle = TLS_INVALID_HANDLE;
- return RMT_ERROR_TLS_ALLOC_FAIL;
- }
- #elif defined(RMT_PLATFORM_POSIX)
- if (pthread_key_create(handle, NULL) != 0)
- {
- *handle = TLS_INVALID_HANDLE;
- return RMT_ERROR_TLS_ALLOC_FAIL;
- }
- #endif
- return RMT_ERROR_NONE;
- }
- static void tlsFree(rmtTLS handle)
- {
- assert(handle != TLS_INVALID_HANDLE);
- #if defined(RMT_PLATFORM_WINDOWS)
- TlsFree(handle);
- #elif defined(RMT_PLATFORM_POSIX)
- pthread_key_delete((pthread_key_t)handle);
- #endif
- }
- static void tlsSet(rmtTLS handle, void* value)
- {
- assert(handle != TLS_INVALID_HANDLE);
- #if defined(RMT_PLATFORM_WINDOWS)
- TlsSetValue(handle, value);
- #elif defined(RMT_PLATFORM_POSIX)
- pthread_setspecific((pthread_key_t)handle, value);
- #endif
- }
- static void* tlsGet(rmtTLS handle)
- {
- assert(handle != TLS_INVALID_HANDLE);
- #if defined(RMT_PLATFORM_WINDOWS)
- return TlsGetValue(handle);
- #elif defined(RMT_PLATFORM_POSIX)
- return pthread_getspecific((pthread_key_t)handle);
- #endif
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @ATOMIC: Atomic Operations
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- static rmtBool AtomicCompareAndSwap(rmtU32 volatile* val, long old_val, long new_val)
- {
- #if defined(RMT_PLATFORM_WINDOWS)
- return _InterlockedCompareExchange((long volatile*)val, new_val, old_val) == old_val ? RMT_TRUE : RMT_FALSE;
- #elif defined(RMT_PLATFORM_POSIX)
- return __sync_bool_compare_and_swap(val, old_val, new_val) ? RMT_TRUE : RMT_FALSE;
- #endif
- }
- static rmtBool AtomicCompareAndSwapPointer(long* volatile* ptr, long* old_ptr, long* new_ptr)
- {
- #if defined(RMT_PLATFORM_WINDOWS)
- #ifdef _WIN64
- return _InterlockedCompareExchange64((__int64 volatile*)ptr, (__int64)new_ptr, (__int64)old_ptr) == (__int64)old_ptr ? RMT_TRUE : RMT_FALSE;
- #else
- return _InterlockedCompareExchange((long volatile*)ptr, (long)new_ptr, (long)old_ptr) == (long)old_ptr ? RMT_TRUE : RMT_FALSE;
- #endif
- #elif defined(RMT_PLATFORM_POSIX)
- return __sync_bool_compare_and_swap(ptr, old_ptr, new_ptr) ? RMT_TRUE : RMT_FALSE;
- #endif
- }
- //
- // NOTE: Does not guarantee a memory barrier
- // TODO: Make sure all platforms don't insert a memory barrier as this is only for stats
- // Alternatively, add strong/weak memory order equivalents
- //
- static void AtomicAdd(rmtS32 volatile* value, rmtS32 add)
- {
- #if defined(RMT_PLATFORM_WINDOWS)
- _InterlockedExchangeAdd((long volatile*)value, (long)add);
- #elif defined(RMT_PLATFORM_POSIX)
- __sync_fetch_and_add(value, add);
- #endif
- }
- static void AtomicSub(rmtS32 volatile* value, rmtS32 sub)
- {
- // Not all platforms have an implementation so just negate and add
- AtomicAdd(value, -sub);
- }
- // Compiler read/write fences (windows implementation)
- static void ReadFence()
- {
- #ifdef RMT_PLATFORM_WINDOWS
- _ReadBarrier();
- #else
- asm volatile ("" : : : "memory");
- #endif
- }
- static void WriteFence()
- {
- #ifdef RMT_PLATFORM_WINDOWS
- _WriteBarrier();
- #else
- asm volatile ("" : : : "memory");
- #endif
- }
- // Get a shared value with acquire semantics, ensuring the read is complete
- // before the function returns.
- static void* LoadAcquire(void* volatile const* addr)
- {
- // Hardware fence is implicit on x86 so only need the compiler fence
- void* v = *addr;
- ReadFence();
- return v;
- }
- // Set a shared value with release semantics, ensuring any prior writes
- // are complete before the value is set.
- static void StoreRelease(void* volatile* addr, void* v)
- {
- // Hardware fence is implicit on x86 so only need the compiler fence
- WriteFence();
- *addr = v;
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @VMBUFFER: Mirror Buffer using Virtual Memory for auto-wrap
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- typedef struct VirtualMirrorBuffer
- {
- // Page-rounded size of the buffer without mirroring
- rmtU32 size;
- // Pointer to the first part of the mirror
- // The second part comes directly after at ptr+size bytes
- rmtU8* ptr;
- #ifdef RMT_PLATFORM_WINDOWS
- HANDLE file_map_handle;
- #endif
- } VirtualMirrorBuffer;
- static void VirtualMirrorBuffer_Destroy(VirtualMirrorBuffer* buffer)
- {
- assert(buffer != 0);
- #ifdef RMT_PLATFORM_WINDOWS
- if (buffer->file_map_handle != NULL)
- {
- CloseHandle(buffer->file_map_handle);
- buffer->file_map_handle = NULL;
- }
- #endif
- #ifdef RMT_PLATFORM_MACOS
- if (buffer->ptr != NULL)
- vm_deallocate(mach_task_self(), (vm_address_t)buffer->ptr, buffer->size * 2);
- #endif
- #ifdef RMT_PLATFORM_LINUX
- if (buffer->ptr != NULL)
- munmap(buffer->ptr, buffer->size * 2);
- #endif
- buffer->ptr = NULL;
- free(buffer);
- }
- static enum rmtError VirtualMirrorBuffer_Create(VirtualMirrorBuffer** buffer, rmtU32 size, int nb_attempts)
- {
- static const rmtU32 k_64 = 64 * 1024;
- #ifdef RMT_PLATFORM_LINUX
- char path[] = "/dev/shm/ring-buffer-XXXXXX";
- int file_descriptor;
- #endif
- assert(buffer != 0);
- // Allocate container
- *buffer = (VirtualMirrorBuffer*)malloc(sizeof(VirtualMirrorBuffer));
- if (*buffer == 0)
- return RMT_ERROR_MALLOC_FAIL;
- // Round up to page-granulation; the nearest 64k boundary for now
- size = (size + k_64 - 1) / k_64 * k_64;
- // Set defaults
- (*buffer)->size = size;
- (*buffer)->ptr = NULL;
- #ifdef RMT_PLATFORM_WINDOWS
- (*buffer)->file_map_handle = INVALID_HANDLE_VALUE;
- #endif
- #ifdef RMT_PLATFORM_WINDOWS
- // Windows version based on https://gist.github.com/rygorous/3158316
- while (nb_attempts-- > 0)
- {
- rmtU8* desired_addr;
- // Create a file mapping for pointing to its physical address with multiple virtual pages
- (*buffer)->file_map_handle = CreateFileMapping(
- INVALID_HANDLE_VALUE,
- 0,
- PAGE_READWRITE,
- 0,
- size,
- 0);
- if ((*buffer)->file_map_handle == NULL)
- break;
- // Reserve two contiguous pages of virtual memory
- desired_addr = (rmtU8*)VirtualAlloc(0, size * 2, MEM_RESERVE, PAGE_NOACCESS);
- if (desired_addr == NULL)
- break;
- // Release the range immediately but retain the address for the next sequence of code to
- // try and map to it. In the mean-time some other OS thread may come along and allocate this
- // address range from underneath us so multiple attempts need to be made.
- VirtualFree(desired_addr, 0, MEM_RELEASE);
- // Immediately try to point both pages at the file mapping
- if (MapViewOfFileEx((*buffer)->file_map_handle, FILE_MAP_ALL_ACCESS, 0, 0, size, desired_addr) == desired_addr &&
- MapViewOfFileEx((*buffer)->file_map_handle, FILE_MAP_ALL_ACCESS, 0, 0, size, desired_addr + size) == desired_addr + size)
- {
- (*buffer)->ptr = desired_addr;
- break;
- }
- // Failed to map the virtual pages; cleanup and try again
- CloseHandle((*buffer)->file_map_handle);
- (*buffer)->file_map_handle = NULL;
- }
- #endif
- #ifdef RMT_PLATFORM_MACOS
- //
- // Mac version based on https://github.com/mikeash/MAMirroredQueue
- //
- // Copyright (c) 2010, Michael Ash
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
- // the following conditions are met:
- //
- // Redistributions of source code must retain the above copyright notice, this list of conditions and the following
- // disclaimer.
- //
- // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
- // following disclaimer in the documentation and/or other materials provided with the distribution.
- // Neither the name of Michael Ash nor the names of its contributors may be used to endorse or promote products
- // derived from this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- //
- while (nb_attempts-- > 0)
- {
- vm_prot_t cur_prot, max_prot;
- kern_return_t mach_error;
- rmtU8* ptr = NULL;
- rmtU8* target = NULL;
- // Allocate 2 contiguous pages of virtual memory
- if (vm_allocate(mach_task_self(), (vm_address_t*)&ptr, size * 2, VM_FLAGS_ANYWHERE) != KERN_SUCCESS)
- break;
- // Try to deallocate the last page, leaving its virtual memory address free
- target = ptr + size;
- if (vm_deallocate(mach_task_self(), (vm_address_t)target, size) != KERN_SUCCESS)
- {
- vm_deallocate(mach_task_self(), (vm_address_t)ptr, size * 2);
- break;
- }
- // Attempt to remap the page just deallocated to the buffer again
- mach_error = vm_remap(
- mach_task_self(),
- (vm_address_t*)&target,
- size,
- 0, // mask
- 0, // anywhere
- mach_task_self(),
- (vm_address_t)ptr,
- 0, //copy
- &cur_prot,
- &max_prot,
- VM_INHERIT_COPY);
- if (mach_error == KERN_NO_SPACE)
- {
- // Failed on this pass, cleanup and make another attempt
- if (vm_deallocate(mach_task_self(), (vm_address_t)ptr, size) != KERN_SUCCESS)
- break;
- }
- else if (mach_error == KERN_SUCCESS)
- {
- // Leave the loop on success
- (*buffer)->ptr = ptr;
- break;
- }
- else
- {
- // Unknown error, can't recover
- vm_deallocate(mach_task_self(), (vm_address_t)ptr, size);
- break;
- }
- }
- #endif
- #ifdef RMT_PLATFORM_LINUX
- // Linux version based on now-defunct Wikipedia section http://en.wikipedia.org/w/index.php?title=Circular_buffer&oldid=600431497
- // Create a unique temporary filename in the shared memory folder
- file_descriptor = mkstemp(path);
- if (file_descriptor < 0)
- {
- VirtualMirrorBuffer_Destroy(*buffer);
- return RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL;
- }
- // Delete the name
- if (unlink(path))
- {
- VirtualMirrorBuffer_Destroy(*buffer);
- return RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL;
- }
- // Set the file size to twice the buffer size
- // TODO: this 2x behaviour can be avoided with similar solution to Win/Mac
- if (ftruncate (file_descriptor, size * 2))
- {
- VirtualMirrorBuffer_Destroy(*buffer);
- return RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL;
- }
- // Map 2 contiguous pages
- (*buffer)->ptr = mmap(NULL, size * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- if ((*buffer)->ptr == MAP_FAILED)
- {
- VirtualMirrorBuffer_Destroy(*buffer);
- return RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL;
- }
- // Point both pages to the same memory file
- if (mmap((*buffer)->ptr, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, file_descriptor, 0) != (*buffer)->ptr ||
- mmap((*buffer)->ptr + size, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, file_descriptor, 0) != (*buffer)->ptr + size)
- {
- VirtualMirrorBuffer_Destroy(*buffer);
- return RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL;
- }
- #endif
- // Cleanup if exceeded number of attempts or failed
- if ((*buffer)->ptr == NULL)
- {
- VirtualMirrorBuffer_Destroy(*buffer);
- return RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL;
- }
- return RMT_ERROR_NONE;
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @THREADS: Threads
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- // comp.lang.c FAQ 4.13 : http://c-faq.com/ptrs/generic.html
- // Should not store a funcptr in a void*, but any funcptr type will do
- typedef int(*FuncPtr)();
- typedef struct
- {
- // OS-specific data
- #if defined(RMT_PLATFORM_WINDOWS)
- HANDLE handle;
- #else
- pthread_t handle;
- #endif
- // Callback executed when the thread is created
- FuncPtr callback;
- // Caller-specified parameter passed to Thread_Create
- void* param;
- // Error state returned from callback
- enum rmtError error;
- // External threads can set this to request an exit
- volatile rmtBool request_exit;
- } Thread;
- typedef enum rmtError (*ThreadProc)(Thread* thread);
- #if defined(RMT_PLATFORM_WINDOWS)
- static DWORD WINAPI ThreadProcWindows(LPVOID lpParameter)
- {
- Thread* thread = (Thread*)lpParameter;
- assert(thread != NULL);
- thread->error = ((ThreadProc)thread->callback)(thread);
- return thread->error == RMT_ERROR_NONE ? 1 : 0;
- }
- #else
- static void* StartFunc( void* pArgs )
- {
- Thread* thread = (Thread*)pArgs;
- assert(thread != NULL);
- thread->error = ((ThreadProc)thread->callback)(thread);
- return NULL; // returned error not use, check thread->error.
- }
- #endif
- static void Thread_Destroy(Thread* thread);
- static int Thread_Valid(Thread* thread)
- {
- assert(thread != NULL);
- #if defined(RMT_PLATFORM_WINDOWS)
- return thread->handle != NULL;
- #else
- return pthread_equal(thread->handle, pthread_self());
- #endif
- }
- static enum rmtError Thread_Create(Thread** thread, ThreadProc callback, void* param)
- {
- assert(thread != NULL);
- // Allocate space for the thread data
- *thread = (Thread*)malloc(sizeof(Thread));
- if (*thread == NULL)
- return RMT_ERROR_MALLOC_FAIL;
- (*thread)->callback = (FuncPtr)callback;
- (*thread)->param = param;
- (*thread)->error = RMT_ERROR_NONE;
- (*thread)->request_exit = RMT_FALSE;
- // OS-specific thread creation
- #if defined (RMT_PLATFORM_WINDOWS)
- (*thread)->handle = CreateThread(
- NULL, // lpThreadAttributes
- 0, // dwStackSize
- ThreadProcWindows, // lpStartAddress
- *thread, // lpParameter
- 0, // dwCreationFlags
- NULL); // lpThreadId
- if ((*thread)->handle == NULL)
- {
- Thread_Destroy(*thread);
- *thread = NULL;
- return RMT_ERROR_CREATE_THREAD_FAIL;
- }
- #else
- int32_t error = pthread_create( &(*thread)->handle, NULL, StartFunc, *thread );
- if (error)
- {
- // Contents of 'thread' parameter to pthread_create() are undefined after
- // failure call so can't pre-set to invalid value before hand.
- (*thread)->handle = pthread_self();
- Thread_Destroy(*thread);
- *thread = NULL;
- return RMT_ERROR_CREATE_THREAD_FAIL;
- }
- #endif
- return RMT_ERROR_NONE;
- }
- static void Thread_RequestExit(Thread* thread)
- {
- // Not really worried about memory barriers or delayed visibility to the target thread
- assert(thread != NULL);
- thread->request_exit = RMT_TRUE;
- }
- static void Thread_Join(Thread* thread)
- {
- assert(Thread_Valid(thread));
- #if defined(RMT_PLATFORM_WINDOWS)
- WaitForSingleObject(thread->handle, INFINITE);
- #else
- pthread_join(thread->handle, NULL);
- #endif
- }
- static void Thread_Destroy(Thread* thread)
- {
- assert(thread != NULL);
- if (Thread_Valid(thread))
- {
- // Shutdown the thread
- Thread_RequestExit(thread);
- Thread_Join(thread);
- // OS-specific release of thread resources
- #if defined(RMT_PLATFORM_WINDOWS)
- CloseHandle(thread->handle);
- thread->handle = NULL;
- #endif
- }
- free(thread);
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @SAFEC: Safe C Library excerpts
- http://sourceforge.net/projects/safeclib/
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- /*------------------------------------------------------------------
- *
- * November 2008, Bo Berry
- *
- * Copyright (c) 2008-2011 by Cisco Systems, Inc
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *------------------------------------------------------------------
- */
- // NOTE: Microsoft also has its own version of these functions so I'm do some hacky PP to remove them
- #define strnlen_s strnlen_s_safe_c
- #define strncat_s strncat_s_safe_c
- #define RSIZE_MAX_STR (4UL << 10) /* 4KB */
- #define RCNEGATE(x) x
- #define EOK ( 0 )
- #define ESNULLP ( 400 ) /* null ptr */
- #define ESZEROL ( 401 ) /* length is zero */
- #define ESLEMAX ( 403 ) /* length exceeds max */
- #define ESOVRLP ( 404 ) /* overlap undefined */
- #define ESNOSPC ( 406 ) /* not enough space for s2 */
- #define ESUNTERM ( 407 ) /* unterminated string */
- #define ESNOTFND ( 409 ) /* not found */
- #ifndef _ERRNO_T_DEFINED
- #define _ERRNO_T_DEFINED
- typedef int errno_t;
- #endif
- #if !defined(_WIN64) && !defined(__APPLE__)
- typedef unsigned int rsize_t;
- #endif
- static rsize_t
- strnlen_s (const char *dest, rsize_t dmax)
- {
- rsize_t count;
- if (dest == NULL) {
- return RCNEGATE(0);
- }
- if (dmax == 0) {
- return RCNEGATE(0);
- }
- if (dmax > RSIZE_MAX_STR) {
- return RCNEGATE(0);
- }
- count = 0;
- while (*dest && dmax) {
- count++;
- dmax--;
- dest++;
- }
- return RCNEGATE(count);
- }
- static errno_t
- strstr_s (char *dest, rsize_t dmax,
- const char *src, rsize_t slen, char **substring)
- {
- rsize_t len;
- rsize_t dlen;
- int i;
- if (substring == NULL) {
- return RCNEGATE(ESNULLP);
- }
- *substring = NULL;
- if (dest == NULL) {
- return RCNEGATE(ESNULLP);
- }
- if (dmax == 0) {
- return RCNEGATE(ESZEROL);
- }
- if (dmax > RSIZE_MAX_STR) {
- return RCNEGATE(ESLEMAX);
- }
- if (src == NULL) {
- return RCNEGATE(ESNULLP);
- }
- if (slen == 0) {
- return RCNEGATE(ESZEROL);
- }
- if (slen > RSIZE_MAX_STR) {
- return RCNEGATE(ESLEMAX);
- }
- /*
- * src points to a string with zero length, or
- * src equals dest, return dest
- */
- if (*src == '\0' || dest == src) {
- *substring = dest;
- return RCNEGATE(EOK);
- }
- while (*dest && dmax) {
- i = 0;
- len = slen;
- dlen = dmax;
- while (src[i] && dlen) {
- /* not a match, not a substring */
- if (dest[i] != src[i]) {
- break;
- }
- /* move to the next char */
- i++;
- len--;
- dlen--;
- if (src[i] == '\0' || !len) {
- *substring = dest;
- return RCNEGATE(EOK);
- }
- }
- dest++;
- dmax--;
- }
- /*
- * substring was not found, return NULL
- */
- *substring = NULL;
- return RCNEGATE(ESNOTFND);
- }
- static errno_t
- strncat_s (char *dest, rsize_t dmax, const char *src, rsize_t slen)
- {
- rsize_t orig_dmax;
- char *orig_dest;
- const char *overlap_bumper;
- if (dest == NULL) {
- return RCNEGATE(ESNULLP);
- }
- if (src == NULL) {
- return RCNEGATE(ESNULLP);
- }
- if (slen > RSIZE_MAX_STR) {
- return RCNEGATE(ESLEMAX);
- }
- if (dmax == 0) {
- return RCNEGATE(ESZEROL);
- }
- if (dmax > RSIZE_MAX_STR) {
- return RCNEGATE(ESLEMAX);
- }
- /* hold base of dest in case src was not copied */
- orig_dmax = dmax;
- orig_dest = dest;
- if (dest < src) {
- overlap_bumper = src;
- /* Find the end of dest */
- while (*dest != '\0') {
- if (dest == overlap_bumper) {
- return RCNEGATE(ESOVRLP);
- }
- dest++;
- dmax--;
- if (dmax == 0) {
- return RCNEGATE(ESUNTERM);
- }
- }
- while (dmax > 0) {
- if (dest == overlap_bumper) {
- return RCNEGATE(ESOVRLP);
- }
- /*
- * Copying truncated before the source null is encountered
- */
- if (slen == 0) {
- *dest = '\0';
- return RCNEGATE(EOK);
- }
- *dest = *src;
- if (*dest == '\0') {
- return RCNEGATE(EOK);
- }
- dmax--;
- slen--;
- dest++;
- src++;
- }
- } else {
- overlap_bumper = dest;
- /* Find the end of dest */
- while (*dest != '\0') {
- /*
- * NOTE: no need to check for overlap here since src comes first
- * in memory and we're not incrementing src here.
- */
- dest++;
- dmax--;
- if (dmax == 0) {
- return RCNEGATE(ESUNTERM);
- }
- }
- while (dmax > 0) {
- if (src == overlap_bumper) {
- return RCNEGATE(ESOVRLP);
- }
- /*
- * Copying truncated
- */
- if (slen == 0) {
- *dest = '\0';
- return RCNEGATE(EOK);
- }
- *dest = *src;
- if (*dest == '\0') {
- return RCNEGATE(EOK);
- }
- dmax--;
- slen--;
- dest++;
- src++;
- }
- }
- /*
- * the entire src was not copied, so the string will be nulled.
- */
- return RCNEGATE(ESNOSPC);
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @OBJALLOC: Reusable Object Allocator
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- //
- // All objects that require free-list-backed allocation need to inherit from this type.
- //
- typedef struct ObjectLink
- {
- struct ObjectLink* volatile next;
- } ObjectLink;
- static void ObjectLink_Constructor(ObjectLink* link)
- {
- assert(link != NULL);
- link->next = NULL;
- }
- typedef enum rmtError (*ObjConstructor)(void*);
- typedef void (*ObjDestructor)(void*);
- typedef struct
- {
- // Object create/destroy parameters
- rmtU32 object_size;
- ObjConstructor constructor;
- ObjDestructor destructor;
- // Number of objects in the free list
- volatile rmtS32 nb_free;
- // Number of objects used by callers
- volatile rmtS32 nb_inuse;
- // Total allocation count
- volatile rmtS32 nb_allocated;
- ObjectLink* first_free;
- } ObjectAllocator;
- static enum rmtError ObjectAllocator_Create(ObjectAllocator** allocator, rmtU32 object_size, ObjConstructor constructor, ObjDestructor destructor)
- {
- // Allocate space for the allocator
- assert(allocator != NULL);
- *allocator = (ObjectAllocator*)malloc(sizeof(ObjectAllocator));
- if (*allocator == NULL)
- return RMT_ERROR_MALLOC_FAIL;
- // Construct it
- (*allocator)->object_size = object_size;
- (*allocator)->constructor = constructor;
- (*allocator)->destructor = destructor;
- (*allocator)->nb_free = 0;
- (*allocator)->nb_inuse = 0;
- (*allocator)->nb_allocated = 0;
- (*allocator)->first_free = NULL;
- return RMT_ERROR_NONE;
- }
- static void ObjectAllocator_Push(ObjectAllocator* allocator, ObjectLink* start, ObjectLink* end)
- {
- assert(allocator != NULL);
- assert(start != NULL);
- assert(end != NULL);
- // CAS pop add range to the front of the list
- while (1)
- {
- ObjectLink* old_link = (ObjectLink*)allocator->first_free;
- end->next = old_link;
- if (AtomicCompareAndSwapPointer((long* volatile*)&allocator->first_free, (long*)old_link, (long*)start) == RMT_TRUE)
- break;
- }
- }
- static ObjectLink* ObjectAllocator_Pop(ObjectAllocator* allocator)
- {
- ObjectLink* link;
- assert(allocator != NULL);
- assert(allocator->first_free != NULL);
- // CAS pop from the front of the list
- while (1)
- {
- ObjectLink* old_link = (ObjectLink*)allocator->first_free;
- ObjectLink* next_link = old_link->next;
- if (AtomicCompareAndSwapPointer((long* volatile*)&allocator->first_free, (long*)old_link, (long*)next_link) == RMT_TRUE)
- {
- link = old_link;
- break;
- }
- }
- link->next = NULL;
- return link;
- }
- static enum rmtError ObjectAllocator_Alloc(ObjectAllocator* allocator, void** object)
- {
- // This function only calls the object constructor on initial malloc of an object
- assert(allocator != NULL);
- assert(object != NULL);
- // Has the free list run out?
- if (allocator->first_free == NULL)
- {
- enum rmtError error;
- // Allocate/construct a new object
- void* free_object = malloc(allocator->object_size);
- if (free_object == NULL)
- return RMT_ERROR_MALLOC_FAIL;
- assert(allocator->constructor != NULL);
- error = allocator->constructor(free_object);
- if (error != RMT_ERROR_NONE)
- {
- // Auto-teardown on failure
- assert(allocator->destructor != NULL);
- allocator->destructor(free_object);
- free(free_object);
- return error;
- }
- // Add to the free list
- ObjectAllocator_Push(allocator, (ObjectLink*)free_object, (ObjectLink*)free_object);
- AtomicAdd(&allocator->nb_allocated, 1);
- AtomicAdd(&allocator->nb_free, 1);
- }
- // Pull available objects from the free list
- *object = ObjectAllocator_Pop(allocator);
- AtomicSub(&allocator->nb_free, 1);
- AtomicAdd(&allocator->nb_inuse, 1);
- return RMT_ERROR_NONE;
- }
- static void ObjectAllocator_Free(ObjectAllocator* allocator, void* object)
- {
- // Add back to the free-list
- assert(allocator != NULL);
- ObjectAllocator_Push(allocator, (ObjectLink*)object, (ObjectLink*)object);
- AtomicSub(&allocator->nb_inuse, 1);
- AtomicAdd(&allocator->nb_free, 1);
- }
- static void ObjectAllocator_FreeRange(ObjectAllocator* allocator, void* start, void* end, rmtU32 count)
- {
- assert(allocator != NULL);
- ObjectAllocator_Push(allocator, (ObjectLink*)start, (ObjectLink*)end);
- AtomicSub(&allocator->nb_inuse, count);
- AtomicAdd(&allocator->nb_free, count);
- }
- static void ObjectAllocator_Destroy(ObjectAllocator* allocator)
- {
- // Ensure everything has been released to the allocator
- assert(allocator != NULL);
- assert(allocator->nb_inuse == 0);
- // Destroy all objects released to the allocator
- assert(allocator != NULL);
- while (allocator->first_free != NULL)
- {
- ObjectLink* next = allocator->first_free->next;
- assert(allocator->destructor != NULL);
- allocator->destructor(allocator->first_free);
- free(allocator->first_free);
- allocator->first_free = next;
- }
- free(allocator);
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @DYNBUF: Dynamic Buffer
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- typedef struct
- {
- rmtU32 alloc_granularity;
- rmtU32 bytes_allocated;
- rmtU32 bytes_used;
- rmtU8* data;
- } Buffer;
- static enum rmtError Buffer_Create(Buffer** buffer, rmtU32 alloc_granularity)
- {
- assert(buffer != NULL);
- // Allocate and set defaults as nothing allocated
- *buffer = (Buffer*)malloc(sizeof(Buffer));
- if (*buffer == NULL)
- return RMT_ERROR_MALLOC_FAIL;
- (*buffer)->alloc_granularity = alloc_granularity;
- (*buffer)->bytes_allocated = 0;
- (*buffer)->bytes_used = 0;
- (*buffer)->data = NULL;
- return RMT_ERROR_NONE;
- }
- static void Buffer_Destroy(Buffer* buffer)
- {
- assert(buffer != NULL);
- if (buffer->data != NULL)
- {
- free(buffer->data);
- buffer->data = NULL;
- }
- free(buffer);
- }
- static enum rmtError Buffer_Write(Buffer* buffer, void* data, rmtU32 length)
- {
- assert(buffer != NULL);
- // Reallocate the buffer on overflow
- if (buffer->bytes_used + length > buffer->bytes_allocated)
- {
- // Calculate size increase rounded up to the requested allocation granularity
- rmtU32 g = buffer->alloc_granularity;
- rmtU32 a = buffer->bytes_allocated + length;
- a = a + ((g - 1) - ((a - 1) % g));
- buffer->bytes_allocated = a;
- buffer->data = (rmtU8*)realloc(buffer->data, buffer->bytes_allocated);
- if (buffer->data == NULL)
- return RMT_ERROR_MALLOC_FAIL;
- }
- // Copy all bytes
- memcpy(buffer->data + buffer->bytes_used, data, length);
- buffer->bytes_used += length;
- // NULL terminate (if possible) for viewing in debug
- if (buffer->bytes_used < buffer->bytes_allocated)
- buffer->data[buffer->bytes_used] = 0;
- return RMT_ERROR_NONE;
- }
- static enum rmtError Buffer_WriteString(Buffer* buffer, rmtPStr string)
- {
- assert(string != NULL);
- return Buffer_Write(buffer, (void*)string, (rmtU32)strnlen_s(string, 2048));
- }
- /*
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- @SOCKETS: Sockets TCP/IP Wrapper
- ------------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------------------------------------------------------------------------------
- */
- #ifndef RMT_PLATFORM_WINDOWS
- typedef int SOCKET;
- #define INVALID_SOCKET -1
- #define SOCKET_ERROR -1
- #define SD_SEND SHUT_WR
- #define closesocket close
- #endif
- typedef struct
- {
- SOCKET socket;
- } TCPSocket;
- typedef struct
- {
- rmtBool can_read;
- rmtBool can_write;
- enum rmtError error_state;
- } SocketStatus;
- //
- // Function prototypes
- //
- static void TCPSocket_Close(TCPSocket* tcp_socket);
- static enum rmtError TCPSocket_Destroy(TCPSocket** tcp_socket, enum rmtError error);
- static enum rmtError InitialiseNetwork()
- {
- #ifdef RMT_PLATFORM_WINDOWS
- WSADATA wsa_data;
- if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
- return RMT_ERROR_SOCKET_INIT_NETWORK_FAIL;
- if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
- return RMT_ERROR_SOCKET_INIT_NETWORK_FAIL;
- return RMT_ERROR_NONE;
- #else
- return RMT_ERROR_NONE;
- #endif
- }
- static void ShutdownNetwork()
- {
- #ifdef RMT_PLATFORM_WINDOWS
- WSACleanup();
- #endif
- }
- static enum rmtError TCPSocket_Create(TCPSocket** tcp_socket)
- {
- enum rmtError error;
-
- assert(tcp_socket != NULL);
- // Allocate and initialise
- *tcp_socket = (TCPSocket*)malloc(sizeof(TCPSocket));
- if (*tcp_socket == NULL)
- return RMT_ERROR_MALLOC_FAIL;
- (*tcp_socket)->socket = INVALID_SOCKET;
- error = InitialiseNetwork();
- if (error != RMT_ERROR_NONE)
- return TCPSocket_Destroy(tcp_socket, error);
- return RMT_ERROR_NONE;
- }
- static enum rmtError TCPSocket_CreateServer(rmtU16 port, TCPSocket** tcp_socket)
- {
- SOCKET s = INVALID_SOCKET;
- struct sockaddr_in sin = { 0 };
- #ifdef RMT_PLATFORM_WINDOWS
- u_long nonblock = 1;
- #endif
- // Create socket container
- enum rmtError error = TCPSocket_Create(tcp_socket);
- if (error != RMT_ERROR_NONE)
- return error;
- // Try to create the socket
- s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (s == SOCKET_ERROR)
- return TCPSocket_Destroy(tcp_socket, RMT_ERROR_SOCKET_CREATE_FAIL);
- // Bind the socket to the incoming port
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(port);
- if (bind(s, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
- return TCPSocket_Destroy(tcp_socket, RMT_ERROR_SOCKET_BIND_FAIL);
- // Connection is valid, remaining code is socket state modification
- (*tcp_socket)->socket = s;
- // Enter a listening state with a backlog of 1 connection
- if (listen(s, 1) == SOCKET_ERROR)
- return TCPSocket_Destroy(tcp_socket, RMT_ERROR_SOCKET_LISTEN_FAIL);
- // Set as non-blocking
- #ifdef RMT_PLATFORM_WINDOWS
- if (ioctlsocket((*tcp_socket)->socket, FIONBIO, &nonblock) == SOCKET_ERROR)
- return TCPSocket_Destroy(tcp_socket, RMT_ERROR_SOCKET_SET_NON_BLOCKING_FAIL);
- #else
- if (fcntl((*tcp_socket)->socket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR)
- return TCPSocket_Destroy(tcp_socket, RMT_ERROR_SOCKET_SET_NON_BLOCKING_FAIL);
- #endif
- return RMT_ERROR_NONE;
- }
- static enum rmtError TCPSocket_Destroy(TCPSocket** tcp_socket, enum rmtError error)
- {
- assert(tcp_socket != NULL);
- TCPSocket_Close(*tcp_socket);
- ShutdownNetwork();
- free(*tcp_socket);
- *tcp_socket = NULL;
- return error;
- }
- static void TCPSocket_Close(TCPSocket* tcp_socket)
- {
- assert(tcp_socket != NULL);
- if (tcp_socket->socket != INVALID_SOCKET)
- {
- // Shutdown the connection, stopping all sends
- int result = shutdown(tcp_socket->socket, SD_SEND);
- if (result != SOCKET_ERROR)
- {
- // Keep receiving until the peer closes the connection
- int total = 0;
- char temp_buf[128];
- while (result > 0)
- {
- result = (int)recv(tcp_socket->socket, temp_buf, sizeof(temp_buf), 0);
- total += result;
- }
- }
- // Close the socket and issue a network shutdown request
- closesocket(tcp_socket->socket);
- tcp_socket->socket = INVALID_SOCKET;
- }
- }
- static SocketStatus TCPSocket_PollStatus(TCPSocket* tcp_socket)
- {
- SocketStatus status;
- fd_set fd_read, fd_write, fd_errors;
- struct timeval tv;
- status.can_read = RMT_FALSE;
- status.can_write = RMT_FALSE;
- status.error_state = RMT_ERROR_NONE;
- assert(tcp_socket != NULL);
- if (tcp_socket->socket == INVALID_SOCKET)
- {
- status.error_state = RMT_ERROR_SOCKET_INVALID_POLL;
- return status;
- }
- // Set read/write/error markers for the socket
- FD_ZERO(&fd_read);
- FD_ZERO(&fd_write);
- FD_ZERO(&fd_errors);
- FD_SET(tcp_socket->socket, &fd_read);
- FD_SET(tcp_socket->socket, &fd_write);
- FD_SET(tcp_socket->socket, &fd_errors);
- // Poll socket status without blocking
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- if (select(((int)tcp_socket->socket)+1, &fd_read, &fd_write, &fd_errors, &tv) == SOCKET_ERROR)
- {
- status.error_state = RMT_ERROR_SOCKET_SELECT_FAIL;
- return status;
- }
- status.can_read = FD_ISSET(tcp_socket->socket, &fd_read) != 0 ? RMT_TRUE : RMT_FALSE;
- status.can_write = FD_ISSET(tcp_socket->socket, &fd_write) != 0 ? RMT_TRUE : RMT_FALSE;
- status.error_state = FD_ISSET(tcp_socket->socket, &fd_errors) != 0 ? RMT_ERROR_SOCKET_POLL_ERRORS : RMT_ERROR_NONE;
- return status;
- }
- static enum rmtError TCPSocket_AcceptConnection(TCPSocket* tcp_socket, TCPSocket** client_socket)
- {
- SocketStatus status;
- SOCKET s;
- enum rmtError error;
- // Ensure there is an incoming connection
- assert(tcp_socket != NULL);
- status = TCPSocket_PollStatus(tcp_socket);
- if (status.error_state != RMT_ERROR_NONE || !status.can_read)
- return status.error_state;
- // Accept the connection
- s = accept(tcp_socket->socket, 0, 0);
- if (s == SOCKET_ERROR)
- return RMT_ERROR_SOCKET_ACCEPT_FAIL;
- // Create a client socket for the new connection
- assert(client_socket != NULL);
- error = TCPSocket_Create(client_socket);
- if (error != RMT_ERROR_NONE)
- return error;
- (*client_socket)->socket = s;
- return RMT_ERROR_NONE;
- }
- static int TCPSocketWouldBlock()
- {
- #ifdef RMT_PLATFORM_WINDOWS
- DWORD error = WSAGetLastError();
- return (error == WSAEWOULDBLOCK);
- #else
- int error = errno;
- return (error == EAGAIN || error == EWOULDBLOCK);
- #endif
- }
- static enum rmtError TCPSocket_Send(TCPSocket* tcp_socket, const void* data, rmtU32 length, rmtU32 timeout_ms)
- {
- SocketStatus status;
- char* cur_data = NULL;
- char* end_data = NULL;
- rmtU32 start_ms = 0;
- rmtU32 cur_ms = 0;
- assert(tcp_socket != NULL);
- // Can't send if there are socket errors
- status = TCPSocket_PollStatus(tcp_socket);
- if (status.error_state != RMT_ERROR_NONE)
- return status.error_state;
- if (!status.can_write)
- return RMT_ERROR_SOCKET_SEND_TIMEOUT;
- cur_data = (char*)data;
- end_data = cur_data + length;
- start_ms = msTimer_Get();
- while (cur_data < end_data)
- {
- // Attempt to send the remaining chunk of data
- int bytes_sent = (int)send(tcp_socket->socket, cur_data, (int)(end_data - cur_data), 0);
- if (bytes_sent == SOCKET_ERROR || bytes_sent == 0)
- {
- // Close the connection if sending fails for any other reason other than blocking
- if (bytes_sent != 0 && !TCPSocketWouldBlock())
- return RMT_ERROR_SOCKET_SEND_FAIL;
- // First check for tick-count overflow and reset, giving a slight hitch every 49.7 days
- cur_ms = msTimer_Get();
- if (cur_ms < start_ms)
- {
- start_ms = cur_ms;
- continue;
- }
- //
- // Timeout can happen when:
- //
- // 1) endpoint is no longer there
- // 2) endpoint can't consume quick enough
- // 3) local buffers overflow
- //
- // As none of these are actually errors, we have to pass this timeout back to the caller.
- //
- // TODO: This strategy breaks down if a send partially completes and then times out!
- //
- if (cur_ms - start_ms > timeout_ms)
- {
- return RMT_ERROR_SOCKET_SEND_TIMEOUT;
- }
- }
- else
- {
- // Jump over the data sent
- cur_data += bytes_sent;
- }
- }
- return R…
Large files files are truncated, but you can click here to view the full file