void loom_asset_flush(const char *name) { // Currently we only want to do this on the main thread so piggy back on the // native delegate sanity check to bail if on secondary thread. if(platform_getCurrentThreadId() != LS::NativeDelegate::smMainThreadID && LS::NativeDelegate::smMainThreadID != 0xBAADF00D) return; loom_mutex_lock(gAssetLock); // Delete it + unload it. loom_asset_t *asset = loom_asset_getAssetByName(name, 0); if(!asset || asset->isSupplied) { loom_mutex_unlock(gAssetLock); return; } lmLogDebug(gAssetLogGroup, "Flushing '%s'", name); if (asset->blob) { asset->blob->decRef(); asset->blob = NULL; } asset->state = loom_asset_t::Unloaded; // Fire subscribers. if(!gShuttingDown) loom_asset_notifySubscribers(asset->name.c_str()); loom_mutex_unlock(gAssetLock); }
void DLLEXPORT assetAgent_command(const char *cmd) { if (strstr(cmd, ".sendall") != 0) { postAllFiles(); } else if (strstr(cmd, ".clients") != 0) { listClients(); } else if (strstr(cmd, ".telemetry") != 0) { TelemetryServer::isRunning() ? TelemetryServer::stop() : TelemetryServer::start(); assetAgent_command(TelemetryServer::isRunning() ? "telemetryEnable" : "telemetryDisable"); } else if (cmd[0] != 0) { loom_mutex_lock(gActiveSocketsMutex); if (gActiveHandlers.size() == 0) { if (strcmp(cmd, "terminate") != 0) sendIgnoredError(); } for (UTsize i = 0; i < gActiveHandlers.size(); i++) { gActiveHandlers[i]->sendCommand(cmd); } loom_mutex_unlock(gActiveSocketsMutex); } }
int loom_asset_subscribe(const char *name, LoomAssetChangeCallback cb, void *payload, int doFirstCall) { loom_mutex_lock(gAssetLock); loom_asset_t *asset = loom_asset_getAssetByName(name, 1); if (!asset) { loom_mutex_unlock(gAssetLock); return 0; } // Add to list of subscribers. loom_asset_subscription_t subscription; subscription.callback = cb; subscription.payload = payload; asset->subscribers.push_back(subscription); // If it is loaded and we want it, do the first call. if (doFirstCall && (asset->state == loom_asset_t::Loaded)) { cb(payload, name); } loom_mutex_unlock(gAssetLock); return 1; }
void loom_asset_supply(const char *name, void *bits, int length) { loom_mutex_lock(gAssetLock); // Prep the asset. loom_asset_t *asset = loom_asset_getAssetByName(name, 1); // Make sure it's pristine. lmAssert(asset->state == loom_asset_t::Unloaded, "Can't supply an asset that's already queued or in process of loading. Supply assets before you make any asset requests!"); // Figure out the type from the path. utString nameAsUt = name; int type = loom_asset_recognizeAssetTypeFromPath(nameAsUt); if (type == 0) { lmLog(gAssetLogGroup, "Could not infer type of supplied resource '%s', skipping it...", name); asset->state = loom_asset_t::Unloaded; return; } // Deserialize it. LoomAssetCleanupCallback dtor = NULL; void *assetBits = loom_asset_deserializeAsset(name, type, length, bits, &dtor); // Instate the asset. // TODO: We can save some memory by pointing directly and not making a copy. asset->instate(type, assetBits, dtor); // Note it's supplied so we don't flush it. asset->isSupplied = 1; loom_mutex_unlock(gAssetLock); }
int loom_asset_unsubscribe(const char *name, LoomAssetChangeCallback cb, void *payload) { loom_mutex_lock(gAssetLock); loom_asset_t *asset = loom_asset_getAssetByName(name, 0); if (!asset) { loom_mutex_unlock(gAssetLock); return 0; } // Remove from list of subscribers. for (UTsize i = 0; i < asset->subscribers.size(); i++) { const loom_asset_subscription_t& subscription = asset->subscribers[i]; if (subscription.callback != cb) { continue; } if (subscription.payload != payload) { continue; } asset->subscribers.erase(i, true); loom_mutex_unlock(gAssetLock); return 1; } loom_mutex_unlock(gAssetLock); return 0; }
static void pollScaling() { // Check the event queue. if (gEventQueueMutex == NULL) { return; } loom_mutex_lock(gEventQueueMutex); while (gEventQueue.size() > 0) { RescaleEventStatus curItem = gEventQueue.front(); gImageScaleProgressDelegate.pushArgument(curItem.path.c_str()); gImageScaleProgressDelegate.pushArgument(curItem.progress); gImageScaleProgressDelegate.invoke(); // Flush the asset (only works on main thread atm) if (curItem.progress == 1.0f) { loom_asset_flush(curItem.path.c_str()); } gEventQueue.pop_front(); } loom_mutex_unlock(gEventQueueMutex); }
void *loom_asset_lock(const char *name, unsigned int type, int block) { const char *namePtr = stringtable_insert(name); loom_mutex_lock(gAssetLock); // Look it up. loom_asset_t *asset = loom_asset_getAssetByName(namePtr, 1); lmAssert(asset != NULL, "Didn't get asset even though we should have!"); // If not loaded, and we aren't ready to block, return NULL. if ((block == 0) && (asset->state != loom_asset_t::Loaded)) { lmLogDebug(gAssetLogGroup, "Not blocking and not loaded yet; lock of '%s' failed.", namePtr); loom_mutex_unlock(gAssetLock); return NULL; } // Otherwise, let's force it to load now. if (asset->state != loom_asset_t::Loaded) { lmLogDebug(gAssetLogGroup, "Blocking so forcing load of '%s'.", namePtr); loom_asset_preload(namePtr); lmAssert(loom_asset_isOnTrackToLoad(asset), "Preloaded but wasn't on track to load!"); while (loom_asset_checkLoadedPercentage(namePtr) != 1.f && loom_asset_isOnTrackToLoad(asset)) { lmLogDebug(gAssetLogGroup, "Pumping load of '%s'...", namePtr); loom_asset_pump(); } if (asset->state != loom_asset_t::Loaded) { lmLogError(gAssetLogGroup, "Unable to load asset '%s'!", name); loom_mutex_unlock(gAssetLock); return NULL; } } // Check type. if (asset->type != type) { lmLogError(gAssetLogGroup, "Tried to lock asset '%s' with wrong type, assetType=%x, requestedType=%x", name, asset->type, type); loom_mutex_unlock(gAssetLock); return NULL; } // Inc count. asset->blob->incRef(); loom_mutex_unlock(gAssetLock); lmLogDebug(gAssetLogGroup, "Locked asset '%s'...", namePtr); // Return ptr. return asset->blob->bits; }
VertexPosColorTex *QuadRenderer::getQuadVertexMemory(uint16_t vertexCount, TextureID texture, bool blendEnabled, uint32_t srcBlend, uint32_t dstBlend, ShaderProgram *shader) { LOOM_PROFILE_SCOPE(quadGetVertices); if (!vertexCount || (texture < 0) || (vertexCount > MAXBATCHQUADS * 4) || shader == nullptr) { return NULL; } #ifdef LOOM_DEBUG loom_mutex_lock(Texture::sTexInfoLock); lmAssert(!(vertexCount % 4), "numVertices % 4 != 0"); lmAssert(texture == Texture::getTextureInfo(texture)->id, "Texture ID signature mismatch, you might be trying to draw a disposed texture"); loom_mutex_unlock(Texture::sTexInfoLock); lmAssert(batchedVertices, "batchedVertices should not be null"); #endif bool doSubmit = false; if (currentTexture != TEXTUREINVALID && currentTexture != texture) doSubmit = true; if (sCurrentShader != NULL && *sCurrentShader != *shader) doSubmit = true; if (srcBlend != sSrcBlend || dstBlend != sDstBlend) doSubmit = true; if ((batchedVertexCount + vertexCount) > MAXBATCHQUADS * 4) doSubmit = true; if (doSubmit) submit(); if (currentTexture != TEXTUREINVALID && currentTexture != texture) sTextureStateValid = false; if (sCurrentShader != NULL && *sCurrentShader != *shader) sShaderStateValid = false; if (srcBlend != sSrcBlend || dstBlend != sDstBlend || blendEnabled != sBlendEnabled) sBlendStateValid = false; sSrcBlend = srcBlend; sDstBlend = dstBlend; sBlendEnabled = blendEnabled; currentTexture = texture; sCurrentShader = shader; VertexPosColorTex *currentVertices = &batchedVertices[batchedVertexCount]; batchedVertexCount += vertexCount; return currentVertices; }
// Helper function to route Loom custom output over the network. void loom_asset_custom(void* buffer, int length) { loom_mutex_lock(gAssetServerSocketLock); if (gAssetProtocolHandler) { gAssetProtocolHandler->sendCustom(buffer, length); } loom_mutex_unlock(gAssetServerSocketLock); }
void loom_asset_reload(const char *name) { loom_mutex_lock(gAssetLock); loom_asset_t *asset = loom_asset_getAssetByName(name, 1); // Put it in the queue, this will trigger a new blob to be loaded. gAssetLoadQueue.push_back(asset); loom_mutex_unlock(gAssetLock); }
static void enqueueFileChangeCallback(const char *path) { CallbackQueueNote *cqn = lmNew(NULL) CallbackQueueNote(); cqn->type = QNT_Change; cqn->text = utString(path); loom_mutex_lock(gCallbackLock); gCallbackQueue.push_back(cqn); loom_mutex_unlock(gCallbackLock); }
static void enqueueLogCallback(const char *msg) { CallbackQueueNote *cqn = lmNew(NULL) CallbackQueueNote(); cqn->type = QNT_Log; cqn->text = utString(msg); loom_mutex_lock(gCallbackLock); gCallbackQueue.push_back(cqn); loom_mutex_unlock(gCallbackLock); }
// Helper function to route Loom log output over the network. void loom_asset_logListener(void *payload, loom_logGroup_t *group, loom_logLevel_t level, const char *msg) { loom_mutex_lock(gAssetServerSocketLock); if (gAssetProtocolHandler) { gAssetProtocolHandler->sendLog(msg); } loom_mutex_unlock(gAssetServerSocketLock); }
void loom_asset_pump() { // Currently we only want to do this on the main thread so piggy back on the // native delegate sanity check to bail if on secondary thread. if(platform_getCurrentThreadId() != LS::NativeDelegate::smMainThreadID && LS::NativeDelegate::smMainThreadID != 0xBAADF00D) return; loom_mutex_lock(gAssetLock); // Talk to the asset server. loom_asset_serviceServer(); // For now just blast all the data from each file into the asset. while(gAssetLoadQueue.size()) { loom_asset_t *asset = gAssetLoadQueue.front(); // Figure out the type from the path. utString path = asset->name; int type = loom_asset_recognizeAssetTypeFromPath(path); if(type == 0) { lmLog(gAssetLogGroup, "Could not infer type of resource '%s', skipping it...", path.c_str()); asset->state = loom_asset_t::Unloaded; gAssetLoadQueue.erase((UTsize)0, true); continue; } // Open the file. void *ptr; long size; if(!platform_mapFile(asset->name.c_str(), &ptr, &size)) { lmAssert(false, "Could not open file '%s'.", asset->name.c_str()); } // Deserialize it. LoomAssetCleanupCallback dtor = NULL; void *assetBits = loom_asset_deserializeAsset(path, type, size, ptr, &dtor); // Close the file. platform_unmapFile(ptr); // Instate the asset. asset->instate(type, assetBits, dtor); // Done! Update queue. gAssetLoadQueue.erase((UTsize)0, true); } loom_mutex_unlock(gAssetLock); }
void NativeDelegate::postNativeDelegateCallNote(NativeDelegateCallNote *ndcn) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Prep for reading. ndcn->rewind(); // Store for later access. gNDCallNoteQueue.push_back(ndcn); loom_mutex_unlock(gCallNoteMutex); }
// Dump connected clients to the console; useful for telling who is connected to the console! static void listClients() { loom_mutex_lock(gActiveSocketsMutex); // Blast it out to all clients. lmLog(gAssetAgentLogGroup, "Clients"); for (UTsize i = 0; i < gActiveHandlers.size(); i++) { lmLog(gAssetAgentLogGroup, " #%d - %s", gActiveHandlers[i]->getId(), gActiveHandlers[i]->description().c_str()); } loom_mutex_unlock(gActiveSocketsMutex); }
static void postResampleEvent(const char *path, float progress) { if (gEventQueueMutex == NULL) { gEventQueueMutex = loom_mutex_create(); } loom_mutex_lock(gEventQueueMutex); RescaleEventStatus res; res.path = path; res.progress = progress; gEventQueue.push_back(res); loom_mutex_unlock(gEventQueueMutex); }
float loom_asset_checkLoadedPercentage(const char *name) { loom_mutex_lock(gAssetLock); // Look it up. loom_asset_t *asset = loom_asset_getAssetByName(name, 0); loom_mutex_unlock(gAssetLock); if (!asset) { return 0.f; } // If loaded, return 1, else 0. (For now.) return asset->state == loom_asset_t::Loaded ? 1.f : 0.2f; }
int loom_asset_pending(const char *name) { loom_mutex_lock(gAssetLock); // Look 'er up. loom_asset_t *asset = loom_asset_getAssetByName(name, 0); // If it's not pending load, then stick it in the queue. int result; if(asset && loom_asset_isOnTrackToLoad(asset)) result = 1; else result = 0; loom_mutex_unlock(gAssetLock); return result; }
void loom_asset_preload(const char *name) { loom_mutex_lock(gAssetLock); // Look 'er up. loom_asset_t *asset = loom_asset_getAssetByName(name, 1); // If it's not pending load, then stick it in the queue. if (loom_asset_isOnTrackToLoad(asset)) { loom_mutex_unlock(gAssetLock); return; } asset->state = loom_asset_t::QueuedForDownload; gAssetLoadQueue.push_back(asset); loom_mutex_unlock(gAssetLock); }
static CallbackQueueNote *dequeueCallback() { CallbackQueueNote *cqn = NULL; loom_mutex_lock(gCallbackLock); if (gCallbackQueue.begin() == NULL) { cqn = NULL; } else { cqn = gCallbackQueue.front(); gCallbackQueue.pop_front(); } loom_mutex_unlock(gCallbackLock); return cqn; }
void loom_asset_notifySubscribers(const char *name) { loom_mutex_lock(gAssetLock); loom_asset_t *asset = loom_asset_getAssetByName(name, 0); if (!asset) { loom_mutex_unlock(gAssetLock); return; } for (UTsize i = 0; i < asset->subscribers.size(); i++) { loom_asset_subscription_t& s = asset->subscribers[i]; s.callback(s.payload, name); } loom_mutex_unlock(gAssetLock); }
static loom_asset_t *loom_asset_getAssetByName(const char *name, int create) { loom_mutex_lock(gAssetLock); static char normalized[4096]; strncpy(normalized, name, sizeof(normalized)); platform_normalizePath(normalized); utHashedString key = normalized; loom_mutex_unlock(gAssetLock); loom_asset_t **assetPtr = gAssetHash.get(key); loom_asset_t *asset = assetPtr ? *assetPtr : NULL; if ((asset == NULL) && create) { // Create one. asset = lmNew(gAssetAllocator) loom_asset_t; asset->name = name; gAssetHash.insert(key, asset); } return asset; }
/** * Post all known files to all clients, or if specified, a single client. * * Useful for fully synching client with the current asset state. * * TODO: Optimize to use hashes to only transmit modified data, based on * client's starting assets. */ static void postAllFiles(int clientId = -1) { lmLog(gAssetAgentLogGroup, "Queueing all files for client %d.", clientId); loom_mutex_lock(gFileScannerLock); // Walk all the files. utArray<FileEntry> *list = lmNew(NULL) utArray<FileEntry>(); platform_walkFiles(".", handleFileStateWalkCallback, list); // Queue them all to be sent. for (UTsize i = 0; i < list->size(); i++) { FileModificationNote note; note.path = stringtable_insert((*list)[i].path.c_str()); note.lastSeenTime = 0; note.onlyForClient = clientId; gPendingModifications.push_back(note); } loom_mutex_unlock(gFileScannerLock); }
void loom_asset_unlock( const char *name ) { // Hack to report usage. //size_t allocBytes, allocCount; //loom_allocator_getTrackerProxyStats(gAssetAllocator, &allocBytes, &allocCount); //lmLogError(gAssetLogGroup, "Seeing %d bytes of allocator and %d allocations", allocBytes, allocCount); loom_mutex_lock(gAssetLock); // TODO: This needs to be against the blob we locked NOT the asset's // current state. // Look it up. loom_asset_t *asset = loom_asset_getAssetByName(name, 0); // Assert if not loaded. lmAssert(asset, "Could not find asset '%s' to unlock!", name); //lmAssert(asset->blob, "Asset was not locked!"); if(asset->state == loom_asset_t::Loaded) { // Dec count. if(asset->blob->decRef()) { asset->state = loom_asset_t::Unloaded; asset->blob = NULL; } } else { // Nothing - it's not loaded. lmLogWarn(gAssetLogGroup, "Couldn't unlock '%s' as it was not loaded.", name); } loom_mutex_unlock(gAssetLock); }
void NativeDelegate::executeDeferredCalls(lua_State *L) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Try to resolve the delegate pointer. utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } else { // No delegate list, can't do it. loom_mutex_unlock(gCallNoteMutex); return; } for(unsigned int i=0; i<gNDCallNoteQueue.size(); i++) { NativeDelegateCallNote *ndcn = gNDCallNoteQueue[i]; bool found = false; for(unsigned int i=0; i<delegates->size(); i++) { // Look for our delegate. if((*delegates)[i] != ndcn->delegate) continue; // If key mismatches, warn and bail. if((*delegates)[i]->_key != ndcn->delegateKey) { lmLogError(gNativeDelegateGroup, "Found delegate call note with key mismatch (delegate=%x actualKey=%x expectedKey=%x), ignoring...", (*delegates)[i], (*delegates)[i]->_key, ndcn->delegateKey); break; } // Match! found = true; break; } // Bail if no match. if(!found) continue; // Otherwise, let's call it. const NativeDelegate *theDelegate = ndcn->delegate; for(;;) { unsigned char actionType = ndcn->readByte(); bool done = false; char *str = NULL; utByteArray *bytes; switch(actionType) { case MSG_Nop: lmLogError(gNativeDelegateGroup, "Got a nop in delegate data stream."); break; case MSG_PushString: str = ndcn->readString(); theDelegate->pushArgument(str); free(str); break; case MSG_PushByteArray: bytes = ndcn->readByteArray(); theDelegate->pushArgument(bytes); free(bytes); break; case MSG_PushDouble: theDelegate->pushArgument(ndcn->readDouble()); break; case MSG_PushFloat: theDelegate->pushArgument(ndcn->readFloat()); break; case MSG_PushInt: theDelegate->pushArgument((int)ndcn->readInt()); break; case MSG_PushBool: theDelegate->pushArgument(ndcn->readBool()); break; case MSG_Invoke: theDelegate->invoke(); done = true; break; } if(done) break; } } // Purge queue. gNDCallNoteQueue.clear(); loom_mutex_unlock(gCallNoteMutex); }
// Take a difference report from compareFileEntries and issue appropriate // file modification notes, and check whether they have settled. If so, // transmit updates to clients. static void processFileEntryDeltas(utArray<FileEntryDelta> *deltas) { int curTime = platform_getMilliseconds(); loom_mutex_lock(gFileScannerLock); // Update the pending list with all the stuff we've seen. for (UTsize i = 0; i < deltas->size(); i++) { // Get the delta. const FileEntryDelta& fed = deltas->at(i); // If it's removal, we don't currently send a notification. if (fed.action == FileEntryDelta::Removed) { continue; } // If it's not whitelisted, ignore it. if (!checkInWhitelist(fed.path)) { continue; } // Note it in the pending modification list. bool sawInList = false; for (UTsize i = 0; i < gPendingModifications.size(); i++) { FileModificationNote& fmn = gPendingModifications.at(i); if (strcmp(fmn.path, fed.path.c_str())) { continue; } // Match - update time. lmLogDebug(gAssetAgentLogGroup, "FILE CHANGING - '%s'", fed.path.c_str()); fmn.lastSeenTime = curTime; sawInList = true; } if (!sawInList) { FileModificationNote fmn; fmn.path = stringtable_insert(fed.path.c_str()); fmn.lastSeenTime = curTime; gPendingModifications.push_back(fmn); lmLogDebug(gAssetAgentLogGroup, "FILE CHANGED - '%s'", fed.path.c_str()); } } // Now, walk the pending list and send everyone who hasn't been touched for the settling period. // See how many files we're sending and note that state. const int settleTimeMs = 750; int transferStartTime = platform_getMilliseconds(); int totalPendingTransfers = 0; for (UTsize i = 0; i < gPendingModifications.size(); i++) { // Only consider pending items that have aged out. FileModificationNote& fmn = gPendingModifications.at(i); if (curTime - fmn.lastSeenTime < settleTimeMs) { continue; } totalPendingTransfers++; } bool didWeNotifyUserAboutPending = false; for (UTsize i = 0; i < gPendingModifications.size(); i++) { // Only consider pending items that have aged out. FileModificationNote& fmn = gPendingModifications.at(i); if (curTime - fmn.lastSeenTime < settleTimeMs) { continue; } // Make the path canonical. utString filename = fmn.path; char canonicalFile[MAXPATHLEN]; makeAssetPathCanonical(filename.c_str(), canonicalFile); // Note: we don't deal with deleted files properly (by uploading new state) because realpath // only works right when the file exists. So we just skip doing anything about it. // Note we are using gActiveHandlers.size() outside of a lock, but this is ok as it's a word. if ((strstr(canonicalFile, ".loom") || strstr(canonicalFile, ".ls")) && (gActiveHandlers.size() > 0)) { lmLog(gAssetAgentLogGroup, "Changed '%s'", canonicalFile); } if (canonicalFile[0] == 0) { lmLog(gAssetAgentLogGroup, " o Ignoring file missing from the asset folder!"); // Remove from the pending list. gPendingModifications.erase(i); i--; continue; } // Queue the callback. enqueueFileChangeCallback(canonicalFile); // Map the file. void *fileBits = NULL; long fileBitsLength = 0; if (!platform_mapFile(canonicalFile, &fileBits, &fileBitsLength)) { lmLog(gAssetAgentLogGroup, " o Skipping due to file failing to map."); continue; } // Loop over the active sockets. loom_mutex_lock(gActiveSocketsMutex); // Blast it out to all clients. for (UTsize j = 0; j < gActiveHandlers.size(); j++) { // If it's for a specific client then only send to that client. if ((fmn.onlyForClient != -1) && (fmn.onlyForClient != gActiveHandlers[j]->getId())) { continue; } gActiveHandlers[j]->sendFile(canonicalFile, fileBits, fileBitsLength, totalPendingTransfers); // If it has been more than a second, note that we are still working. const int remainingTransferCount = (totalPendingTransfers * gActiveHandlers.size()) - j; if (((platform_getMilliseconds() - transferStartTime) > 2000) && (remainingTransferCount > 1)) { transferStartTime = platform_getMilliseconds(); lmLog(gAssetAgentLogGroup, "Still transferring files. %d to go!", remainingTransferCount - 1); didWeNotifyUserAboutPending = true; } } loom_mutex_unlock(gActiveSocketsMutex); totalPendingTransfers--; // Unmap the file. platform_unmapFile(fileBits); // Remove from the pending list. gPendingModifications.erase(i); i--; } loom_mutex_unlock(gFileScannerLock); if (didWeNotifyUserAboutPending) { lmLog(gAssetAgentLogGroup, "Done transferring files!"); } }
// 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; }
StringTableEntry stringtable_insert(const char *str) { unsigned long hash_result; stringTableEntry_t *walk = NULL; StringTableEntry result = NULL; int bucket; // Hash the string. hash_result = hash(str); // Determine the hash table bucket. bucket = hash_result % csmTableSize; // TODO: Make this a readwrite lock, since most of the time we are just // traversing the stringtable to find existing data, not inserting. loom_mutex_lock(gTableMutex); // Walk the chain, if any. walk = gTable[bucket]; // Empty bucket, easy case! if (!walk) { gTable[bucket] = allocEntry(str); assert(gTable[bucket]->string); result = gTable[bucket]->string; loom_mutex_unlock(gTableMutex); return result; } while (walk) { // Is it a match? if (strcmp(walk->string, str) == 0) { assert(walk->string); result = (StringTableEntry)walk->string; loom_mutex_unlock(gTableMutex); return result; } // Another one to check? if (walk->next != NULL) { walk = walk->next; continue; } break; } // No match. Chain 'er on. walk->next = allocEntry(str); assert(walk->next->string); result = walk->next->string; loom_mutex_unlock(gTableMutex); return result; }
// Service our connection to the asset agent. static void loom_asset_serviceServer() { loom_mutex_lock(gAssetServerSocketLock); // Try to connect to the asset server if we aren't already, and it is set. if ((gAssetServerSocket == NULL) && ((ASSET_STREAM_HOST != NULL) && (strlen(ASSET_STREAM_HOST) > 0)) && ((platform_getMilliseconds() - gAssetServerLastConnectTryTime) > gAssetServerConnectTryInterval)) { lmLog(gAssetLogGroup, "Attempting to stream assets from %s:%d", ASSET_STREAM_HOST, ASSET_STREAM_PORT); gAssetServerLastConnectTryTime = platform_getMilliseconds(); gAssetServerSocket = loom_net_openTCPSocket(ASSET_STREAM_HOST, ASSET_STREAM_PORT, 0); gAssetConnectionOpen = false; loom_asset_notifyPendingCountChange(); loom_mutex_unlock(gAssetServerSocketLock); return; } if ((gAssetServerSocket != NULL) && (gAssetConnectionOpen == false)) { // We are waiting on the connection, see if it's writable... If not, return. if (loom_net_isSocketWritable(gAssetServerSocket) == 0) { loom_mutex_unlock(gAssetServerSocketLock); return; } if (loom_net_isSocketDead(gAssetServerSocket) == 1) { // Might be DOA, ie, connect failed. lmLog(gAssetLogGroup, "Failed to connect to asset server %s:%d", ASSET_STREAM_HOST, ASSET_STREAM_PORT); loom_net_closeTCPSocket(gAssetServerSocket); gAssetServerSocket = NULL; lmSafeDelete(NULL, gAssetProtocolHandler); gAssetConnectionOpen = false; loom_asset_notifyPendingCountChange(); loom_mutex_unlock(gAssetServerSocketLock); return; } lmLog(gAssetLogGroup, "Successfully connected to asset server %s:%d!", ASSET_STREAM_HOST, ASSET_STREAM_PORT); // Do this now to avoid clobbering error state and seeing the socket as // "open" when it is really dead. loom_net_enableSocketKeepalive(gAssetServerSocket); gAssetConnectionOpen = true; loom_asset_notifyPendingCountChange(); // Make sure we have a protocol handler. if (!gAssetProtocolHandler) { gAssetProtocolHandler = lmNew(NULL) AssetProtocolHandler(gAssetServerSocket); gAssetProtocolHandler->registerListener(lmNew(NULL) AssetProtocolFileMessageListener()); gAssetProtocolHandler->registerListener(lmNew(NULL) AssetProtocolCommandListener()); } loom_mutex_unlock(gAssetServerSocketLock); return; } // See if the socket is dead, and if so, clean up. if ((gAssetServerSocket != NULL) && (loom_net_isSocketDead(gAssetServerSocket) == 1)) { lmLog(gAssetLogGroup, "Lost connection to asset server."); loom_net_closeTCPSocket(gAssetServerSocket); gAssetServerSocket = NULL; lmSafeDelete(NULL, gAssetProtocolHandler); gAssetConnectionOpen = false; loom_asset_notifyPendingCountChange(); loom_mutex_unlock(gAssetServerSocketLock); return; } // Bail if we don't have a connection. if (!gAssetServerSocket || !gAssetConnectionOpen) { loom_mutex_unlock(gAssetServerSocketLock); return; } // Ping if we need to. if (platform_getMilliseconds() - gAssetServerLastPingTime > gAssetServerPingInterval) { gAssetProtocolHandler->sendPing(); gAssetServerLastPingTime = platform_getMilliseconds(); } // Service the asset server connection. gAssetProtocolHandler->process(); loom_mutex_unlock(gAssetServerSocketLock); }