diff --git a/rigo/rigo_app.py b/rigo/rigo_app.py index 168cb6c06..7be55e636 100644 --- a/rigo/rigo_app.py +++ b/rigo/rigo_app.py @@ -195,6 +195,14 @@ class RigoServiceController(GObject.Object): if our_signals: sig_match = our_signals.pop(0) sig_match.remove() + else: + # somebody already consumed this signal + if const_debug_enabled(): + const_debug_write( + __name__, + "_repositories_updated_signal: " + "already consumed") + return self._scale_down() # do we really need this? not really i think self._release_local_resources() @@ -203,7 +211,9 @@ class RigoServiceController(GObject.Object): self.emit("repositories-updated") if const_debug_enabled(): - const_debug_write(__name__, "RigoServiceController: unlock-ui") + const_debug_write( + __name__, + "_repositories_updated_signal: repositories-updated") def _output_signal(self, text, header, footer, back, importance, level, count_c, count_t, percent): @@ -348,7 +358,6 @@ class RigoServiceController(GObject.Object): in exclusive mode. Scale up privileges, and ask for root password if not done yet. """ - # FIXME, complete, need to be nice and not block, etc # FIXME, ask for password. acquired_sem = Semaphore(0) @@ -428,7 +437,8 @@ class RigoServiceController(GObject.Object): self._update_repositories_unlocked( repositories, force) - def _update_repositories_unlocked(self, repositories, force): + def _update_repositories_unlocked(self, repositories, force, + execute_method=True): """ Internal method handling the actual Repositories Update execution. @@ -457,13 +467,23 @@ class RigoServiceController(GObject.Object): # 1 -- ACTIVITY CRIT :: ON self._activity_rwsem.writer_acquire() - # connect our signal - sig_match = self._entropy_bus.connect_to_signal( - self._REPOSITORIES_UPDATED_SIGNAL, - self._repositories_updated_signal, - dbus_interface=self.DBUS_INTERFACE) + signal_sem = Semaphore(1) + + def _repositories_updated_signal(result, message): + if not signal_sem.acquire(False): + # already called, no need to call again + return + # this is done in order to have it called + # only once by two different code paths + self._repositories_updated_signal(result, message) with self._registered_signals_mutex: + # connect our signal + sig_match = self._entropy_bus.connect_to_signal( + self._REPOSITORIES_UPDATED_SIGNAL, + _repositories_updated_signal, + dbus_interface=self.DBUS_INTERFACE) + # and register it as a signal generated by us obj = self._registered_signals.setdefault( self._REPOSITORIES_UPDATED_SIGNAL, []) @@ -477,10 +497,43 @@ class RigoServiceController(GObject.Object): self._terminal.reset() self._release_local_resources() - iface = dbus.Interface( - self._entropy_bus, - dbus_interface=self.DBUS_INTERFACE) - iface.update_repositories(repositories, force) + + if execute_method: + dbus.Interface( + self._entropy_bus, + dbus_interface=self.DBUS_INTERFACE + ).update_repositories(repositories, force) + else: + # check if we need to cope with races + self._update_repositories_signal_check( + sig_match, signal_sem) + + def _update_repositories_signal_check(self, sig_match, signal_sem): + """ + Called via _update_repositories_unlocked() in order to handle + the possible race between RigoDaemon signal and the fact that + we just lost it. + """ + activity = self.activity() + if activity == ActivityStates.UPDATING_REPOSITORIES: + return + + # lost the signal or not, we're going to force + # the callback. + if not signal_sem.acquire(False): + # already called, no need to call again + const_debug_write( + __name__, + "_update_repositories_signal_check: abort") + return + + const_debug_write( + __name__, + "_update_repositories_signal_check: accepting") + # Run in the main loop, to avoid calling a signal + # callback in random threads. + GLib.idle_add(self._repositories_updated_signal, + 0, "") def update_repositories(self, repositories, force): """ @@ -570,7 +623,6 @@ class WorkViewController(GObject.Object): self._box.pack_start(box, True, True, 0) self._service.set_terminal(self._terminal) - self._service.set_work_controller(self) self.deactivate_progress_bar() self.deactivate_app_box() @@ -2123,6 +2175,7 @@ class Rigo(Gtk.Application): self._work_view_c = WorkViewController( self._service, self._work_view) + self._service.set_work_controller(self._work_view_c) def is_ui_locked(self): """ @@ -2317,8 +2370,11 @@ class Rigo(Gtk.Application): acquired = not self._entropy.wait_resources( max_lock_count=1, shared=True) + is_exclusive = False if not acquired: # check whether RigoDaemon is running in excluive mode + # and ignore non-atomicity here (failing with error + # is acceptable) if not self._service.is_exclusive(): self._show_ok_dialog( None, @@ -2327,6 +2383,7 @@ class Rigo(Gtk.Application): entropy.tools.kill_threads() Gtk.main_quit() return + is_exclusive = True # otherwise we can go ahead and handle our state later # check RigoDaemon, don't worry about races between Rigo Clients @@ -2335,11 +2392,21 @@ class Rigo(Gtk.Application): # based rwsem). activity = self._service.activity() if activity != ActivityStates.AVAILABLE: + msg = "" + show_dialog = True + if activity == ActivityStates.NOT_AVAILABLE: msg = _("Background Service is currently not available") + elif activity == ActivityStates.UPDATING_REPOSITORIES: - # FIXME, jump to WORK_VIEW and show the progress. - msg = _("Background Service is updating repositories") + show_dialog = False + task = ParallelTask( + self._service._update_repositories_unlocked, + [], False, execute_method=False) + task.daemon = True + task.name = "UpdateRepositoriesUnlocked" + task.start() + elif activity == ActivityStates.INSTALLING_APPLICATION: # FIXME, jump to WORK_VIEW and show the progress. msg = _("Background Service is installing Applications") @@ -2351,6 +2418,18 @@ class Rigo(Gtk.Application): else: msg = _("Background Service is incompatible with Rigo") + if show_dialog: + self._show_ok_dialog( + None, + escape_markup(_("Rigo")), + escape_markup(msg)) + entropy.tools.kill_threads() + Gtk.main_quit() + return + + elif is_exclusive: + msg = _("Background Service is currently unavailable") + # no lock acquired, cannot continue the initialization self._show_ok_dialog( None, escape_markup(_("Rigo")),