// This is the entry point for the file watcher thread. It scans local files // for changes and processes the diffs with processFileEntryDeltas. static int fileWatcherThread(void *payload) { // Start with a sane state so we don't stream everything. utArray<FileEntry> *oldState = generateFileState("."); // Loop forever looking for changes. for ( ; ; ) { int startTime = platform_getMilliseconds(); utArray<FileEntry> *newState = generateFileState("."); utArray<FileEntryDelta> *deltas = compareFileEntries(oldState, newState); int endTime = platform_getMilliseconds(); if (endTime - startTime > 250) { lmLogWarn(gAssetAgentLogGroup, "Scanning files took %dms, consider removing unused files", endTime - startTime); } processFileEntryDeltas(deltas); lmDelete(NULL, deltas); // Make the new state the old state and clean up the old state. lmDelete(NULL, oldState); oldState = newState; // Wait a bit so we don't saturate disk or CPU. loom_thread_sleep(gFileCheckInterval); } }
int loom_net_writeTCPSocket(loom_socketId_t s, void *buffer, int bytesToWrite) { #if LOOM_PLATFORM == LOOM_PLATFORM_WIN32 int winsockErrorCode; #endif int bytesLeft = bytesToWrite; for ( ; ; ) { int result = send((SOCKET)s, buffer, bytesLeft, MSG_NOSIGNAL); if (result >= 0) { bytesLeft -= result; if (bytesLeft != 0) { lmLogDebug(netLogGroup, "Partial write on socket %d, expected %d but wrote %d! Retrying...", s, bytesToWrite, result); // Set up to try again by advancing into the buffer. buffer = (void *)((char *)buffer + result); continue; } return bytesToWrite - bytesLeft; } #if LOOM_PLATFORM == LOOM_PLATFORM_WIN32 winsockErrorCode = WSAGetLastError(); if (winsockErrorCode != WSAEWOULDBLOCK) { break; } #else if (errno != EAGAIN) { break; } #endif if (loom_net_isSocketDead(s)) { break; } loom_thread_sleep(5); } return -1; }
void loom_asset_waitForConnection(int msToWait) { // be extra agressive before starting up gAssetServerConnectTryInterval = 10; int startTime = platform_getMilliseconds(); while (!gAssetConnectionOpen && (platform_getMilliseconds() - startTime) < msToWait) { loom_asset_pump(); loom_thread_sleep(10); } // Go back to pinging every 3 seconds gAssetServerConnectTryInterval = 3000; }
void loom_net_readTCPSocket(loom_socketId_t s, void *buffer, int *bytesToRead, int peek /*= 0*/) { int errorCode; int waiting; int tmp = *bytesToRead; int bytesLeft; if (loom_net_isSocketDead(s)) { *bytesToRead = 0; return; } if (peek) { *bytesToRead = recv((SOCKET)s, buffer, tmp, MSG_PEEK); return; } bytesLeft = *bytesToRead; while (bytesLeft > 0) { int received = recv((SOCKET)s, buffer, bytesLeft, 0); if (received == -1) { waiting = 0; #if LOOM_PLATFORM == LOOM_PLATFORM_WIN32 errorCode = WSAGetLastError(); if (errorCode == WSAEWOULDBLOCK) { waiting = 1; } #else errorCode = errno; if (errorCode == EAGAIN) { waiting = 1; } #endif if (waiting) { // TODO figure out a better way to do this? //lmLogDebug(netLogGroup, "Waiting for receive buffer %d / %d", *bytesToRead - bytesLeft, *bytesToRead); loom_thread_sleep(5); continue; } else { lmLogError(netLogGroup, "Read socket error (%d)", errorCode); *bytesToRead = -1; return; } } bytesLeft -= received; buffer = (char*)buffer + received; } lmAssert(bytesLeft == 0, "Internal recv error, read too much data? %d", bytesLeft); }
void DLLEXPORT assetAgent_run(IdleCallback idleCb, LogCallback logCb, FileChangeCallback changeCb) { loom_log_initialize(); platform_timeInitialize(); stringtable_initialize(); loom_net_initialize(); // Put best effort towards closing our listen socket when we shut down, to // avoid bugs on OSX where the OS won't release it for a while. #if LOOM_PLATFORM == LOOM_PLATFORM_OSX || LOOM_PLATFORM == LOOM_PLATFORM_LINUX atexit(shutdownListenSocket); signal(SIGINT, shutdownListenSocketSignalHandler); #endif // Set up mutexes. gActiveSocketsMutex = loom_mutex_create(); gFileScannerLock = loom_mutex_create(); gCallbackLock = loom_mutex_create(); // Note callbacks. gLogCallback = logCb; gFileChangeCallback = changeCb; utString *sdkPath = optionGet("sdk"); if (sdkPath != NULL) TelemetryServer::setClientRootFromSDK(sdkPath->c_str()); const char *ltcPath = getenv("LoomTelemetry"); if (ltcPath != NULL) TelemetryServer::setClientRoot(ltcPath); if (optionEquals("telemetry", "true")) TelemetryServer::start(); // Set up the log callback. loom_log_addListener(fileWatcherLogListener, NULL); lmLogDebug(gAssetAgentLogGroup, "Starting file watcher thread..."); gFileWatcherThread = loom_thread_start((ThreadFunction)fileWatcherThread, NULL); lmLogDebug(gAssetAgentLogGroup, " o OK!"); lmLogDebug(gAssetAgentLogGroup, "Starting socket listener thread..."); gSocketListenerThread = loom_thread_start((ThreadFunction)socketListeningThread, NULL); lmLogDebug(gAssetAgentLogGroup, " o OK!"); // Loop till it's time to quit. while (!gQuitFlag) { // Serve the idle callback. if (idleCb) { idleCb(); } // And anything in the queue. while (CallbackQueueNote *cqn = dequeueCallback()) { if (!cqn) { break; } // Issue the call. if (cqn->type == QNT_Change) { gFileChangeCallback(cqn->text.c_str()); } else if (cqn->type == QNT_Log) { gLogCallback(cqn->text.c_str()); } else { lmAssert(false, "Unknown callback queue note type."); } // Clean it up. //free((void *)cqn->text); lmDelete(NULL, cqn); } // Pump any remaining socket writes loom_net_pump(); // Poll at about 60hz. loom_thread_sleep(16); } // Clean up the socket. shutdownListenSocket(); }
// Entry point for the socket thread. Listen for connections and incoming data, // and route it to the protocol handlers. static int socketListeningThread(void *payload) { // Listen for incoming connections. int listenPort = 12340; gListenSocket = (loom_socketId_t)-1; for ( ; ; ) { gListenSocket = loom_net_listenTCPSocket(listenPort); if (gListenSocket != (loom_socketId_t)-1) { break; } lmLogWarn(gAssetAgentLogGroup, " - Failed to acquire port %d, trying port %d", listenPort, listenPort + 1); listenPort++; } lmLog(gAssetAgentLogGroup, "Listening on port %d", listenPort); while (loom_socketId_t acceptedSocket = loom_net_acceptTCPSocket(gListenSocket)) { // Check to see if we got anybody... if (!acceptedSocket || ((int)(long)acceptedSocket == -1)) { // Process the connections. loom_mutex_lock(gActiveSocketsMutex); for (UTsize i = 0; i < gActiveHandlers.size(); i++) { AssetProtocolHandler* aph = gActiveHandlers[i]; aph->process(); // Check for ping timeout int msSincePing = loom_readTimer(aph->lastActiveTime); if (msSincePing > socketPingTimeoutMs) { gActiveHandlers.erase(i); i--; lmLog(gAssetAgentLogGroup, "Client timed out (%x)", aph->socket); loom_net_closeTCPSocket(aph->socket); lmDelete(NULL, aph); } } loom_mutex_unlock(gActiveSocketsMutex); loom_thread_sleep(10); continue; } lmLog(gAssetAgentLogGroup, "Client connected (%x)", acceptedSocket); loom_mutex_lock(gActiveSocketsMutex); gActiveHandlers.push_back(lmNew(NULL) AssetProtocolHandler(acceptedSocket)); AssetProtocolHandler *handler = gActiveHandlers.back(); handler->registerListener(lmNew(NULL) TelemetryListener()); if (TelemetryServer::isRunning()) handler->sendCommand("telemetryEnable"); // Send it all of our files. // postAllFiles(gActiveHandlers[gActiveHandlers.size()-1]->getId()); loom_mutex_unlock(gActiveSocketsMutex); } return 0; }
task_t *dummyChildTask(void *payload, task_t *task) { atomic_increment(&gChildWorkCount); loom_thread_sleep(2); return NULL; }
void loom_tick() { if (atomic_load32(&gLoomTicking) < 1) { // Signal that the app has really stopped execution if (atomic_load32(&gLoomPaused) == 0) { atomic_store32(&gLoomPaused, 1); } // Sleep for longer while paused. // Since graphics aren't running in a paused state, there is no yielding // we would otherwise run in a busy loop without sleeping. loom_thread_sleep(30); return; } atomic_store32(&gLoomPaused, 0); Telemetry::beginTick(); LOOM_PROFILE_START(loom_tick); LSLuaState *vm = NULL; vm = LoomApplication::getReloadQueued() ? NULL : LoomApplication::getRootVM(); // Mark the main thread for NativeDelegates. On some platforms this // may change so we remark every frame. NativeDelegate::markMainThread(); if (vm) NativeDelegate::executeDeferredCalls(vm->VM()); performance_tick(); profilerBlock_t p = { "loom_tick", platform_getMilliseconds(), 17 }; if (LoomApplication::getReloadQueued()) { LoomApplication::reloadMainAssembly(); } else { if (vm) { // https://theengineco.atlassian.net/browse/LOOM-468 // decouple debugger enabled from connection time // as the debugger matures this may change a bit if (LoomApplicationConfig::waitForDebugger() > 0) { vm->invokeStaticMethod("system.debugger.DebuggerClient", "update"); } LoomApplication::ticks.invoke(); } } loom_asset_pump(); platform_HTTPUpdate(); GFX::Texture::tick(); if (Loom2D::Stage::smMainStage) Loom2D::Stage::smMainStage->invokeRenderStage(); finishProfilerBlock(&p); LOOM_PROFILE_END(loom_tick); LOOM_PROFILE_ZERO_CHECK() Telemetry::endTick(); }