/** This runs in a separate thread */ static int fpathThreadFunc(void *) { wzMutexLock(fpathMutex); while (!fpathQuit) { if (pathJobs.empty()) { ASSERT(!waitingForResult, "Waiting for a result (id %u) that doesn't exist.", waitingForResultId); wzMutexUnlock(fpathMutex); wzSemaphoreWait(fpathSemaphore); // Go to sleep until needed. wzMutexLock(fpathMutex); continue; } // Copy the first job from the queue. packagedPathJob job = std::move(pathJobs.front()); pathJobs.pop_front(); wzMutexUnlock(fpathMutex); job(); wzMutexLock(fpathMutex); waitingForResult = false; objTrace(waitingForResultId, "These are the droids you are looking for."); wzSemaphorePost(waitingForResultSemaphore); } wzMutexUnlock(fpathMutex); return 0; }
/** * Similar to write(2) with the exception that this function will block until * <em>all</em> data has been written or an error occurs. * * @return @c size when succesful or @c SOCKET_ERROR if an error occurred. */ ssize_t writeAll(Socket *sock, const void *buf, size_t size, size_t *rawByteCount) { size_t ignored; size_t &rawBytes = rawByteCount != NULL ? *rawByteCount : ignored; rawBytes = 0; if (!sock || sock->fd[SOCK_CONNECTION] == INVALID_SOCKET) { debug(LOG_ERROR, "Invalid socket (EBADF)"); setSockErr(EBADF); return SOCKET_ERROR; } if (sock->writeError) { return SOCKET_ERROR; } if (size > 0) { if (!sock->isCompressed) { wzMutexLock(socketThreadMutex); if (socketThreadWrites.empty()) { wzSemaphorePost(socketThreadSemaphore); } std::vector<uint8_t> &writeQueue = socketThreadWrites[sock]; writeQueue.insert(writeQueue.end(), static_cast<char const *>(buf), static_cast<char const *>(buf) + size); wzMutexUnlock(socketThreadMutex); rawBytes = size; } else { sock->zDeflate.next_in = (Bytef *)buf; sock->zDeflate.avail_in = size; sock->zDeflateInSize += sock->zDeflate.avail_in; do { size_t alreadyHave = sock->zDeflateOutBuf.size(); sock->zDeflateOutBuf.resize(alreadyHave + size + 20); // A bit more than size should be enough to always do everything in one go. sock->zDeflate.next_out = (Bytef *)&sock->zDeflateOutBuf[alreadyHave]; sock->zDeflate.avail_out = sock->zDeflateOutBuf.size() - alreadyHave; int ret = deflate(&sock->zDeflate, Z_NO_FLUSH); ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); // Remove unused part of buffer. sock->zDeflateOutBuf.resize(sock->zDeflateOutBuf.size() - sock->zDeflate.avail_out); } while (sock->zDeflate.avail_out == 0); ASSERT(sock->zDeflate.avail_in == 0, "zlib didn't compress everything!"); } } return size; }
void socketFlush(Socket *sock, size_t *rawByteCount) { size_t ignored; size_t &rawBytes = rawByteCount != NULL ? *rawByteCount : ignored; rawBytes = 0; if (!sock->isCompressed) { return; // Not compressed, so don't mess with zlib. } // Flush data out of zlib compression state. do { sock->zDeflate.next_in = (Bytef *)NULL; sock->zDeflate.avail_in = 0; size_t alreadyHave = sock->zDeflateOutBuf.size(); sock->zDeflateOutBuf.resize(alreadyHave + 1000); // 100 bytes would probably be enough to flush the rest in one go. sock->zDeflate.next_out = (Bytef *)&sock->zDeflateOutBuf[alreadyHave]; sock->zDeflate.avail_out = sock->zDeflateOutBuf.size() - alreadyHave; int ret = deflate(&sock->zDeflate, Z_PARTIAL_FLUSH); ASSERT(ret != Z_STREAM_ERROR, "zlib compression failed!"); // Remove unused part of buffer. sock->zDeflateOutBuf.resize(sock->zDeflateOutBuf.size() - sock->zDeflate.avail_out); } while (sock->zDeflate.avail_out == 0); if (sock->zDeflateOutBuf.empty()) { return; // No data to flush out. } wzMutexLock(socketThreadMutex); if (socketThreadWrites.empty()) { wzSemaphorePost(socketThreadSemaphore); } std::vector<uint8_t> &writeQueue = socketThreadWrites[sock]; writeQueue.insert(writeQueue.end(), sock->zDeflateOutBuf.begin(), sock->zDeflateOutBuf.end()); wzMutexUnlock(socketThreadMutex); // Primitive network logging, uncomment to use. //printf("Size %3u ->%3zu, buf =", sock->zDeflateInSize, sock->zDeflateOutBuf.size()); //for (unsigned n = 0; n < std::min<unsigned>(sock->zDeflateOutBuf.size(), 40); ++n) printf(" %02X", sock->zDeflateOutBuf[n]); //printf("\n"); // Data sent, don't send again. rawBytes = sock->zDeflateOutBuf.size(); sock->zDeflateInSize = 0; sock->zDeflateOutBuf.clear(); }
/** This runs in a separate thread */ static int fpathThreadFunc(void *) { wzMutexLock(fpathMutex); while (!fpathQuit) { if (pathJobs.empty()) { ASSERT(!waitingForResult, "Waiting for a result (id %u) that doesn't exist.", waitingForResultId); wzMutexUnlock(fpathMutex); wzSemaphoreWait(fpathSemaphore); // Go to sleep until needed. wzMutexLock(fpathMutex); continue; } // Copy the first job from the queue. Don't pop yet, since the main thread may want to set .deleted = true. PATHJOB job = pathJobs.front(); wzMutexUnlock(fpathMutex); // Execute path-finding for this job using our local temporaries PATHRESULT result; result.droidID = job.droidID; memset(&result.sMove, 0, sizeof(result.sMove)); result.retval = FPR_FAILED; result.originalDest = Vector2i(job.destX, job.destY); // we need to lock BEFORE we fiddle with the data, or we get ugly data race conditions. wzMutexLock(fpathMutex); fpathExecute(&job, &result); ASSERT(pathJobs.front().droidID == job.droidID, "Bug"); // The front of pathJobs may have .deleted set to true, but should not otherwise have been modified or deleted. if (!pathJobs.front().deleted) { pathResults.push_back(result); } pathJobs.pop_front(); // Unblock the main thread, if it was waiting for this particular result. if (waitingForResult && waitingForResultId == job.droidID) { waitingForResult = false; objTrace(waitingForResultId, "These are the droids you are looking for."); wzSemaphorePost(waitingForResultSemaphore); } } wzMutexUnlock(fpathMutex); return 0; }
void fpathShutdown() { // Signal the path finding thread to quit fpathQuit = true; wzSemaphorePost(fpathSemaphore); // Wake up thread. if (fpathThread) { wzThreadJoin(fpathThread); fpathThread = NULL; wzMutexDestroy(fpathMutex); fpathMutex = NULL; wzSemaphoreDestroy(fpathSemaphore); fpathSemaphore = NULL; wzSemaphoreDestroy(waitingForResultSemaphore); waitingForResultSemaphore = NULL; } fpathHardTableReset(); }
static FPATH_RETVAL fpathRoute(MOVE_CONTROL *psMove, unsigned id, int startX, int startY, int tX, int tY, PROPULSION_TYPE propulsionType, DROID_TYPE droidType, FPATH_MOVETYPE moveType, int owner, bool acceptNearest, StructureBounds const &dstStructure) { objTrace(id, "called(*,id=%d,sx=%d,sy=%d,ex=%d,ey=%d,prop=%d,type=%d,move=%d,owner=%d)", id, startX, startY, tX, tY, (int)propulsionType, (int)droidType, (int)moveType, owner); if (!worldOnMap(startX, startY) || !worldOnMap(tX, tY)) { debug(LOG_ERROR, "Droid trying to find path to/from invalid location (%d %d) -> (%d %d).", startX, startY, tX, tY); objTrace(id, "Invalid start/end."); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // don't have to do anything if already there if (startX == tX && startY == tY) { // return failed to stop them moving anywhere objTrace(id, "Tried to move nowhere"); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // Check if waiting for a result while (psMove->Status == MOVEWAITROUTE) { objTrace(id, "Checking if we have a path yet"); auto const &I = pathResults.find(id); ASSERT(I != pathResults.end(), "Missing path result promise"); PATHRESULT result = I->second.get(); ASSERT(result.retval != FPR_OK || result.sMove.asPath, "Ok result but no path in list"); // Copy over select fields - preserve others psMove->destination = result.sMove.destination; psMove->numPoints = result.sMove.numPoints; bool correctDestination = tX == result.originalDest.x && tY == result.originalDest.y; psMove->pathIndex = 0; psMove->Status = MOVENAVIGATE; free(psMove->asPath); psMove->asPath = result.sMove.asPath; FPATH_RETVAL retval = result.retval; ASSERT(retval != FPR_OK || psMove->asPath, "Ok result but no path after copy"); ASSERT(retval != FPR_OK || psMove->numPoints > 0, "Ok result but path empty after copy"); // Remove it from the result list pathResults.erase(id); objTrace(id, "Got a path to (%d, %d)! Length=%d Retval=%d", psMove->destination.x, psMove->destination.y, psMove->numPoints, (int)retval); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = %d, path[%d] = %08X->(%d, %d)", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner, retval, psMove->numPoints, ~crcSumVector2i(0, psMove->asPath, psMove->numPoints), psMove->destination.x, psMove->destination.y); if (!correctDestination) { goto queuePathfinding; // Seems we got the result of an old pathfinding job for this droid, so need to pathfind again. } return retval; } queuePathfinding: // We were not waiting for a result, and found no trivial path, so create new job and start waiting PATHJOB job; job.origX = startX; job.origY = startY; job.droidID = id; job.destX = tX; job.destY = tY; job.dstStructure = dstStructure; job.droidType = droidType; job.propulsion = propulsionType; job.moveType = moveType; job.owner = owner; job.acceptNearest = acceptNearest; job.deleted = false; fpathSetBlockingMap(&job); debug(LOG_NEVER, "starting new job for droid %d 0x%x", id, id); // Clear any results or jobs waiting already. It is a vital assumption that there is only one // job or result for each droid in the system at any time. fpathRemoveDroidData(id); packagedPathJob task([job]() { return fpathExecute(job); }); pathResults[id] = task.get_future(); // Add to end of list wzMutexLock(fpathMutex); bool isFirstJob = pathJobs.empty(); pathJobs.push_back(std::move(task)); wzMutexUnlock(fpathMutex); if (isFirstJob) { wzSemaphorePost(fpathSemaphore); // Wake up processing thread. } objTrace(id, "Queued up a path-finding request to (%d, %d), at least %d items earlier in queue", tX, tY, isFirstJob); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_WAIT", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_WAIT; // wait while polling result queue }
static FPATH_RETVAL fpathRoute(MOVE_CONTROL *psMove, int id, int startX, int startY, int tX, int tY, PROPULSION_TYPE propulsionType, DROID_TYPE droidType, FPATH_MOVETYPE moveType, int owner, bool acceptNearest) { objTrace(id, "called(*,id=%d,sx=%d,sy=%d,ex=%d,ey=%d,prop=%d,type=%d,move=%d,owner=%d)", id, startX, startY, tX, tY, (int)propulsionType, (int)droidType, (int)moveType, owner); // don't have to do anything if already there if (startX == tX && startY == tY) { // return failed to stop them moving anywhere objTrace(id, "Tried to move nowhere"); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // Check if waiting for a result while (psMove->Status == MOVEWAITROUTE) { objTrace(id, "Checking if we have a path yet"); wzMutexLock(fpathMutex); // psNext should be _declared_ here, after the mutex lock! Used to be a race condition, thanks to -Wdeclaration-after-statement style pseudocompiler compatibility. for (std::list<PATHRESULT>::iterator psResult = pathResults.begin(); psResult != pathResults.end(); ++psResult) { if (psResult->droidID != id) { continue; // Wrong result, try next one. } ASSERT(psResult->retval != FPR_OK || psResult->sMove.asPath, "Ok result but no path in list"); // Copy over select fields - preserve others psMove->destination = psResult->sMove.destination; psMove->numPoints = psResult->sMove.numPoints; psMove->Position = 0; psMove->Status = MOVENAVIGATE; free(psMove->asPath); psMove->asPath = psResult->sMove.asPath; FPATH_RETVAL retval = psResult->retval; ASSERT(retval != FPR_OK || psMove->asPath, "Ok result but no path after copy"); ASSERT(retval != FPR_OK || psMove->numPoints > 0, "Ok result but path empty after copy"); // Remove it from the result list pathResults.erase(psResult); wzMutexUnlock(fpathMutex); objTrace(id, "Got a path to (%d, %d)! Length=%d Retval=%d", psMove->destination.x, psMove->destination.y, psMove->numPoints, (int)retval); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = %d, path[%d] = %08X->(%d, %d)", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner, retval, psMove->numPoints, ~crcSumVector2i(0, psMove->asPath, psMove->numPoints), psMove->destination.x, psMove->destination.y); return retval; } objTrace(id, "No path yet. Waiting."); waitingForResult = true; waitingForResultId = id; wzMutexUnlock(fpathMutex); wzSemaphoreWait(waitingForResultSemaphore); // keep waiting } // We were not waiting for a result, and found no trivial path, so create new job and start waiting PATHJOB job; job.origX = startX; job.origY = startY; job.droidID = id; job.destX = tX; job.destY = tY; job.droidType = droidType; job.propulsion = propulsionType; job.moveType = moveType; job.owner = owner; job.acceptNearest = acceptNearest; job.deleted = false; fpathSetBlockingMap(&job); // Clear any results or jobs waiting already. It is a vital assumption that there is only one // job or result for each droid in the system at any time. fpathRemoveDroidData(id); wzMutexLock(fpathMutex); // Add to end of list bool isFirstJob = pathJobs.empty(); pathJobs.push_back(job); if (isFirstJob) { wzSemaphorePost(fpathSemaphore); // Wake up processing thread. } wzMutexUnlock(fpathMutex); objTrace(id, "Queued up a path-finding request to (%d, %d), at least %d items earlier in queue", tX, tY, isFirstJob); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_WAIT", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_WAIT; // wait while polling result queue }