diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp
index 9d5ec33fed74add89e7f34e6ee401a2e542638d5..63e540987607b2c3cdcd4d5ecd994c53546f479d 100644
--- a/indra/linux_crash_logger/linux_crash_logger.cpp
+++ b/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -51,7 +51,7 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	app.mainLoop();
+	app.frame();
 	app.cleanup();
 	LL_INFOS() << "Crash reporter finished normally." << LL_ENDL;
 	return 0;
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
index e2d2e7ff26949fdfbfdc9c81db9520badbf255f2..4092d43fc5fc029818bb8cac9d76c13f003b77cc 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.cpp
+++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
@@ -114,7 +114,7 @@ void LLCrashLoggerLinux::gatherPlatformSpecificFiles()
 {
 }
 
-bool LLCrashLoggerLinux::mainLoop()
+bool LLCrashLoggerLinux::frame()
 {
 	bool send_logs = true;
 	if(CRASH_BEHAVIOR_ASK == getCrashBehavior())
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.h b/indra/linux_crash_logger/llcrashloggerlinux.h
index dae6c46651ba642e401aa09874d4c2d2567eb92f..789f6f03f578b48dd2953b992f502e3fc085c957 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.h
+++ b/indra/linux_crash_logger/llcrashloggerlinux.h
@@ -36,7 +36,7 @@ class LLCrashLoggerLinux : public LLCrashLogger
 public:
 	LLCrashLoggerLinux(void);
 	~LLCrashLoggerLinux(void);
-	virtual bool mainLoop();
+	virtual bool frame();
 	virtual void updateApplication(const std::string& = LLStringUtil::null);
 	virtual void gatherPlatformSpecificFiles();
 	virtual bool cleanup();
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index d9933b3d369d21590bedc35c07c1a258b54f1e02..ff9a92b45f703418c1d2c03018af7fee8486566b 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -172,12 +172,12 @@ class LL_COMMON_API LLApp
 	virtual bool cleanup() = 0;			// Override to do application cleanup
 
 	//
-	// mainLoop()
+	// frame()
 	//
-	// Runs the application main loop.  It's assumed that when you exit
-	// this method, the application is in one of the cleanup states, either QUITTING or ERROR
+	// Pass control to the application for a single frame. Returns 'done'
+	// flag: if frame() returns false, it expects to be called again.
 	//
-	virtual bool mainLoop() = 0; // Override for the application main loop.  Needs to at least gracefully notice the QUITTING state and exit.
+	virtual bool frame() = 0; // Override for application body logic
 
 	//
 	// Crash logging
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h
index 8b4afae24ab4309744897afffdd4816f3762cb45..56e26c23ba3f49ae4264d1c1dbb9f075913bd813 100644
--- a/indra/llcrashlogger/llcrashlogger.h
+++ b/indra/llcrashlogger/llcrashlogger.h
@@ -57,7 +57,7 @@ class LLCrashLogger : public LLApp
 	LLSD constructPostData();
 	virtual void updateApplication(const std::string& message = LLStringUtil::null);
 	virtual bool init();
-	virtual bool mainLoop() = 0;
+	virtual bool frame() = 0;
 	virtual bool cleanup() = 0;
 	void commonCleanup();
 	void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h
index faa5d3abb71e2049e33d03d76fde6145fa130673..6daf1ac55b98c6f080c49deb483d34d7ff8e934d 100644
--- a/indra/llwindow/llappdelegate-objc.h
+++ b/indra/llwindow/llappdelegate-objc.h
@@ -41,7 +41,7 @@
 
 @property (retain) NSString *currentInputLanguage;
 
-- (void) mainLoop;
+- (void) oneFrame;
 - (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent;
 - (void) languageUpdated;
 - (bool) romanScript;
diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
index c22f3382fb3fca5dc08a3c55f527ecc4752adf31..b06cd2c184ec61488d54e024c931cac20ace1fca 100644
--- a/indra/llwindow/llwindowmacosx-objc.h
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -69,7 +69,7 @@ typedef const NativeKeyEventData * NSKeyEventRef;
 // These are defined in llappviewermacosx.cpp.
 bool initViewer();
 void handleQuit();
-bool runMainLoop();
+bool pumpMainLoop();
 void initMainLoop();
 void cleanupViewer();
 void handleUrl(const char* url);
diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp
index 3149fad6e8732a59312f868b21ec189607960262..ec3616e26a93a173c052407a62c0e46cae31b3ea 100644
--- a/indra/mac_crash_logger/llcrashloggermac.cpp
+++ b/indra/mac_crash_logger/llcrashloggermac.cpp
@@ -64,7 +64,7 @@ void LLCrashLoggerMac::gatherPlatformSpecificFiles()
 {
 }
 
-bool LLCrashLoggerMac::mainLoop()
+bool LLCrashLoggerMac::frame()
 {
 
     if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h
index 6d8f63ecac0570c8e8000a111568c384c191bfc3..05ef8c9f53976161c8ac392a9ea8f095a64ac75d 100644
--- a/indra/mac_crash_logger/llcrashloggermac.h
+++ b/indra/mac_crash_logger/llcrashloggermac.h
@@ -37,7 +37,7 @@ class LLCrashLoggerMac : public LLCrashLogger
 	LLCrashLoggerMac(void);
 	~LLCrashLoggerMac(void);
 	virtual bool init();
-	virtual bool mainLoop();
+	virtual bool frame();
 	virtual bool cleanup();
 	virtual void gatherPlatformSpecificFiles();
 };
diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp
index 95d4e65207d8930d114cf75eecb1ac70426b18f6..54e41a1954d927894dc394d2112841e2550903d0 100644
--- a/indra/mac_crash_logger/mac_crash_logger.cpp
+++ b/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -49,7 +49,7 @@ int main(int argc, char **argv)
     {
 //        return NSApplicationMain(argc, (const char **)argv);
     }
-	app.mainLoop();
+	app.frame();
 	app.cleanup();
 
 	LL_INFOS() << "Crash reporter finished normally." << LL_ENDL;
diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm
index 549df80fa1a501a268d77f1f948d58d26022ab50..be8877328d767e974fd5f797f25934d2366b60ca 100644
--- a/indra/newview/llappdelegate-objc.mm
+++ b/indra/newview/llappdelegate-objc.mm
@@ -48,16 +48,19 @@
 - (void) applicationDidFinishLaunching:(NSNotification *)notification
 {
 	frameTimer = nil;
-	
+
 	[self languageUpdated];
-	
+
 	if (initViewer())
 	{
-		frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(mainLoop) userInfo:nil repeats:YES];
+		// Set up recurring calls to oneFrame (repeating timer with timeout 0)
+		// until applicationShouldTerminate.
+		frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self
+							  selector:@selector(oneFrame) userInfo:nil repeats:YES];
 	} else {
 		handleQuit();
 	}
-	
+
 	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];
 
  //   [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
