Repository: nvaccess/nvda

Issue:
nvda settings randomly resetting
**Reported by camlorn on 2013-04-17 13:24**
This has happened twice in 3 days.  Nvda will randomly forget _all_ of my settings, including that nvda has been run on my computer before, re displaying the start up dialogs and resetting everything.  Obviously, this is...problematic.
What do I need to do, besides (if I can) comment with how to reliably reproduce it?
The only changes in my setup since this has started happening are the enabling of the us-dvorak keyboard layout.
I'm on Version: master-9098,fb41602, and continually update as soon as new development snapshots come out.  I first saw this on Monday the 15th.
The easiest fix would probably be making a backup of all settings at startup, of some sort.  I'm not really sure why this is happening, but both times were while the system was being very sluggish--maybe some sort of interrupted file writing?
**Blocked by #667**



Discussion:
Comment 1 by jteh on 2013-04-17 19:52
Do you have any NVDA add-ons installed?

Comment 2 by briang1 on 2013-04-18 07:49
I assume that the older version does not do this even if reinstalled  does it?

The last time I saw this effect it turned out to be the computer or in fact a program running on it that seemed to interfere with some files being written to. 

However something like that should affect older versions of nvda equally as often as the newer ones I'd have thought.
Have you installed anything recently?

Comment 3 by camlorn on 2013-04-19 20:54
The most recently installed program is Visual Studio 2012.  The second most recently installed program is Skype.  Both times, this happened shortly after a processor spike during which large parts of the system became unresponsive.  Since it hasn't yet happened a third time, I'm going to go with it having something to do with not enough cpu for settings saving.
If I can reliably duplicate it, I'll comment; that said, it is probably worthwhile to at least do something to make the settings more resistant to this.
The only installed add-on is OCR.

Comment 4 by briang1 on 2013-04-20 06:20
Sounds to me that its more likely to be  a Windows process and I have noticed on XP since a recent Windows update, with nvda running something stops Explorer.exe from closing every time at shutdown. As you say, its not doing it to order, and I did wonder if it might be the malicious software removal tool or anti virus that is spiking the processor and then some software does  not  do as expected. I don't see how one can make a program resistant to this myself if this is indeed the case. Last time it happened they updated the  malware scanning routines so it could not max out a processor. Just an idea.
Good ol Microsoft!

Comment 5 by camlorn on 2013-04-21 17:27
I am running no Antivirus.  Windows firewall is running, but that's it.  I'm also windows 7.

Comment 6 by jteh on 2013-04-23 05:01
The only way this could happen that i can think of is that NVDA was terminated while it was writing the configuration file. We could work around this in one of two ways:
1. Back up the old configuration before writing; or
2. Write the configuration to a temporary file and then move that file over the top of the configuration file.
The second option is probably easier, as it avoids the need to detect and restore a broken configuration.

Comment 7 by PZajda on 2013-09-13 17:55
Hi,

I have done something which should fix this issue, I hop I understood the way to fix it correctly:
- First, change the filename attribute of config.conf to nvda.ini.tmp;
- Write the configuration to this file;
- put the filename attribute to its original value;
- Delete the old nvda.ini then replace it by the temporary file.

If the temporary config is not written correctly, the configuration shouldn't be change so not damaged too.
The worst case is if NVDA is stopped before the copy of the new file, it is still possible to rename nvda.ini.tmp to nvda.ini and get the configuration back. Maybe should it be done automatically when NVDA load the configuration?

It is available on git at:
https://bitbucket.org/pzajda/nvda.git
Branch: t3165

Comment 8 by jteh on 2013-09-13 21:06
Thanks again for your work. Unfortunately, I meant to block this on #667 but forgot, as #667 completely changes the config code and it also writes profiles, so it may as well be done after that is merged. The idea is the same, though. We probably want to factor it out into a helper function so that it can also be used by inputCore when writing gestures.ini (#1532).

I sincerely apologise for having wasted some of your time on this.

I've seen this issue happen a few times recently on a fairly new laptop of a friend running Windows 10. It seems quite easy to implement this and I would be glad to do so.

Please produce steps to reproduce the

Sent from my heavily encrypted iPhone.
Please disregard errors as this is a smaller device.

> On Apr 26, 2016, at 19:19, Bram Duvigneau notifications@github.com wrote:
> 
> I've seen this issue happen a few times recently on a fairly new laptop of a friend running Windows 10. It seems quite easy to implement this and I would be glad to do so.
> 
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly or view it on GitHub please give steps on reproducing this bug. Thanks

