diff --git a/limelight-common/Connection.c b/limelight-common/Connection.c index df8f4fe..4296bbc 100644 --- a/limelight-common/Connection.c +++ b/limelight-common/Connection.c @@ -4,6 +4,7 @@ static int stage = STAGE_NONE; static CONNECTION_LISTENER_CALLBACKS listenerCallbacks; static CONNECTION_LISTENER_CALLBACKS originalCallbacks; +static PLATFORM_CALLBACKS platformCallbacks; static int alreadyTerminated; @@ -134,12 +135,19 @@ void ClInternalDisplayTransientMessage(char* message) originalCallbacks.displayTransientMessage(message); } +void LiCompleteThreadStart(void) +{ + PltRunThreadProc(); +} + /* Starts the connection to the streaming machine */ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks, - PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags) { + PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, PPLATFORM_CALLBACKS plCallbacks, + void* renderContext, int drFlags) { int err; memcpy(&originalCallbacks, clCallbacks, sizeof(originalCallbacks)); + memcpy(&platformCallbacks, plCallbacks, sizeof(platformCallbacks)); listenerCallbacks.stageStarting = ClInternalStageStarting; listenerCallbacks.stageComplete = ClInternalStageComplete; @@ -153,13 +161,13 @@ int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONN Limelog("Initializing platform..."); listenerCallbacks.stageStarting(STAGE_PLATFORM_INIT); - err = initializePlatformSockets(); + err = initializePlatformSockets(&platformCallbacks); if (err != 0) { Limelog("failed: %d\n", err); listenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err); goto Cleanup; } - err = initializePlatformThreads(); + err = initializePlatformThreads(&platformCallbacks); if (err != 0) { Limelog("failed: %d\n", err); listenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err); diff --git a/limelight-common/Limelight.h b/limelight-common/Limelight.h index 4710bdf..e056425 100644 --- a/limelight-common/Limelight.h +++ b/limelight-common/Limelight.h @@ -90,11 +90,21 @@ typedef struct _CONNECTION_LISTENER_CALLBACKS { ConnListenerDisplayTransientMessage displayTransientMessage; } CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS; +typedef void(*PlatformThreadStart)(void); + +typedef struct _PLATFORM_CALLBACKS { + PlatformThreadStart threadStart; +} PLATFORM_CALLBACKS, *PPLATFORM_CALLBACKS; + int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks, - PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags); + PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, PPLATFORM_CALLBACKS plCallbacks, + void* renderContext, int drFlags); void LiStopConnection(void); const char* LiGetStageName(int stage); +/* Call in the context of a new thread */ +void LiCompleteThreadStart(void); + int LiSendMouseMoveEvent(short deltaX, short deltaY); #define BUTTON_ACTION_PRESS 0x07 diff --git a/limelight-common/PlatformSockets.c b/limelight-common/PlatformSockets.c index bc0e8bb..48df198 100644 --- a/limelight-common/PlatformSockets.c +++ b/limelight-common/PlatformSockets.c @@ -80,7 +80,7 @@ int enableNoDelay(SOCKET s) { return 0; } -int initializePlatformSockets(void) { +int initializePlatformSockets(PPLATFORM_CALLBACKS plCallbacks) { #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) WSADATA data; return WSAStartup(MAKEWORD(2, 0), &data); diff --git a/limelight-common/PlatformSockets.h b/limelight-common/PlatformSockets.h index f101872..5afc3be 100644 --- a/limelight-common/PlatformSockets.h +++ b/limelight-common/PlatformSockets.h @@ -33,5 +33,5 @@ typedef ssize_t SOCK_RET; SOCKET connectTcpSocket(IP_ADDRESS dstaddr, unsigned short port); SOCKET bindUdpSocket(void); int enableNoDelay(SOCKET s); -int initializePlatformSockets(void); +int initializePlatformSockets(PPLATFORM_CALLBACKS plCallbacks); void cleanupPlatformSockets(void); \ No newline at end of file diff --git a/limelight-common/PlatformThreads.c b/limelight-common/PlatformThreads.c index f814785..9261631 100644 --- a/limelight-common/PlatformThreads.c +++ b/limelight-common/PlatformThreads.c @@ -1,28 +1,15 @@ #include "PlatformThreads.h" #include "Platform.h" -struct thread_context { - ThreadEntry entry; - void* context; -}; - #if defined(LC_WINDOWS_PHONE) || defined(LC_WINDOWS) WCHAR DbgBuf[512]; #endif #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) PLT_MUTEX thread_list_lock; +PLT_THREAD *pending_thread_head; PLT_THREAD *thread_head; - -DWORD WINAPI ThreadProc(LPVOID lpParameter) { - struct thread_context *ctx = (struct thread_context *)lpParameter; - - ctx->entry(ctx->context); - - free(ctx); - - return 0; -} +PPLATFORM_CALLBACKS platformCallbacks; #else void* ThreadProc(void* context) { struct thread_context *ctx = (struct thread_context *)context; @@ -86,7 +73,8 @@ void PltUnlockMutex(PLT_MUTEX *mutex) { void PltJoinThread(PLT_THREAD *thread) { #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) - WaitForSingleObjectEx(thread->handle, INFINITE, FALSE); + // Wait for the thread to leave our code + WaitForSingleObjectEx(thread->termCompleted, INFINITE, FALSE); #else pthread_join(*thread, NULL); #endif @@ -123,8 +111,8 @@ void PltCloseThread(PLT_THREAD *thread) { PltUnlockMutex(&thread_list_lock); - CloseHandle(thread->termevent); - CloseHandle(thread->handle); + CloseHandle(thread->termRequested); + CloseHandle(thread->termCompleted); #else #endif } @@ -142,12 +130,47 @@ int PltIsThreadInterrupted(PLT_THREAD *thread) { void PltInterruptThread(PLT_THREAD *thread) { #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) thread->cancelled = 1; - SetEvent(thread->termevent); + SetEvent(thread->termRequested); #else pthread_cancel(*thread); #endif } +void PltRunThreadProc(void) { +#if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) + PLT_THREAD *thread; + + // Grab the first entry from the pending list and move it + // to the active list + PltLockMutex(&thread_list_lock); + thread = pending_thread_head; + + // If there's no pending thread, something is seriously wrong + LC_ASSERT(thread != NULL); + + pending_thread_head = pending_thread_head->next; + + thread->next = thread_head; + thread_head = thread; + PltUnlockMutex(&thread_list_lock); + + // Set up final thread state before running + thread->tid = GetCurrentThreadId(); + + // Now we're going to invoke the thread proc + thread->ctx->entry(thread->ctx->context); + free(thread->ctx); + + // Signal the event to indicate the thread has "terminated" + SetEvent(thread->termCompleted); + + // PltCloseThread() frees this state +#else + // This code shouldn't be called on *NIX + LC_ASSERT(0); +#endif +} + int PltCreateThread(ThreadEntry entry, void* context, PLT_THREAD *thread) { struct thread_context *ctx; int err; @@ -162,32 +185,32 @@ int PltCreateThread(ThreadEntry entry, void* context, PLT_THREAD *thread) { #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) { - thread->termevent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); - if (thread->termevent == NULL) { + thread->termRequested = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); + if (thread->termRequested == NULL) { free(ctx); return -1; } + + thread->termCompleted = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); + if (thread->termCompleted == NULL) { + CloseHandle(thread->termRequested); + free(ctx); + return -1; + } + thread->cancelled = 0; - // TODO we aren't allowed to use CreateThread API in kernel32.dll - thread->handle = CreateThread(NULL, 0, ThreadProc, ctx, CREATE_SUSPENDED, &thread->tid); - if (thread->handle == NULL) { - CloseHandle(thread->termevent); - free(ctx); - return -1; - } - else { - // Add this thread to the thread list - PltLockMutex(&thread_list_lock); - thread->next = thread_head; - thread_head = thread; - PltUnlockMutex(&thread_list_lock); + thread->ctx = ctx; - // Now the thread can run - // TODO can't use ResumeThread in kernel32.dll - ResumeThread(thread->handle); + // Queue on the pending threads list + PltLockMutex(&thread_list_lock); + thread->next = pending_thread_head; + pending_thread_head = thread; + PltUnlockMutex(&thread_list_lock); - err = 0; - } + // Make a callback to managed code to ask for a thread to grab this + platformCallbacks->threadStart(); + + err = 0; } #else { @@ -263,7 +286,7 @@ int PltWaitForEvent(PLT_EVENT *event) { LC_ASSERT(current_thread != NULL); objects[0] = *event; - objects[1] = current_thread->termevent; + objects[1] = current_thread->termRequested; error = WaitForMultipleObjectsEx(2, objects, FALSE, INFINITE, FALSE); if (error == WAIT_OBJECT_0) { return PLT_WAIT_SUCCESS; @@ -285,8 +308,14 @@ int PltWaitForEvent(PLT_EVENT *event) { #endif } -int initializePlatformThreads(void) { +int initializePlatformThreads(PPLATFORM_CALLBACKS plCallbacks) { #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) + pending_thread_head = thread_head = NULL; + + // This data is stored in static memory in Connection.c, so we don't + // bother making our own copy + platformCallbacks = plCallbacks; + return PltCreateMutex(&thread_list_lock); #else return 0; diff --git a/limelight-common/PlatformThreads.h b/limelight-common/PlatformThreads.h index 2a30205..f597a8b 100644 --- a/limelight-common/PlatformThreads.h +++ b/limelight-common/PlatformThreads.h @@ -1,15 +1,23 @@ #pragma once +#include "Limelight.h" #include "Platform.h" typedef void (*ThreadEntry)(void *context); +struct thread_context { + ThreadEntry entry; + void* context; +}; + #if defined(LC_WINDOWS) || defined(LC_WINDOWS_PHONE) typedef struct _PLT_THREAD { - HANDLE handle; int cancelled; DWORD tid; - HANDLE termevent; + HANDLE termRequested; + HANDLE termCompleted; + + struct thread_context *ctx; struct _PLT_THREAD *next; } PLT_THREAD; @@ -27,28 +35,7 @@ typedef struct _PLT_EVENT { #error Unsupported platform #endif -#ifdef LC_WINDOWS_PHONE -WINBASEAPI -_Ret_maybenull_ -HANDLE -WINAPI -CreateThread( - _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, - _In_ SIZE_T dwStackSize, - _In_ LPTHREAD_START_ROUTINE lpStartAddress, - _In_opt_ __drv_aliasesMem LPVOID lpParameter, - _In_ DWORD dwCreationFlags, - _Out_opt_ LPDWORD lpThreadId -); - -DWORD -WINAPI -ResumeThread( - _In_ HANDLE hThread -); -#endif - -int initializePlatformThreads(void); +int initializePlatformThreads(PPLATFORM_CALLBACKS plCallbacks); void cleanupPlatformThreads(void); int PltCreateMutex(PLT_MUTEX *mutex); @@ -68,6 +55,8 @@ void PltSetEvent(PLT_EVENT *event); void PltClearEvent(PLT_EVENT *event); int PltWaitForEvent(PLT_EVENT *event); +void PltRunThreadProc(void); + #define PLT_WAIT_SUCCESS 0 #define PLT_WAIT_INTERRUPTED 1