@@ -96,22 +99,29 @@
 
 - (NSApplicationDelegateReply) applicationShouldTerminate:(NSApplication *)sender
 {
-	if (!runMainLoop())
+	// run one frame to assess state
+	if (!pumpMainLoop())
 	{
+		// pumpMainLoop() returns true when done, false if it wants to be
+		// called again. Since it returned false, do not yet cancel
+		// frameTimer.
 		handleQuit();
 		return NSTerminateCancel;
 	} else {
+		// pumpMainLoop() returned true: it's done. Okay, done with frameTimer.
 		[frameTimer release];
 		cleanupViewer();
 		return NSTerminateNow;
 	}
 }
 
-- (void) mainLoop
+- (void) oneFrame
 {
-	bool appExiting = runMainLoop();
+	bool appExiting = pumpMainLoop();
 	if (appExiting)
 	{
+		// Once pumpMainLoop() reports that we're done, cancel frameTimer:
+		// stop the repetitive calls.
 		[frameTimer release];
 		[[NSApplication sharedApplication] terminate:self];
 	}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index b6d02ea2f8111335803441c59497c605f88dc83d..604e45f31489ae59a0330258ac0bd5da557ab342 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -769,9 +769,6 @@ bool LLAppViewer::init()
 	//
 	// Start of the application
 	//
-#ifdef LL_DARWIN
-	mMainLoopInitialized = false;
-#endif
 
 	// initialize LLWearableType translation bridge.
 	// Memory will be cleaned up in ::cleanupClass()
@@ -1220,6 +1217,23 @@ bool LLAppViewer::init()
         boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1),
         boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS));
 