@derekriemer So far, I was unable to reproduce this reliably. It just sometimes happened after a shutdown/reboot of the computer.

Are you sure that the computer is not trying to recover from an error, it 
has at least once happened to meas it has restored to what it thought was a 
last known good start up.
 Brian

bglists@blueyonder.co.uk
Sent via blueyonder.
Please address personal email to:-
briang1@blueyonder.co.uk, putting 'Brian Gaff'
in the display name field.
----- Original Message ----- 
From: "Bram Duvigneau" notifications@github.com
To: "nvaccess/nvda" nvda@noreply.github.com
Sent: Friday, April 29, 2016 9:59 PM
Subject: Re: [nvaccess/nvda] nvda settings randomly resetting (#3165)

> @derekriemer So far, I was unable to reproduce this reliably. It just 
> sometimes happened after a shutdown/reboot of the computer.
> 
> ---
> 
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly or view it on GitHub:
> https://github.com/nvaccess/nvda/issues/3165#issuecomment-215880233 


Resolving patch:
diff --git a/source/config/__init__.py b/source/config/__init__.py
--- a/source/config/__init__.py
+++ b/source/config/__init__.py
@@ -25,6 +25,7 @@
 import shlobj
 import baseObject
 import easeOfAccess
+from fileUtils import FaultTolerantFile
 import winKernel
 import profileUpgrader
 from .configSpec import confspec
@@ -498,10 +499,12 @@ def save(self):
 			# Never save the config if running securely.
 			return
 		try:
-			self.profiles[0].write()
+			with FaultTolerantFile(self.profiles[0].filename) as f:
+				self.profiles[0].write(f)
 			log.info("Base configuration saved")
 			for name in self._dirtyProfiles:
-				self._profileCache[name].write()
+				with FaultTolerantFile(self._profileCache[name].filename) as f:
+					self._profileCache[name].write(f)
 				log.info("Saved configuration profile %s" % name)
 			self._dirtyProfiles.clear()
 		except Exception as e:
diff --git a/source/fileUtils.py b/source/fileUtils.py
new file mode 100644
--- /dev/null
+++ b/source/fileUtils.py
@@ -0,0 +1,38 @@
+#fileUtils.py
+#A part of NonVisual Desktop Access (NVDA)
+#Copyright (C) 2017 NV Access Limited, Bram Duvigneau
+#This file is covered by the GNU General Public License.
+#See the file COPYING for more details.
+import os
+import ctypes
+from contextlib import contextmanager
+from tempfile import NamedTemporaryFile
+from logHandler import log
+
+#: Constant; flag for MoveFileEx(). If a file with the destination filename already exists, it is overwritten.
+MOVEFILE_REPLACE_EXISTING = 1
+
+@contextmanager
+def FaultTolerantFile(name):
+	'''Used to write out files in a more fault tolerant way. A temporary file is used, and replaces the 
+	file `name' when the context manager scope ends and the the context manager __exit__ is called. This
+	means writing out the complete file can be performed with less concern of corrupting the original file
+	if the process is interrupted by windows shutting down.
+
+	Usage:
+		with FaultTolerantFile("myFile.txt") as f:
+			f.write("This is a test")
+
+	This creates a temporary file, and the writes actually happen on this temp file. At the end of the 
+	`with` block, when `f` goes out of context the temporary file is closed and, this temporary file replaces "myFile.txt"
+	'''
+	dirpath, filename = os.path.split(name)
+	with NamedTemporaryFile(dir=dirpath, prefix=filename, suffix='.tmp', delete=False) as f:
+		log.debug(f.name)
+		yield f
+		f.flush()
+		os.fsync(f)
+		f.close()
+		moveFileResult = ctypes.windll.kernel32.MoveFileExW(f.name, name, MOVEFILE_REPLACE_EXISTING)
+		if moveFileResult == 0:
+			raise ctypes.WinError()
diff --git a/source/inputCore.py b/source/inputCore.py
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -23,6 +23,7 @@
 import speech
 import characterProcessing
 import config
+from fileUtils import FaultTolerantFile
 import watchdog
 from logHandler import log
 import globalVars
@@ -360,7 +361,8 @@ def save(self):
 					else:
 						outSect[script] = [outVal, gesture]
 
-		out.write()
+		with FaultTolerantFile(out.filename) as f:
+			out.write(f)
 
 class InputManager(baseObject.AutoPropertyObject):
 	"""Manages functionality related to input from the user.


Test patch: