cleanup: use ordinary invokeLater and avoid unnecessary synchronization