+	/*----------------------------------------------------------------------*/
+	// nat 2016-06-29 moved the following here from the former mainLoop().
+	mMainloopTimeout = new LLWatchdogTimeout();
+
+	// Create IO Pump to use for HTTP Requests.
+	gServicePump = new LLPumpIO(gAPRPoolp);
+
+	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
+
+	LLVoiceChannel::initClass();
+	LLVoiceClient::getInstance()->init(gServicePump);
+	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);
+
+	joystick = LLViewerJoystick::getInstance();
+	joystick->setNeedsReset(true);
+	/*----------------------------------------------------------------------*/
+
 	return true;
 }
 
@@ -1294,297 +1308,255 @@ static LLTrace::BlockTimerStatHandle FTM_AGENT_UPDATE("Update");
 // externally visible timers
 LLTrace::BlockTimerStatHandle FTM_FRAME("Frame");
 
-bool LLAppViewer::mainLoop()
+bool LLAppViewer::frame()
 {
-#ifdef LL_DARWIN
-	if (!mMainLoopInitialized)
-#endif
-	{
-        LL_INFOS() << "Entering main_loop" << LL_ENDL;
-		mMainloopTimeout = new LLWatchdogTimeout();
-		
-		//-------------------------------------------
-		// Run main loop until time to quit
-		//-------------------------------------------
-		
-		// Create IO Pump to use for HTTP Requests.
-		gServicePump = new LLPumpIO(gAPRPoolp);
-		
-		// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
-		
-		LLVoiceChannel::initClass();
-		LLVoiceClient::getInstance()->init(gServicePump);
-		LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);
-		
-		joystick = LLViewerJoystick::getInstance();
-		joystick->setNeedsReset(true);
-		
-#ifdef LL_DARWIN
-		// Ensure that this section of code never gets called again on OS X.
-		mMainLoopInitialized = true;
-#endif
-	}
-	// As we do not (yet) send data on the mainloop LLEventPump that varies
-	// with each frame, no need to instantiate a new LLSD event object each
-	// time. Obviously, if that changes, just instantiate the LLSD at the
-	// point of posting.
-	
 	LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
-	
-    LLSD newFrame;
-	
+	LLSD newFrame;
+
 	LLTimer frameTimer,idleTimer;
 	LLTimer debugTime;
-	
+
 	//LLPrivateMemoryPoolTester::getInstance()->run(false) ;
 	//LLPrivateMemoryPoolTester::getInstance()->run(true) ;
 	//LLPrivateMemoryPoolTester::destroy() ;
 
-	// Handle messages
-#ifdef LL_DARWIN
-	if (!LLApp::isExiting())
-#else
-	while (!LLApp::isExiting())
-#endif
+	LL_RECORD_BLOCK_TIME(FTM_FRAME);
+	LLTrace::BlockTimer::processTimes();
+	LLTrace::get_frame_recording().nextPeriod();
+	LLTrace::BlockTimer::logStats();
+
+	LLTrace::get_thread_recorder()->pullFromChildren();
+
+	//clear call stack records
+	LL_CLEAR_CALLSTACKS();
+
+	//check memory availability information
+	checkMemory() ;
+
+	try
 	{
-		LL_RECORD_BLOCK_TIME(FTM_FRAME);
-		LLTrace::BlockTimer::processTimes();
-		LLTrace::get_frame_recording().nextPeriod();
-		LLTrace::BlockTimer::logStats();
+		pingMainloopTimeout("Main:MiscNativeWindowEvents");
+
+		if (gViewerWindow)
+		{
+			LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
+			gViewerWindow->getWindow()->processMiscNativeEvents();
+		}
 
-		LLTrace::get_thread_recorder()->pullFromChildren();
+		pingMainloopTimeout("Main:GatherInput");
 
-		//clear call stack records
-		LL_CLEAR_CALLSTACKS();
+		if (gViewerWindow)
+		{
+			LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
+			if (!restoreErrorTrap())
+			{
+				LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL;
+			}
 
-		//check memory availability information
-		checkMemory() ;
+			gViewerWindow->getWindow()->gatherInput();
+		}
+
+#if 1 && !LL_RELEASE_FOR_DOWNLOAD
+		// once per second debug info
+		if (debugTime.getElapsedTimeF32() > 1.f)
+		{
+			debugTime.reset();
+		}
 		
-		try
+#endif
+		//memory leaking simulation
+		LLFloaterMemLeak* mem_leak_instance =
+			LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
+		if(mem_leak_instance)
 		{
-			pingMainloopTimeout("Main:MiscNativeWindowEvents");
+			mem_leak_instance->idle() ;				
+		}							
+
+		// canonical per-frame event
+		mainloop.post(newFrame);
 
-			if (gViewerWindow)
+		if (!LLApp::isExiting())
+		{
+			pingMainloopTimeout("Main:JoystickKeyboard");
+
+			// Scan keyboard for movement keys.  Command keys and typing
+			// are handled by windows callbacks.  Don't do this until we're
+			// done initializing.  JC
+			if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible())
+				&& gViewerWindow->getActive()
+				&& !gViewerWindow->getWindow()->getMinimized()
+				&& LLStartUp::getStartupState() == STATE_STARTED
+				&& (gHeadlessClient || !gViewerWindow->getShowProgress())
+				&& !gFocusMgr.focusLocked())
 			{
-				LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
-				gViewerWindow->getWindow()->processMiscNativeEvents();
+				joystick->scanJoystick();
+				gKeyboard->scanKeyboard();
 			}
