/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2014 Oleksandr Shneyder */ /* Copyright (c) 2011-2016 Mike Gabriel */ /* Copyright (c) 2014-2016 Mihai Moldovan */ /* Copyright (c) 2014-2016 Ulrich Sibiller */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXAGENT, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE which comes in the source */ /* distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ #include "dixstruct.h" #include "scrnintstr.h" #include "windowstr.h" #include "osdep.h" #include "Agent.h" #include "Handlers.h" #include "Display.h" #include "Events.h" #include "Client.h" #include "Reconnect.h" #include "Dialog.h" #include "Drawable.h" #include "Xdmcp.h" #include "Screen.h" #include "Millis.h" #define Window XlibWindow #include "compext/Compext.h" #undef Window #include /* * Set here the required log level. */ #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP /* * Log begin and end of the important handlers. */ #undef BLOCKS /* * If not defined, flush immediately upon entering the block handler. */ #define FLUSH_AFTER_MULTIPLE_READS /* * The soft limit should roughly match the size of the Xlib I/O * buffer. */ #define BYTES_BEFORE_SOFT_TOKEN 2048 #define BYTES_BEFORE_HARD_TOKEN 65536 /* * Maximum number of synchronization requests before waiting for the * remote. */ #define TOKENS_PENDING_LIMIT 8 /* * Limits are very unobtrusive. We don't want to interfere with the * normal dispatching. */ #define BYTES_BEFORE_YIELD 1048576 #define TIME_BEFORE_YIELD 500 /* * Dynamically reduce the display buffer size after a congestion. */ #undef DYNAMIC_DISPLAY_BUFFER /* * Minimum display buffer size. */ #define MINIMUM_DISPLAY_BUFFER 512 #ifdef NX_DEBUG_INPUT extern int nxagentDebugInputDevices; extern unsigned long nxagentLastInputDevicesDumpTime; extern void nxagentDumpInputDevicesState(void); #endif /* * Used in the handling of the X desktop manager protocol. */ int nxagentXdmcpUp = 0; int nxagentXdmcpAlertUp = 0; /* * Also used in the block, wakeup and sync handlers. */ int nxagentBuffer; int nxagentBlocking; int nxagentCongestion; double nxagentBytesIn; double nxagentBytesOut; /* * Total number of descriptors ready as reported by the wakeup * handler. */ int nxagentReady; /* * Timestamp of the last write to the remote display. */ int nxagentFlush; /* * Arbitrate the bandwidth among our clients. */ struct _TokensRec nxagentTokens = { 0, 0, 0 }; struct _DispatchRec nxagentDispatch = { UNDEFINED, 0, 0, 0 }; /* * Called just before blocking, waiting for our clients or the X * server. */ extern int nxagentSkipImage; void nxagentBlockHandler(void * data, struct timeval **timeout, void * mask) { /* * Zero timeout. */ static struct timeval zero; /* * Current timestamp. */ static int now; /* * Pending bytes to write to the network. */ static int flushable; /* * Set if we need to synchronize any drawable. */ static int synchronize; #ifdef BLOCKS fprintf(stderr, "[Begin block]\n"); #endif now = GetTimeInMillis(); #ifdef NX_DEBUG_INPUT if (nxagentDebugInputDevices == 1 && now - nxagentLastInputDevicesDumpTime > 5000) { nxagentLastInputDevicesDumpTime = now; nxagentDumpInputDevicesState(); } #endif if (nxagentNeedConnectionChange() == 1) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Calling nxagentHandleConnectionChanges " "with ioError [%d] sigHup [%d].\n", nxagentException.ioError, nxagentException.sigHup); #endif nxagentHandleConnectionChanges(); } if (nxagentOption(Rootless) && nxagentLastWindowDestroyed && nxagentRootlessDialogPid == 0 && now > nxagentLastWindowDestroyedTime + 30 * 1000 && !nxagentOption(NoRootlessExit)) { #ifdef WARNING fprintf(stderr, "nxagentBlockHandler: No application running. Closing the session.\n"); #endif nxagentTerminateSession(); } #ifdef TEST if (nxagentLastWindowDestroyed) { fprintf(stderr, "nxagentBlockHandler: Elapsed time [%lu].\n", now - nxagentLastWindowDestroyedTime); } #endif /* * Slow down the agent if the session is not connected to a valid * display. */ if (NXDisplayError(nxagentDisplay) == 1 && nxagentShadowCounter == 0 && nxagentOption(SleepTimeMillis) > 0) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: sleeping for %d milliseconds for slowdown.\n", nxagentOption(SleepTimeMillis)); #endif usleep(nxagentOption(SleepTimeMillis) * 1000); now = GetTimeInMillis(); } #ifdef TEST else if (0 == nxagentOption(SleepTimeMillis)) { fprintf(stderr, "nxagentBlockHandler: not sleeping for slowdown.\n"); } #endif /* * Update the shadow display. This is only for test purposes. */ #ifdef DUMP nxagentPixmapOnShadowDisplay(NULL); nxagentFbOnShadowDisplay(); #endif /* * We need this here because some window configuration changes can * be generated by the X server outside the control of the DIX. */ nxagentFlushConfigureWindow(); /* * Check whether there is any drawable to synchronize. */ #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Checking synchronization at %s with " "[%d][%d][%d].\n", GetTimeInMillisAsString(), nxagentCorruptedWindows, nxagentCorruptedBackgrounds, nxagentCorruptedPixmaps); #endif synchronize = (nxagentCorruptedWindows > 0 || nxagentCorruptedBackgrounds > 0 || nxagentCorruptedPixmaps > 0); /* * The synchronization function requires a mask as parameter: * * EVENT_BREAK Breaks if an user input, like a key press * or a mouse move, is detected. * * CONGESTION_BREAK Breaks if the congestion becomes greater * than 4. * * BLOCKING_BREAK Breaks if the display descriptor becomes * blocked for write during the loop. * * ALWAYS_BREAK Any of the previous conditions is met. * * NEVER_BREAK The loop continues until all the drawables * are synchronized. */ if (synchronize == 1) { /* * We should not enter the synchronization loop if there is any * user input pending, i.e. if we are in the middle of a scroll * operation. */ if (nxagentForceSynchronization == 1) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Going to force a synchronization at %s.\n", GetTimeInMillisAsString()); #endif nxagentSynchronizationLoop(NEVER_BREAK); nxagentForceSynchronization = 0; } else if (nxagentUserInput(NULL) == 0 && nxagentBlocking == 0 && nxagentCongestion <= 4) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Going to synchronize at %s.\n", GetTimeInMillisAsString()); #endif nxagentSynchronizationLoop(ALWAYS_BREAK); } #ifdef TEST else { fprintf(stderr, "nxagentBlockHandler: Not synchronizing with [%d][%d].\n", nxagentBlocking, nxagentCongestion); } #endif /* * Check if we have more corrupted resources and whether the * conditions are satisfied to continue with the synchronization. */ synchronize = (nxagentCongestion <= 4 && (nxagentCorruptedWindows > 0 || nxagentCorruptedBackgrounds > 0 || nxagentCorruptedPixmaps > 0)); if (nxagentSkipImage == 0 && synchronize == 1) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Setting a zero timeout with [%d][%d][%d] and " "congestion [%d].\n", nxagentCorruptedWindows, nxagentCorruptedBackgrounds, nxagentCorruptedPixmaps, nxagentCongestion); #endif zero.tv_sec = 0; zero.tv_usec = 0; *timeout = &zero; } #ifdef TEST else { if (nxagentCorruptedWindows == 0 && nxagentCorruptedBackgrounds == 0 && nxagentCorruptedPixmaps == 0) { fprintf(stderr, "nxagentBlockHandler: Nothing more to synchronize at %s.\n", GetTimeInMillisAsString()); } else { fprintf(stderr, "nxagentBlockHandler: Delaying synchronization with [%d][%d][%d] and " "congestion [%d].\n", nxagentCorruptedWindows, nxagentCorruptedBackgrounds, nxagentCorruptedPixmaps, nxagentCongestion); } } #endif } /* * If the remote X server is blocking, reduce the amount of data * sent in a single display update by reducing the size of the * display buffer. */ #ifdef DYNAMIC_DISPLAY_BUFFER if (nxagentBlocking == 1 && nxagentBuffer > MINIMUM_DISPLAY_BUFFER) { nxagentBuffer >>= 1; if (nxagentBuffer < MINIMUM_DISPLAY_BUFFER) { nxagentBuffer = MINIMUM_DISPLAY_BUFFER; } #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Reducing the display buffer to [%d] bytes.\n", nxagentBuffer); #endif NXSetDisplayBuffer(nxagentDisplay, nxagentBuffer); } else if (nxagentBuffer < nxagentOption(DisplayBuffer) && nxagentCongestion == 0) { nxagentBuffer = nxagentOption(DisplayBuffer); #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Increasing the display buffer to [%d] bytes.\n", nxagentBuffer); #endif NXSetDisplayBuffer(nxagentDisplay, nxagentBuffer); } #endif /* #ifdef DYNAMIC_DISPLAY_BUFFER */ /* * Dispatch to the clients the events that may have become * available. */ if (nxagentPendingEvents(nxagentDisplay) > 0) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Reading the events available.\n"); #endif nxagentDispatchEvents(NULL); } /* * Check if there is any data remaining, either in the display * buffer or in the NX transport. */ flushable = NXDisplayFlushable(nxagentDisplay); if (flushable > 0) { #ifdef FLUSH_AFTER_MULTIPLE_READS /* * Flush all the outstanding data if the wakeup handler didn't * detect any activity. */ if (nxagentReady == 0 || now - nxagentFlush >= nxagentOption(DisplayCoalescence)) { #ifdef DEBUG fprintf(stderr, "nxagentBlockHandler: Flushing the display with [%d] bytes to flush.\n", flushable); #endif NXFlushDisplay(nxagentDisplay, NXFlushLink); /* * New events may have become available after the flush. */ if (nxagentPendingEvents(nxagentDisplay) > 0) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Reading the events available.\n"); #endif nxagentDispatchEvents(NULL); } } else { #ifdef DEBUG fprintf(stderr, "nxagentBlockHandler: Delaying flush with [%d][%d] and [%d] bytes.\n", synchronize, nxagentReady, flushable); #endif zero.tv_sec = 0; zero.tv_usec = 0; *timeout = &zero; } #else /* #ifdef FLUSH_AFTER_MULTIPLE_READS */ /* * We are entering the select. Tell the NX transport to write any * produced data to the remote end. */ NXFlushDisplay(nxagentDisplay, NXFlushLink); if (nxagentPendingEvents(nxagentDisplay) > 0) { #ifdef TEST fprintf(stderr, "nxagentBlockHandler: Reading the events available.\n"); #endif nxagentDispatchEvents(NULL); } #endif /* #ifdef FLUSH_AFTER_MULTIPLE_READS */ } else { #ifdef DEBUG fprintf(stderr, "nxagentBlockHandler: Nothing to flush with [%d][%d].\n", synchronize, nxagentReady); #endif if (NXDisplayError(nxagentDisplay) == 0 && nxagentQueuedEvents(nxagentDisplay) > 0) { #ifdef WARNING fprintf(stderr, "nxagentBlockHandler: WARNING! Forcing a null timeout with events queued.\n"); #endif zero.tv_sec = 0; zero.tv_usec = 0; *timeout = &zero; } } /* * WaitForSomething() sets a zero timeout if there are clients with * input, but doesn't stop the timer. The select is then interrupted * to update the schedule time even if, what the dispatcher cares, * is only the number of ticks at the time the client is scheduled * in. */ #ifdef DEBUG fprintf(stderr, "nxagentBlockHandler: Stopping the smart schedule timer.\n"); #endif nxagentStopTimer(); nxagentPrintGeometry(); #ifdef BLOCKS fprintf(stderr, "[End block]\n"); #endif } void nxagentWakeupHandler(void * data, int count, void * mask) { #ifdef BLOCKS fprintf(stderr, "[Begin wakeup]\n"); #endif if (nxagentException.sigHup || nxagentException.ioError) { #ifdef TEST fprintf(stderr,"nxagentWakeupHandler: Got SIGHUP or I/O error.\n"); #endif #ifdef TEST fprintf(stderr, "nxagentWakeupHandler: Calling nxagentHandleConnectionStates " "with ioError [%d] sigHup [%d].\n", nxagentException.ioError, nxagentException.sigHup); #endif nxagentHandleConnectionStates(); } if (!SmartScheduleSignalEnable) { #ifdef DEBUG fprintf(stderr, "nxagentWakeupHandler: Resetting the dispatch state after wakeup.\n"); #endif nxagentDispatch.start = GetTimeInMillis(); nxagentDispatch.in = nxagentBytesIn; nxagentDispatch.out = nxagentBytesOut; } /* * Can become true during the dispatch loop. */ nxagentBlocking = 0; /* * Check if we got new events. */ if (count > 0 && FD_ISSET(nxagentXConnectionNumber, (fd_set *) mask)) { #ifdef TEST fprintf(stderr, "nxagentWakeupHandler: Reading the X events with count [%d].\n", count); #endif nxagentDispatchEvents(NULL); #ifdef TEST fprintf(stderr, "nxagentWakeupHandler: Removing the X descriptor from the count.\n"); #endif FD_CLR(nxagentXConnectionNumber, (fd_set *) mask); count--; } else if (nxagentQueuedEvents(nxagentDisplay) == 1) { /* * We may have left some events in the queue. */ #ifdef TEST fprintf(stderr, "nxagentWakeupHandler: Reading the queued X events with count [%d].\n", count); #endif nxagentDispatchEvents(NULL); } /* * Save the number of descriptors ready. */ if (count <= 0) { count = (XFD_ANYSET(&ClientsWithInput) ? 1 : 0); } nxagentReady = count; #ifdef TEST if (nxagentReady == 0) { fprintf(stderr, "nxagentWakeupHandler: No X clients found to be processed.\n"); } #endif /* * If the XDM connection can't be established we'll need to create a * dialog to notify the user and give him/her a chance to terminate * the session. */ if (nxagentOption(Xdmcp) && nxagentXdmcpUp == 0) { #ifdef DEBUG fprintf(stderr, "nxagentWakeupHandler: XdmcpState [%d].\n", XdmcpState); #endif if (XdmcpState == XDM_RUN_SESSION) { nxagentXdmcpUp = 1; } if (nxagentXdmcpUp == 0) { #ifdef DEBUG fprintf(stderr, "nxagentWakeupHandler: XdmcpTime [%lu].\n", GetTimeInMillis() - XdmcpStartTime); #endif #ifdef DEBUG fprintf(stderr, "nxagentWakeupHandler: XdmcpTimeOutRtx [%d].\n", XdmcpTimeOutRtx); #endif if (nxagentXdmcpAlertUp == 0 && GetTimeInMillis() - XdmcpStartTime >= XDM_TIMEOUT) { #ifdef WARNING fprintf(stderr, "nxagentWakeupHandler: WARNING! The XDM session seems to be " "unable to start after [%ld] ms.\n",(long int)(GetTimeInMillis() - XdmcpStartTime)); #endif NXTransAlert(FAILED_XDMCP_CONNECTION_ALERT, NX_ALERT_REMOTE); nxagentXdmcpAlertUp = 1; } } } #ifdef BLOCKS fprintf(stderr, "[End wakeup]\n"); #endif } void nxagentShadowBlockHandler(void * data, struct timeval **timeout, void * mask) { static struct timeval zero; int changed; int suspended = 0; int width_, height_; #ifdef BLOCKS fprintf(stderr, "[Begin block]\n"); #endif if (nxagentNeedConnectionChange() == 1) { nxagentHandleConnectionChanges(); } if (nxagentSessionState == SESSION_DOWN && nxagentOption(SleepTimeMillis) > 0) { #ifdef TEST fprintf(stderr, "nxagentShadowBlockHandler: sleeping for %d milliseconds for slowdown.\n", nxagentOption(SleepTimeMillis)); #endif usleep(nxagentOption(SleepTimeMillis) * 1000); } #ifdef TEST else if (0 == nxagentOption(SleepTimeMillis)) { fprintf(stderr, "nxagentShadowBlockHandler: not sleeping for slowdown.\n"); } #endif if (nxagentReadEvents(nxagentDisplay) > 0 || nxagentReadEvents(nxagentShadowDisplay) > 0) { #ifdef TEST fprintf(stderr, "nxagentShadowBlockHandler: Reading X events queued.\n"); #endif nxagentDispatchEvents(NULL); } if (nxagentShadowResize) { nxagentShadowResize = False; nxagentShadowAdaptToRatio(); } changed = 0; NXShadowGetScreenSize(&width_, &height_); if (width_ != nxagentShadowWidth || height_ != nxagentShadowHeight) { /* * The master session has been resized. */ NXShadowSetScreenSize(&nxagentShadowWidth, &nxagentShadowHeight); nxagentShadowAdaptToRatio(); } nxagentShadowPoll(nxagentShadowPixmapPtr, nxagentShadowGCPtr, nxagentShadowDepth, nxagentShadowWidth, nxagentShadowHeight, nxagentShadowBuffer, &changed, &suspended); nxagentShadowSendUpdates(&suspended); if (nxagentBlocking == 0) { nxagentSynchronizeDrawable((DrawablePtr) nxagentShadowPixmapPtr, DONT_WAIT, ALWAYS_BREAK, nxagentShadowWindowPtr); } /* * We are entering the select. Tell the NX transport to write any * produced data to the remote end. */ /* FIXME: Must queue multiple writes and handle the events by resembling the ordinary block handler. */ NXFlushDisplay(nxagentDisplay, NXFlushLink); if (*timeout == NULL) { *timeout = &zero; } if (changed == 0) { (*timeout) -> tv_sec = 0; (*timeout) -> tv_usec = 50 * 1000; } else { (*timeout) -> tv_sec = 0; (*timeout) -> tv_usec = 0; } nxagentPrintGeometry(); #ifdef BLOCKS fprintf(stderr, "[End block]\n"); #endif } void nxagentShadowWakeupHandler(void * data, int count, void * mask) { #ifdef BLOCKS fprintf(stderr, "[Begin wakeup]\n"); #endif if (nxagentException.sigHup || nxagentException.ioError) { #ifdef TEST fprintf(stderr,"nxagentShadowWakeupHandler: Got SIGHUP or I/O error.\n"); #endif nxagentHandleConnectionStates(); } if (!SmartScheduleSignalEnable) { #ifdef DEBUG fprintf(stderr, "nxagentShadowWakeupHandler: Resetting the dispatch state after wakeup.\n"); #endif nxagentDispatch.start = GetTimeInMillis(); nxagentDispatch.in = nxagentBytesIn; nxagentDispatch.out = nxagentBytesOut; } /* * Can become true during the dispatch loop. */ nxagentBlocking = 0; /* * Check if we got new events. */ if (count > 0 && FD_ISSET(nxagentXConnectionNumber, (fd_set *) mask)) { #ifdef TEST fprintf(stderr, "nxagentShadowWakeupHandler: Reading the X events with count [%d].\n", count); #endif nxagentDispatchEvents(NULL); #ifdef TEST fprintf(stderr, "nxagentShadowWakeupHandler: Removing the X descriptor from the count.\n"); #endif FD_CLR(nxagentXConnectionNumber, (fd_set *) mask); count--; } else if (nxagentQueuedEvents(nxagentDisplay) == 1) { /* * We may have left some events in the queue. */ #ifdef TEST fprintf(stderr, "nxagentShadowWakeupHandler: Reading the queued X events with count [%d].\n", count); #endif nxagentDispatchEvents(NULL); } /* * Save the number of descriptors ready. */ if (count <= 0) { count = (XFD_ANYSET(&ClientsWithInput) ? 1 : 0); } nxagentReady = count; #ifdef TEST if (nxagentReady == 0) { fprintf(stderr, "nxagentShadowWakeupHandler: No X clients found to be processed.\n"); } #endif #ifdef BLOCKS fprintf(stderr, "[End wakeup]\n"); #endif } void nxagentHandleCollectInputFocusEvent(int resource) { /* * While we don't even need window or revert_to later on, a discrepancy in * data type sizes between the X server (Window being a 32bit ID) and * the Xlib (Window being a 64bit ID) will lead to stack corruption here. * Calling functions from CompExt from nxagent sounds like a very bad idea * to begin with, but let's assume that's necessary for now and work around * the corruption issue. * * Even though the CompExt header shows that the function expects a Window-sized * parameter, it's not the Window type as defined and used within the X.Org * Server, but an Xlib type. Hence, we'll be using the "XlibWindow" type here * and to avoid compiler warnings, "rewrite" the CompExt.h header file via * overriding the original "Window" type with the XlibWindow type, including * the header file and undefining the macro again, essentially unshadowing * the original type. */ XlibWindow window; int revert_to; if (NXGetCollectedInputFocus(nxagentDisplay, resource, &window, &revert_to) == 0) { #ifdef PANIC fprintf(stderr, "nxagentHandleCollectInputFocusEvent: PANIC! Failed to get the input focus " "reply for resource [%d].\n", resource); #endif } #ifdef DEBUG fprintf(stderr, "nxagentHandleCollectInputFocusEvent: Received a sync reply with [%d] pending.\n", nxagentTokens.pending); #endif nxagentTokens.pending--; nxagentCongestion = (nxagentTokens.pending >= TOKENS_PENDING_LIMIT / 2); #ifdef TEST fprintf(stderr, "nxagentHandleCollectInputFocusEvent: Current congestion level is [%d].\n", nxagentCongestion); #endif } Bool nxagentCollectInputFocusPredicate(Display *disp, XEvent *X, XPointer ptr) { return (X -> xclient.window == 0 && X -> xclient.message_type == 0 && X -> xclient.format == 32 && X -> xclient.data.l[0] == NXCollectInputFocusNotify); } void nxagentDispatchHandler(ClientPtr client, int in, int out) { /* * This function is called by the dispatcher (with 0 bytes out) * after a new request has been processed. It is also called by the * write handler (with 0 bytes in) after more data has been written * to the display. It may be optionally called in the block and * wakeup handlers. In this case both in and out must be 0. */ if (out > 0) { /* * Called by the display write callback. */ #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Called with [%d] bytes written.\n", out); #endif nxagentBytesOut += out; #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Total bytes are [%.0f] in [%.0f] out.\n", nxagentBytesIn, nxagentBytesOut); #endif /* * Don't take care of the synchronization if the NX transport is * running. The NX transport has its own token-based control flow. * * We can't produce more output here because we are in the middle * of the flush. We will take care of the sync requests when * called by the dispatcher. */ if (nxagentOption(LinkType) == LINK_TYPE_NONE) { nxagentTokens.soft += out; if (out > BYTES_BEFORE_HARD_TOKEN) { nxagentTokens.hard += out; } #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Sync bytes accumulated are [%d] and [%d].\n", nxagentTokens.soft, nxagentTokens.hard); #endif } if (!SmartScheduleSignalEnable) { /* * Pay attention to the next client if this client produced * enough output. */ if (nxagentBytesOut - nxagentDispatch.out > BYTES_BEFORE_YIELD) { #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Yielding with [%ld][%.0f][%.0f] for client [%d].\n", GetTimeInMillis() - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in, nxagentBytesOut - nxagentDispatch.out, nxagentDispatch.client); #endif nxagentDispatch.start = GetTimeInMillis(); nxagentDispatch.in = nxagentBytesIn; nxagentDispatch.out = nxagentBytesOut; isItTimeToYield = 1; } #ifdef DEBUG else { fprintf(stderr, "nxagentDispatchHandler: Dispatching with [%ld][%.0f][%.0f] for client [%d].\n", GetTimeInMillis() - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in, nxagentBytesOut - nxagentDispatch.out, nxagentDispatch.client); } #endif } return; } else if (in > 0) { /* * Called by the dispatcher. */ #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Called with [%d] bytes processed for client [%d].\n", in, client -> index); #endif #ifdef COUNT_CLIENT_BYTES /* * This is presently unused. * * nxagentClientAddBytes(client, in); * * #ifdef DEBUG * fprintf(stderr, "nxagentDispatchHandler: Bytes processed for client [%d] are [%ld].\n", * client -> index, nxagentClientBytes(client)); * #endif * */ #endif nxagentBytesIn += in; #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Total bytes are [%.0f] in [%.0f] out.\n", nxagentBytesIn, nxagentBytesOut); #endif /* * When using the dumb scheduler, before reading from another * client, the dispatcher tries to drain all the input from the * client being processed. This means that, if isItTimeToYield is * never set and the client never produces any output, we'll stick * into the inner dispatch loop forever. */ /* * The behaviour described in the comment above also happens with the * smart scheduler if all of the following conditions are met: * - the agent is suspended * - SleepTimeMillis is set (the default is sufficient to trigger this) * - a client is doing a lot of image operations * - nxagentShadowCounter is 0 * In that case the agent will slow down the image operations by calling * an intermediate sleep resulting in the client's request queue never * being empty. Which in turn leads to the drain loop described above. * isItTimeToYield will then never be set. The (dramatic) result of this * is that nxagent will not process any signals and therefore cannot be * resumed anymore! * For this reason we do not limit below code to the dumb scheduler but also * run it if above conditions are met - ensuring regular yields. * See issue #903 */ if (!SmartScheduleSignalEnable || (nxagentShadowCounter == 0 && NXDisplayError(nxagentDisplay) == 1 && nxagentOption(SleepTimeMillis) > 0)) { if (client -> index != nxagentDispatch.client) { #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Resetting the dispatch state with [%d][%d].\n", nxagentDispatch.client, client -> index); #endif nxagentDispatch.client = client -> index; nxagentDispatch.start = GetTimeInMillis(); nxagentDispatch.in = nxagentBytesIn; nxagentDispatch.out = nxagentBytesOut; } else { static unsigned long int now; now = GetTimeInMillis(); if (now - nxagentDispatch.start > TIME_BEFORE_YIELD || nxagentBytesIn - nxagentDispatch.in > BYTES_BEFORE_YIELD) { #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Yielding with [%ld][%.0f][%.0f] for client [%d].\n", now - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in, nxagentBytesOut - nxagentDispatch.out, nxagentDispatch.client); #endif nxagentDispatch.start = now; nxagentDispatch.in = nxagentBytesIn; nxagentDispatch.out = nxagentBytesOut; isItTimeToYield = 1; } #ifdef DEBUG else { fprintf(stderr, "nxagentDispatchHandler: Dispatching with [%ld][%.0f][%.0f] for client [%d].\n", now - nxagentDispatch.start, nxagentBytesIn - nxagentDispatch.in, nxagentBytesOut - nxagentDispatch.out, nxagentDispatch.client); } #endif } } } /* * Let's see if it's time to sync. */ if (nxagentOption(LinkType) == LINK_TYPE_NONE) { if (nxagentTokens.hard > BYTES_BEFORE_HARD_TOKEN) { #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Requesting a hard sync reply with [%d] bytes.\n", nxagentTokens.hard); #endif XSync(nxagentDisplay, 0); if (nxagentPendingEvents(nxagentDisplay) > 0) { nxagentDispatchEvents(NULL); } nxagentTokens.soft = 0; nxagentTokens.hard = 0; } else if (nxagentTokens.soft > BYTES_BEFORE_SOFT_TOKEN) { /* * Alternatively, the amounts of bytes accounted for each sync * request may be decreased according to the number of pending * replies already awaited. * * else if (nxagentTokens.soft > (BYTES_BEFORE_SOFT_TOKEN / (nxagentTokens.pending + 1))) */ int resource; /* * Wait eventually for the number of synchronization requests to * return below the limit. */ #ifdef TEST if (nxagentTokens.pending == TOKENS_PENDING_LIMIT) { fprintf(stderr, "nxagentDispatchHandler: WARNING! Waiting for the synchronization reply.\n"); } #endif while (nxagentTokens.pending == TOKENS_PENDING_LIMIT) { if (nxagentWaitEvents(nxagentDisplay, 0) == -1) { nxagentTokens.pending = 0; nxagentTokens.soft = 0; return; } nxagentDispatchEvents(NULL); nxagentBlocking = 1; } /* * Send a new synchronization request. */ resource = nxagentWaitForResource(NXGetCollectInputFocusResource, nxagentCollectInputFocusPredicate); if (resource == -1) { #ifdef PANIC fprintf(stderr, "nxagentDispatchHandler: PANIC! Cannot allocate any valid resource.\n"); #endif nxagentTokens.soft = 0; return; } #ifdef DEBUG fprintf(stderr, "nxagentDispatchHandler: Requesting a sync reply with [%d] bytes " "and [%d] pending.\n", nxagentTokens.soft, nxagentTokens.pending); #endif NXCollectInputFocus(nxagentDisplay, resource); NXFlushDisplay(nxagentDisplay, NXFlushBuffer); if (nxagentPendingEvents(nxagentDisplay) > 0) { nxagentDispatchEvents(NULL); } nxagentTokens.pending++; nxagentCongestion = (nxagentTokens.pending >= TOKENS_PENDING_LIMIT / 2); #ifdef TEST fprintf(stderr, "nxagentDispatchHandler: Current congestion level is [%d].\n", nxagentCongestion); #endif nxagentTokens.soft = 0; } } /* * Check if there are events to read. */ if (nxagentPendingEvents(nxagentDisplay) > 0) { nxagentDispatchEvents(NULL); } }