Public Access
fix log dump race and unclosed HDF5 data pool file in DamarisGUI
This commit is contained in:
1 parent
efb01250c8
commit
da1dc4a088
1 file changed
+116
-114
+116
-114
@@ -613,8 +613,9 @@ class DamarisGUI:
|
||||
logger.debug("Data cleanup took %.3fs", time.time() - data_cleanup_start)
|
||||
|
||||
# set the text mark for hdf logging
|
||||
if self.log.textbuffer.get_mark( "lastdumped" ) is None:
|
||||
self.log.textbuffer.create_mark( "lastdumped", self.log.textbuffer.get_end_iter( ), left_gravity=True )
|
||||
with self.log.lock:
|
||||
if self.log.textbuffer.get_mark( "lastdumped" ) is None:
|
||||
self.log.textbuffer.create_mark( "lastdumped", self.log.textbuffer.get_end_iter( ), left_gravity=True )
|
||||
|
||||
loop_run=0
|
||||
#user shorter interval because this blocks user events from the GUI
|
||||
@@ -991,125 +992,124 @@ class DamarisGUI:
|
||||
|
||||
# now it's time to create the hdf file
|
||||
dump_file = None
|
||||
if not os.path.isfile( self.dump_filename ):
|
||||
if not init:
|
||||
logger.warning("Dump file \"%s\" vanished unexpectedly, creating new one", self.dump_filename)
|
||||
# have a look to the path and create necessary directories
|
||||
dir_stack = [ ]
|
||||
dir_trunk = os.path.dirname( os.path.abspath( self.dump_filename ) )
|
||||
while dir_trunk != "" and not os.path.isdir( dir_trunk ):
|
||||
dir_stack.append( os.path.basename( dir_trunk ) )
|
||||
dir_trunk = os.path.dirname( dir_trunk )
|
||||
try:
|
||||
while len( dir_stack ):
|
||||
dir_trunk = os.path.join( dir_trunk, dir_stack.pop( ) )
|
||||
if os.path.isdir( dir_trunk ):
|
||||
continue
|
||||
os.mkdir( dir_trunk )
|
||||
except OSError as e:
|
||||
logger.error("Could not create dump directory '%s', so hdf5 dumps disabled: %s", dir_trunk, e)
|
||||
self.dump_filename = ""
|
||||
self.dump_timeinterval = 0
|
||||
return True
|
||||
try:
|
||||
if not os.path.isfile( self.dump_filename ):
|
||||
if not init:
|
||||
logger.warning("Dump file \"%s\" vanished unexpectedly, creating new one", self.dump_filename)
|
||||
# have a look to the path and create necessary directories
|
||||
dir_stack = [ ]
|
||||
dir_trunk = os.path.dirname( os.path.abspath( self.dump_filename ) )
|
||||
while dir_trunk != "" and not os.path.isdir( dir_trunk ):
|
||||
dir_stack.append( os.path.basename( dir_trunk ) )
|
||||
dir_trunk = os.path.dirname( dir_trunk )
|
||||
try:
|
||||
while len( dir_stack ):
|
||||
dir_trunk = os.path.join( dir_trunk, dir_stack.pop( ) )
|
||||
if os.path.isdir( dir_trunk ):
|
||||
continue
|
||||
os.mkdir( dir_trunk )
|
||||
except OSError as e:
|
||||
logger.error("Could not create dump directory '%s', so hdf5 dumps disabled: %s", dir_trunk, e)
|
||||
self.dump_filename = ""
|
||||
self.dump_timeinterval = 0
|
||||
return True
|
||||
|
||||
# create new dump file
|
||||
dump_file = tables.open_file( self.dump_filename, mode="w", title="DAMARIS experiment data" )
|
||||
# write scripts and other useful information
|
||||
scriptgroup = dump_file.create_group( "/", "scripts", "Used Scripts" )
|
||||
exp_text, res_text = self.sw.get_scripts( )
|
||||
if self.si.exp_script:
|
||||
dump_file.create_array( scriptgroup, "experiment_script", exp_text.encode('utf-8') )
|
||||
if self.si.res_script:
|
||||
dump_file.create_array( scriptgroup, "result_script", res_text.encode('utf-8') )
|
||||
if self.si.backend_executable:
|
||||
dump_file.create_array( scriptgroup, "backend_executable", self.si.backend_executable.encode('utf-8') )
|
||||
if self.spool_dir:
|
||||
dump_file.create_array( scriptgroup, "spool_directory", self.spool_dir.encode('utf-8') )
|
||||
timeline_tablecols = numpy.recarray( 0, dtype=([ ("time", "S17"),
|
||||
("experiments", "int64"),
|
||||
("results", "int64") ]) )
|
||||
timeline_table = dump_file.create_table( "/", "timeline", timeline_tablecols,
|
||||
title="Timeline of Experiment" )
|
||||
if tables.__version__[ 0 ] == "1":
|
||||
logarray = dump_file.create_vlarray( where=dump_file.root,
|
||||
name="log",
|
||||
atom=tables.StringAtom( length=120 ),
|
||||
title="log messages",
|
||||
filters=tables.Filters( complevel=9, complib='zlib' ) )
|
||||
else:
|
||||
logarray = dump_file.create_earray( where=dump_file.root,
|
||||
name="log",
|
||||
atom=tables.StringAtom( itemsize=120 ),
|
||||
shape=(0,),
|
||||
title="log messages",
|
||||
filters=tables.Filters( complevel=9, complib='zlib' ) )
|
||||
# create new dump file
|
||||
dump_file = tables.open_file( self.dump_filename, mode="w", title="DAMARIS experiment data" )
|
||||
# write scripts and other useful information
|
||||
scriptgroup = dump_file.create_group( "/", "scripts", "Used Scripts" )
|
||||
exp_text, res_text = self.sw.get_scripts( )
|
||||
if self.si.exp_script:
|
||||
dump_file.create_array( scriptgroup, "experiment_script", exp_text.encode('utf-8') )
|
||||
if self.si.res_script:
|
||||
dump_file.create_array( scriptgroup, "result_script", res_text.encode('utf-8') )
|
||||
if self.si.backend_executable:
|
||||
dump_file.create_array( scriptgroup, "backend_executable", self.si.backend_executable.encode('utf-8') )
|
||||
if self.spool_dir:
|
||||
dump_file.create_array( scriptgroup, "spool_directory", self.spool_dir.encode('utf-8') )
|
||||
timeline_tablecols = numpy.recarray( 0, dtype=([ ("time", "S17"),
|
||||
("experiments", "int64"),
|
||||
("results", "int64") ]) )
|
||||
timeline_table = dump_file.create_table( "/", "timeline", timeline_tablecols,
|
||||
title="Timeline of Experiment" )
|
||||
if tables.__version__[ 0 ] == "1":
|
||||
logarray = dump_file.create_vlarray( where=dump_file.root,
|
||||
name="log",
|
||||
atom=tables.StringAtom( length=120 ),
|
||||
title="log messages",
|
||||
filters=tables.Filters( complevel=9, complib='zlib' ) )
|
||||
else:
|
||||
logarray = dump_file.create_earray( where=dump_file.root,
|
||||
name="log",
|
||||
atom=tables.StringAtom( itemsize=120 ),
|
||||
shape=(0,),
|
||||
title="log messages",
|
||||
filters=tables.Filters( complevel=9, complib='zlib' ) )
|
||||
|
||||
if dump_file is None and os.path.isfile( self.dump_filename ) and tables.is_pytables_file( self.dump_filename ):
|
||||
# prepare for update
|
||||
dump_file = tables.open_file( self.dump_filename, mode="r+" )
|
||||
if dump_file is None and os.path.isfile( self.dump_filename ) and tables.is_pytables_file( self.dump_filename ):
|
||||
# prepare for update
|
||||
dump_file = tables.open_file( self.dump_filename, mode="r+" )
|
||||
|
||||
if dump_file is None:
|
||||
# exit!
|
||||
logger.error("Could not create dump directory '%s', so hdf5 dumps disabled", dir_trunk)
|
||||
self.dump_filename = ""
|
||||
self.dump_timeinterval = 0
|
||||
return True
|
||||
if dump_file is None:
|
||||
# exit!
|
||||
logger.error("Could not create dump directory '%s', so hdf5 dumps disabled", dir_trunk)
|
||||
self.dump_filename = ""
|
||||
self.dump_timeinterval = 0
|
||||
return True
|
||||
|
||||
# no undo please!
|
||||
if dump_file.is_undo_enabled( ):
|
||||
dump_file.disable_undo( )
|
||||
# no undo please!
|
||||
if dump_file.is_undo_enabled( ):
|
||||
dump_file.disable_undo( )
|
||||
|
||||
# save the data!
|
||||
if self.data is not None:
|
||||
self.data.write_hdf5( dump_file, where="/", name="data_pool",
|
||||
complib=self.dump_complib, complevel=self.dump_complevel,
|
||||
incremental=not init, write_interval=self.dump_timeinterval )
|
||||
# save the data!
|
||||
if self.data is not None:
|
||||
self.data.write_hdf5( dump_file, where="/", name="data_pool",
|
||||
complib=self.dump_complib, complevel=self.dump_complevel,
|
||||
incremental=not init, write_interval=self.dump_timeinterval )
|
||||
|
||||
# Re-enable run button if this was called from save thread
|
||||
# NO: this is dangerous as it allows starting a new experiment before cleanup is finished.
|
||||
# it will be re-enabled in observe_running_experiment after joining this thread.
|
||||
# if not init and threading.current_thread().name == "dump states":
|
||||
# gdk.threads_enter()
|
||||
# self.toolbar_run_button.set_sensitive(True)
|
||||
# gdk.threads_leave()
|
||||
# Re-enable run button if this was called from save thread
|
||||
# NO: this is dangerous as it allows starting a new experiment before cleanup is finished.
|
||||
# it will be re-enabled in observe_running_experiment after joining this thread.
|
||||
# if not init and threading.current_thread().name == "dump states":
|
||||
# gdk.threads_enter()
|
||||
# self.toolbar_run_button.set_sensitive(True)
|
||||
# gdk.threads_leave()
|
||||
|
||||
# now save additional information
|
||||
e = 0
|
||||
r = 0
|
||||
if self.si is not None:
|
||||
e = self.si.data.get( "__recentexperiment", -1 ) + 1
|
||||
r = self.si.data.get( "__recentresult", -1 ) + 1
|
||||
timeline_table = dump_file.root.timeline
|
||||
timeline_row = timeline_table.row
|
||||
timeline_row[ "time" ] = time.strftime( "%Y%m%d %H:%M:%S" )
|
||||
timeline_row[ "experiments" ] = e
|
||||
timeline_row[ "results" ] = r
|
||||
timeline_row.append( )
|
||||
timeline_table.flush( )
|
||||
# now save additional information
|
||||
e = 0
|
||||
r = 0
|
||||
if self.si is not None:
|
||||
e = self.si.data.get( "__recentexperiment", -1 ) + 1
|
||||
r = self.si.data.get( "__recentresult", -1 ) + 1
|
||||
timeline_table = dump_file.root.timeline
|
||||
timeline_row = timeline_table.row
|
||||
timeline_row[ "time" ] = time.strftime( "%Y%m%d %H:%M:%S" )
|
||||
timeline_row[ "experiments" ] = e
|
||||
timeline_row[ "results" ] = r
|
||||
timeline_row.append( )
|
||||
timeline_table.flush( )
|
||||
|
||||
# save log window information:
|
||||
# also save backend's logfile information?
|
||||
logtextbuffer = self.log.textbuffer
|
||||
last_end = logtextbuffer.get_mark( "lastdumped" )
|
||||
if last_end is None:
|
||||
last_end = logtextbuffer.create_mark( "lastdumped", logtextbuffer.get_start_iter( ), left_gravity=True )
|
||||
#logtext_start = logtextbuffer.get_iter_at_mark( last_end )
|
||||
logtext_end = logtextbuffer.get_end_iter( )
|
||||
logtextbuffer.move_mark( last_end, logtext_end )
|
||||
# Create new iterators after the mark move
|
||||
logtext_start = logtextbuffer.get_iter_at_mark( last_end )
|
||||
logtext_end = logtextbuffer.get_end_iter( )
|
||||
# recode from unicode
|
||||
logtext = logtextbuffer.get_text( logtext_start, logtext_end, True )
|
||||
# avoid circular references (seems to be necessary with gtk-2.12)
|
||||
# del logtextbuffer, logtext_start, logtext_end, last_end
|
||||
for l in logtext.splitlines( ):
|
||||
dump_file.root.log.append( numpy.array( [ l ], dtype="S120" ) )
|
||||
# save log window information:
|
||||
# also save backend's logfile information?
|
||||
# the dump thread must not race with the GUI thread appending messages
|
||||
with self.log.lock:
|
||||
logtextbuffer = self.log.textbuffer
|
||||
last_end = logtextbuffer.get_mark( "lastdumped" )
|
||||
if last_end is None:
|
||||
last_end = logtextbuffer.create_mark( "lastdumped", logtextbuffer.get_start_iter( ), left_gravity=True )
|
||||
logtext_start = logtextbuffer.get_iter_at_mark( last_end )
|
||||
logtext_end = logtextbuffer.get_end_iter( )
|
||||
logtext = logtextbuffer.get_text( logtext_start, logtext_end, True )
|
||||
logtextbuffer.move_mark( "lastdumped", logtext_end )
|
||||
if logtext is not None:
|
||||
for l in logtext.splitlines( ):
|
||||
dump_file.root.log.append( numpy.array( [ l ], dtype="S120" ) )
|
||||
|
||||
dump_file.flush( )
|
||||
dump_file.close( )
|
||||
dump_file.flush( )
|
||||
finally:
|
||||
if dump_file is not None:
|
||||
dump_file.close( )
|
||||
self.last_dumped = time.time( )
|
||||
del dump_file
|
||||
|
||||
return True
|
||||
|
||||
@@ -1351,6 +1351,7 @@ class LogWindow:
|
||||
self.textview.connect( "key-press-event", self.textview_keypress)
|
||||
apply_font_css(self.textview, "Monospace 10")
|
||||
self.textbuffer = self.textview.get_buffer( )
|
||||
self.lock = threading.Lock( )
|
||||
self.last_timetag = None
|
||||
self( "Started in directory %s\n" % os.getcwd( ) )
|
||||
|
||||
@@ -1365,9 +1366,10 @@ class LogWindow:
|
||||
timetag_int = math.floor( timetag )
|
||||
timetag_ms = int( (timetag - timetag_int) * 1000 )
|
||||
date_tag = time.strftime( "%Y%m%d %H:%M:%S.%%03d:\n", time.localtime( timetag_int ) ) % timetag_ms
|
||||
self.textbuffer.place_cursor( self.textbuffer.get_end_iter( ) )
|
||||
self.textbuffer.insert_at_cursor( date_tag + str( message ) )
|
||||
self.textview.scroll_to_mark( self.textbuffer.get_insert( ), 0.1 , False, 0.5, 0.5)
|
||||
with self.lock:
|
||||
self.textbuffer.place_cursor( self.textbuffer.get_end_iter( ) )
|
||||
self.textbuffer.insert_at_cursor( date_tag + str( message ) )
|
||||
self.textview.scroll_to_mark( self.textbuffer.get_insert( ), 0.1 , False, 0.5, 0.5)
|
||||
|
||||
def textview_keypress( self, widget, event, data=None ):
|
||||
if event.state & gdk.ModifierType.CONTROL_MASK != 0:
|
||||
|
||||
Reference in new issue
Block a user