-		
-			pingMainloopTimeout("Main:GatherInput");
-			
-			if (gViewerWindow)
+
+			// Update state based on messages, user input, object idle.
 			{
-				LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
-				if (!restoreErrorTrap())
-				{
-					LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL;
-				}
+				pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds!
+				
+				LL_RECORD_BLOCK_TIME(FTM_IDLE);
+				idle();
 
-				gViewerWindow->getWindow()->gatherInput();
+				resumeMainloopTimeout();
 			}
 
-#if 1 && !LL_RELEASE_FOR_DOWNLOAD
-			// once per second debug info
-			if (debugTime.getElapsedTimeF32() > 1.f)
+			if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
 			{
-				debugTime.reset();
+				pauseMainloopTimeout();
+				saveFinalSnapshot();
+				disconnectViewer();
+				resumeMainloopTimeout();
 			}
-			
-#endif
-			//memory leaking simulation
-			LLFloaterMemLeak* mem_leak_instance =
-				LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
-			if(mem_leak_instance)
-			{
-				mem_leak_instance->idle() ;				
-			}							
-
-            // canonical per-frame event
-            mainloop.post(newFrame);
 
-			if (!LLApp::isExiting())
+			// Render scene.
+			// *TODO: Should we run display() even during gHeadlessClient?  DK 2011-02-18
+			if (!LLApp::isExiting() && !gHeadlessClient)
 			{
-				pingMainloopTimeout("Main:JoystickKeyboard");
-				
-				// Scan keyboard for movement keys.  Command keys and typing
-				// are handled by windows callbacks.  Don't do this until we're
-				// done initializing.  JC
-				if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible())
-					&& gViewerWindow->getActive()
-					&& !gViewerWindow->getWindow()->getMinimized()
-					&& LLStartUp::getStartupState() == STATE_STARTED
-					&& (gHeadlessClient || !gViewerWindow->getShowProgress())
-					&& !gFocusMgr.focusLocked())
-				{
-					joystick->scanJoystick();
-					gKeyboard->scanKeyboard();
-				}
+				pingMainloopTimeout("Main:Display");
+				gGLActive = TRUE;
+				display();
+				pingMainloopTimeout("Main:Snapshot");
+				LLFloaterSnapshot::update(); // take snapshots
+				gGLActive = FALSE;
+			}
+		}
 
-				// Update state based on messages, user input, object idle.
-				{
-					pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds!
-					
-					LL_RECORD_BLOCK_TIME(FTM_IDLE);
-					idle();
+		pingMainloopTimeout("Main:Sleep");
 
-					resumeMainloopTimeout();
-				}
- 
-				if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
-				{
-					pauseMainloopTimeout();
-					saveFinalSnapshot();
-					disconnectViewer();
-					resumeMainloopTimeout();
-				}
+		pauseMainloopTimeout();
 
