Fix potential race condition on print start
When the sending of the first line of a file to print is still taking place while an "ok" from the firmware comes in, it's possible that two threads will try to access the file handle in parallel. That can lead to trouble within Python's codecs module. Synchronizing all access to the handle should do the trick.
This commit is contained in:
parent
6bd788a83f
commit
a6c4f8ba3b
1 changed files with 53 additions and 48 deletions
|
|
@ -2580,6 +2580,7 @@ class PrintingGcodeFileInformation(PrintingFileInformation):
|
|||
PrintingFileInformation.__init__(self, filename)
|
||||
|
||||
self._handle = None
|
||||
self._handle_mutex = threading.RLock()
|
||||
|
||||
self._offsets_callback = offsets_callback
|
||||
self._current_tool_callback = current_tool_callback
|
||||
|
|
@ -2591,76 +2592,80 @@ class PrintingGcodeFileInformation(PrintingFileInformation):
|
|||
self._read_lines = 0
|
||||
|
||||
def seek(self, offset):
|
||||
if self._handle is None:
|
||||
return
|
||||
with self._handle_mutex:
|
||||
if self._handle is None:
|
||||
return
|
||||
|
||||
self._handle.seek(offset)
|
||||
self._pos = self._handle.tell()
|
||||
self._read_lines = 0
|
||||
self._handle.seek(offset)
|
||||
self._pos = self._handle.tell()
|
||||
self._read_lines = 0
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Opens the file for reading and determines the file size.
|
||||
"""
|
||||
PrintingFileInformation.start(self)
|
||||
self._handle = bom_aware_open(self._filename, encoding="utf-8", errors="replace")
|
||||
self._pos = self._handle.tell()
|
||||
if self._handle.encoding.endswith("-sig"):
|
||||
# Apparently we found an utf-8 bom in the file.
|
||||
# We need to add its length to our pos because it will
|
||||
# be stripped transparently and we'll have no chance
|
||||
# catching that.
|
||||
import codecs
|
||||
self._pos += len(codecs.BOM_UTF8)
|
||||
self._read_lines = 0
|
||||
with self._handle_mutex:
|
||||
self._handle = bom_aware_open(self._filename, encoding="utf-8", errors="replace")
|
||||
self._pos = self._handle.tell()
|
||||
if self._handle.encoding.endswith("-sig"):
|
||||
# Apparently we found an utf-8 bom in the file.
|
||||
# We need to add its length to our pos because it will
|
||||
# be stripped transparently and we'll have no chance
|
||||
# catching that.
|
||||
import codecs
|
||||
self._pos += len(codecs.BOM_UTF8)
|
||||
self._read_lines = 0
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Closes the file if it's still open.
|
||||
"""
|
||||
PrintingFileInformation.close(self)
|
||||
if self._handle is not None:
|
||||
try:
|
||||
self._handle.close()
|
||||
except:
|
||||
pass
|
||||
self._handle = None
|
||||
with self._handle_mutex:
|
||||
if self._handle is not None:
|
||||
try:
|
||||
self._handle.close()
|
||||
except:
|
||||
pass
|
||||
self._handle = None
|
||||
|
||||
def getNext(self):
|
||||
"""
|
||||
Retrieves the next line for printing.
|
||||
"""
|
||||
if self._handle is None:
|
||||
raise ValueError("File %s is not open for reading" % self._filename)
|
||||
with self._handle_mutex:
|
||||
if self._handle is None:
|
||||
raise ValueError("File %s is not open for reading" % self._filename)
|
||||
|
||||
try:
|
||||
offsets = self._offsets_callback() if self._offsets_callback is not None else None
|
||||
current_tool = self._current_tool_callback() if self._current_tool_callback is not None else None
|
||||
try:
|
||||
offsets = self._offsets_callback() if self._offsets_callback is not None else None
|
||||
current_tool = self._current_tool_callback() if self._current_tool_callback is not None else None
|
||||
|
||||
processed = None
|
||||
while processed is None:
|
||||
if self._handle is None:
|
||||
# file got closed just now
|
||||
self._pos = self._size
|
||||
self._report_stats()
|
||||
return None
|
||||
processed = None
|
||||
while processed is None:
|
||||
if self._handle is None:
|
||||
# file got closed just now
|
||||
self._pos = self._size
|
||||
self._report_stats()
|
||||
return None
|
||||
|
||||
# we need to manually keep track of our pos here since
|
||||
# codecs' readline will make our handle's tell not
|
||||
# return the actual number of bytes read, but also the
|
||||
# already buffered bytes (for detecting the newlines)
|
||||
line = self._handle.readline()
|
||||
self._pos += len(line.encode("utf-8"))
|
||||
# we need to manually keep track of our pos here since
|
||||
# codecs' readline will make our handle's tell not
|
||||
# return the actual number of bytes read, but also the
|
||||
# already buffered bytes (for detecting the newlines)
|
||||
line = self._handle.readline()
|
||||
self._pos += len(line.encode("utf-8"))
|
||||
|
||||
if not line:
|
||||
self.close()
|
||||
processed = self._process(line, offsets, current_tool)
|
||||
self._read_lines += 1
|
||||
return processed
|
||||
except Exception as e:
|
||||
self.close()
|
||||
self._logger.exception("Exception while processing line")
|
||||
raise e
|
||||
if not line:
|
||||
self.close()
|
||||
processed = self._process(line, offsets, current_tool)
|
||||
self._read_lines += 1
|
||||
return processed
|
||||
except Exception as e:
|
||||
self.close()
|
||||
self._logger.exception("Exception while processing line")
|
||||
raise e
|
||||
|
||||
def _process(self, line, offsets, current_tool):
|
||||
return process_gcode_line(line, offsets=offsets, current_tool=current_tool)
|
||||
|
|
|
|||
Loading…
Reference in a new issue