-				// Render scene.
-				// *TODO: Should we run display() even during gHeadlessClient?  DK 2011-02-18
-				if (!LLApp::isExiting() && !gHeadlessClient)
+		// Sleep and run background threads
+		{
+			LL_RECORD_BLOCK_TIME(FTM_SLEEP);
+			
+			// yield some time to the os based on command line option
+			if(mYieldTime >= 0)
+			{
+				LL_RECORD_BLOCK_TIME(FTM_YIELD);
+				ms_sleep(mYieldTime);
+			}
+
+			// yield cooperatively when not running as foreground window
+			if (   (gViewerWindow && !gViewerWindow->getWindow()->getVisible())
+					|| !gFocusMgr.getAppHasFocus())
+			{
+				// Sleep if we're not rendering, or the window is minimized.
+				S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000);
+				// don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
+				// of equal priority on Windows
+				if (milliseconds_to_sleep > 0)
 				{
-					pingMainloopTimeout("Main:Display");
-					gGLActive = TRUE;
-					display();
-					pingMainloopTimeout("Main:Snapshot");
-					LLFloaterSnapshot::update(); // take snapshots
-					gGLActive = FALSE;
+					ms_sleep(milliseconds_to_sleep);
+					// also pause worker threads during this wait period
+					LLAppViewer::getTextureCache()->pause();
+					LLAppViewer::getImageDecodeThread()->pause();
 				}
 			}
-
-			pingMainloopTimeout("Main:Sleep");
 			
-			pauseMainloopTimeout();
+			if (mRandomizeFramerate)
+			{
+				ms_sleep(rand() % 200);
+			}
 
-			// Sleep and run background threads
+			if (mPeriodicSlowFrame
+				&& (gFrameCount % 10 == 0))
 			{
-				LL_RECORD_BLOCK_TIME(FTM_SLEEP);
-				
-				// yield some time to the os based on command line option
-				if(mYieldTime >= 0)
-				{
-					LL_RECORD_BLOCK_TIME(FTM_YIELD);
-					ms_sleep(mYieldTime);
-				}
+				LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL;
+				ms_sleep(500);
+			}
+
+			const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second
+			idleTimer.reset();
+			S32 total_work_pending = 0;
+			S32 total_io_pending = 0;	
+			while(1)
+			{
+				S32 work_pending = 0;
+				S32 io_pending = 0;
+				F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);
+
+				work_pending += updateTextureThreads(max_time);
 
-				// yield cooperatively when not running as foreground window
-				if (   (gViewerWindow && !gViewerWindow->getWindow()->getVisible())
-						|| !gFocusMgr.getAppHasFocus())
 				{
-					// Sleep if we're not rendering, or the window is minimized.
-					S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000);
-					// don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
-					// of equal priority on Windows
-					if (milliseconds_to_sleep > 0)
-					{
-						ms_sleep(milliseconds_to_sleep);
-						// also pause worker threads during this wait period
-						LLAppViewer::getTextureCache()->pause();
-						LLAppViewer::getImageDecodeThread()->pause();
-					}
+					LL_RECORD_BLOCK_TIME(FTM_VFS);
+ 					io_pending += LLVFSThread::updateClass(1);
 				}
-				
-				if (mRandomizeFramerate)
 				{
-					ms_sleep(rand() % 200);
+					LL_RECORD_BLOCK_TIME(FTM_LFS);
+ 					io_pending += LLLFSThread::updateClass(1);
 				}
 
-				if (mPeriodicSlowFrame
-					&& (gFrameCount % 10 == 0))
+				if (io_pending > 1000)
 				{
-					LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL;
-					ms_sleep(500);
+					ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
 				}
 
-				const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second
-				idleTimer.reset();
-				S32 total_work_pending = 0;
-				S32 total_io_pending = 0;	
-				while(1)
-				{
-					S32 work_pending = 0;
-					S32 io_pending = 0;
-					F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);
-
-					work_pending += updateTextureThreads(max_time);
-
-					{
-						LL_RECORD_BLOCK_TIME(FTM_VFS);
-	 					io_pending += LLVFSThread::updateClass(1);
-					}
-					{
-						LL_RECORD_BLOCK_TIME(FTM_LFS);
-	 					io_pending += LLLFSThread::updateClass(1);
-					}
-
-					if (io_pending > 1000)
-					{
-						ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
-					}
-
-					total_work_pending += work_pending ;
-					total_io_pending += io_pending ;
-					
-					if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time)
-					{
-						break;
-					}
-				}
-				gMeshRepo.update() ;
+				total_work_pending += work_pending ;
+				total_io_pending += io_pending ;
 				
-				if(!total_work_pending) //pause texture fetching threads if nothing to process.
+				if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time)
 				{
-					LLAppViewer::getTextureCache()->pause();
-					LLAppViewer::getImageDecodeThread()->pause();
-					LLAppViewer::getTextureFetch()->pause(); 
-				}
-				if(!total_io_pending) //pause file threads if nothing to process.
-				{
-					LLVFSThread::sLocal->pause(); 
-					LLLFSThread::sLocal->pause(); 
-				}									
-
-				//texture fetching debugger
-				if(LLTextureFetchDebugger::isEnabled())
-				{
-					LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
-						LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
-					if(tex_fetch_debugger_instance)
-					{
-						tex_fetch_debugger_instance->idle() ;				
-					}
+					break;
 				}
+			}
+			gMeshRepo.update() ;
+			
+			if(!total_work_pending) //pause texture fetching threads if nothing to process.
+			{
+				LLAppViewer::getTextureCache()->pause();
+				LLAppViewer::getImageDecodeThread()->pause();
+				LLAppViewer::getTextureFetch()->pause(); 
+			}
+			if(!total_io_pending) //pause file threads if nothing to process.
+			{
+				LLVFSThread::sLocal->pause(); 
+				LLLFSThread::sLocal->pause(); 
+			}									
 
-				if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
-					(frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
+			//texture fetching debugger
+			if(LLTextureFetchDebugger::isEnabled())
+			{
+				LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
+					LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
+				if(tex_fetch_debugger_instance)
 				{
-					gFrameStalls++;
+					tex_fetch_debugger_instance->idle() ;				
 				}
-				frameTimer.reset();
-
-				resumeMainloopTimeout();
-	
-				pingMainloopTimeout("Main:End");
-			}	
-		}
-		catch(std::bad_alloc)
-		{			
-			LLMemory::logMemoryInfo(TRUE) ;
+			}
 
-			//stop memory leaking simulation
-			LLFloaterMemLeak* mem_leak_instance =
-				LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
-			if(mem_leak_instance)
+			if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
+				(frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
 			{
-				mem_leak_instance->stop() ;				
-				LL_WARNS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ;
+				gFrameStalls++;
 			}
-			else
-			{
-				//output possible call stacks to log file.
-				LLError::LLCallStacks::print() ;
+			frameTimer.reset();
 
-				LL_ERRS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ;
-			}
+			resumeMainloopTimeout();
+
+			pingMainloopTimeout("Main:End");
+		}	
+	}
+	catch(std::bad_alloc)
+	{			
+		LLMemory::logMemoryInfo(TRUE) ;
+
+		//stop memory leaking simulation
+		LLFloaterMemLeak* mem_leak_instance =
+			LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
+		if(mem_leak_instance)
+		{
+			mem_leak_instance->stop() ;				
+			LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ;
+		}
+		else
+		{
+			//output possible call stacks to log file.
+			LLError::LLCallStacks::print() ;
+
+			LL_ERRS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ;
 		}
 	}
 
@@ -1618,7 +1590,7 @@ bool LLAppViewer::mainLoop()
 		LL_INFOS() << "Exiting main_loop" << LL_ENDL;
 	}
 
-	return LLApp::isExiting();
+	return ! LLApp::isRunning();
 }
 
 S32 LLAppViewer::updateTextureThreads(F32 max_time)
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index b5e674bd7b3a8eb672226bc850e6a68138f1bd02..f7c1bb58b481178a071485d84a3f482b4f76512a 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -79,7 +79,7 @@ class LLAppViewer : public LLApp
 	//
 	virtual bool init();			// Override to do application initialization
 	virtual bool cleanup();			// Override to do application cleanup
-	virtual bool mainLoop(); // Override for the application main loop.  Needs to at least gracefully notice the QUITTING state and exit.
+	virtual bool frame(); // Override for application body logic
 
 	// Application control
 	void flushVFSIO(); // waits for vfs transfers to complete
@@ -283,7 +283,6 @@ class LLAppViewer : public LLApp
 	std::string mSerialNumber;
 	bool mPurgeCache;
     bool mPurgeOnExit;
-	bool mMainLoopInitialized;
 	LLViewerJoystick* joystick;
 
 	bool mSavedFinalSnapshot;
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index f5742b29cf09c414ceed55fa749e385810f489ce..6f32aab851ebb2c451490f037ce80637faf0185b 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -95,10 +95,8 @@ int main( int argc, char **argv )
 	}
 
 		// Run the application main loop
-	if(!LLApp::isQuitting()) 
-	{
-		viewer_app_ptr->mainLoop();
-	}
+	while (! viewer_app_ptr->frame()) 
+	{}
 
 	if (!LLApp::isError())
 	{
diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp
index ca219fda59bc971fbcdba41e6731e868057e8fac..4fe1e31668b127316ebafb33292f2c4e455cdd9c 100644
--- a/indra/newview/llappviewermacosx.cpp
+++ b/indra/newview/llappviewermacosx.cpp
@@ -117,12 +117,17 @@ void handleQuit()
 	LLAppViewer::instance()->userQuit();
 }
 
-bool runMainLoop()
+// This function is called pumpMainLoop() rather than runMainLoop() because
+// it passes control to the viewer's main-loop logic for a single frame. Like
+// LLAppViewer::frame(), it returns 'true' when it's done. Until then, it
+// expects to be called again by the timer in LLAppDelegate
+// (llappdelegate-objc.mm).
+bool pumpMainLoop()
 {
 	bool ret = LLApp::isQuitting();
 	if (!ret && gViewerAppPtr != NULL)
 	{
-		ret = gViewerAppPtr->mainLoop();
+		ret = gViewerAppPtr->frame();
 	} else {
 		ret = true;
 	}
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 4786f83bfd1825a1c9a1d23c32bbbd75f3994d75..a7f248ab5a95618f766cc016dbab0115a7714a03 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -317,10 +317,8 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
 	}
 	
 	// Run the application main loop
-	if(!LLApp::isQuitting()) 
-	{
-		viewer_app_ptr->mainLoop();
-	}
+	while (! viewer_app_ptr->frame()) 
+	{}
 
 	if (!LLApp::isError())
 	{
diff --git a/indra/test/llapp_tut.cpp b/indra/test/llapp_tut.cpp
index aa5c0672e691eacfe7c6c096096d43a08ad966ff..98e714a497a961bb53ff1691f65d9e2280340e88 100644
--- a/indra/test/llapp_tut.cpp
+++ b/indra/test/llapp_tut.cpp
@@ -41,7 +41,7 @@ namespace tut
 		public:
 			virtual bool init() { return true; }
 			virtual bool cleanup() { return true; }
-			virtual bool mainLoop() { return true; }
+			virtual bool frame() { return true; }
 		};
 		LLTestApp* mApp;
 		application()
diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp
index 23c29e6b18807be639d60bf2fe3c445da124addf..167acf6ac7b623745613429b08022ea8db981797 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.cpp
+++ b/indra/win_crash_logger/llcrashloggerwindows.cpp
@@ -454,7 +454,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
 	//mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo();  //Not initialized.
 }
 
-bool LLCrashLoggerWindows::mainLoop()
+bool LLCrashLoggerWindows::frame()
 {	
 	LL_INFOS() << "CrashSubmitBehavior is " << mCrashBehavior << LL_ENDL;
 
@@ -503,14 +503,14 @@ bool LLCrashLoggerWindows::mainLoop()
 			TranslateMessage(&msg);
 			DispatchMessage(&msg);
 		}
-		return msg.wParam;
+		return true; // msg.wParam;
 	}
 	else
 	{
 		LL_WARNS() << "Unknown crash behavior " << mCrashBehavior << LL_ENDL;
-		return 1;
+		return true; // 1;
 	}
-	return 0;
+	return true; // 0;
 }
 
 void LLCrashLoggerWindows::updateApplication(const std::string& message)
diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h
index 1812e2737ec1d2f01a6fb46641ba8360a2bd8598..f89b8708dc9386567547b875c518a0ef07522e36 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.h
+++ b/indra/win_crash_logger/llcrashloggerwindows.h
@@ -46,7 +46,7 @@ class LLCrashLoggerWindows : public LLCrashLogger
 	static LLCrashLoggerWindows* sInstance; 
 
 	virtual bool init();
-	virtual bool mainLoop();
+	virtual bool frame();
 	virtual void updateApplication(const std::string& message = LLStringUtil::null);
 	virtual bool cleanup();
 	virtual void gatherPlatformSpecificFiles();
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
index 366edd894b970766a0e621e5067b70c954b9f7d2..7466dbb76638a131d86d4f353a019ccd43d06eff 100644
--- a/indra/win_crash_logger/win_crash_logger.cpp
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -52,7 +52,7 @@ int APIENTRY WinMain(HINSTANCE hInstance,
 	}
 
 	app.processingLoop();
-	app.mainLoop();
+	app.frame();
 	app.cleanup();
 	LL_INFOS() << "Crash reporter finished normally." << LL_ENDL;
 	return 0;