Skip to content

Commit c33b13d

Browse files
committed
improve package search speed using CooperativeIterator
1 parent de6078b commit c33b13d

File tree

1 file changed

+108
-38
lines changed

1 file changed

+108
-38
lines changed

usr/lib/linuxmint/mintinstall/mintinstall.py

Lines changed: 108 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,69 @@ def __init__(self, category):
614614

615615
PkgInfoSearchCache = namedtuple('PkgInfoSearchCache', ['name', 'display_name', 'keywords', 'summary', 'description'])
616616

617+
618+
class CooperativeIterator:
619+
"""
620+
Iterates over items cooperatively within the GLib main loop, yielding periodically to keep the UI responsive.
621+
"""
622+
def __init__(self, iterable, on_per_item, *, on_progress=None, on_finish=None, on_error=None, max_duration_ms=16, **kwargs):
623+
self._size = len(iterable) if hasattr(iterable, '__len__') else None
624+
self._iterator = iter(iterable)
625+
self._on_per_item = on_per_item
626+
self._on_progress = on_progress
627+
self._on_finish = on_finish
628+
self._on_error = on_error
629+
self._kwargs = kwargs
630+
self._max_duration = max_duration_ms / 1000.0
631+
self._cancelled = False
632+
633+
def run(self):
634+
self._start_time = time.monotonic()
635+
self._current_index = 0
636+
GLib.idle_add(self._process)
637+
638+
def _process(self):
639+
if self._cancelled:
640+
return False
641+
642+
start_time = time.monotonic()
643+
644+
try:
645+
while not self._cancelled:
646+
item = next(self._iterator)
647+
648+
self._on_per_item(item, **self._kwargs)
649+
650+
self._current_index += 1
651+
if self._on_progress and self._size is not None:
652+
self._on_progress(self._current_index / self._size)
653+
654+
if (time.monotonic() - start_time) >= self._max_duration:
655+
return True
656+
657+
except StopIteration:
658+
if os.getenv("DEBUG", False):
659+
elapsed_time = time.monotonic() - self._start_time
660+
print(f"CooperativeIterator: Finished processing {self._on_per_item.__name__} in {elapsed_time:.3f} seconds.")
661+
662+
if self._on_finish:
663+
try:
664+
self._on_finish(**self._kwargs)
665+
except Exception as e:
666+
print(f"CooperativeIterator: Error in on_finish: {e}")
667+
return False
668+
669+
except RuntimeError:
670+
if self._on_error:
671+
try:
672+
self._on_error()
673+
except Exception as e:
674+
print(f"CooperativeIterator: Error in on_error: {e}")
675+
return False
676+
677+
def cancel(self):
678+
self._cancelled = True
679+
617680
class Application(Gtk.Application):
618681
(ACTION_TAB, PROGRESS_TAB, SPINNER_TAB) = list(range(3))
619682

@@ -665,7 +728,7 @@ def __init__(self):
665728
self.one_package_idle_timer = 0
666729
self.installer_pulse_timer = 0
667730
self.search_changed_timer = 0
668-
self.search_idle_timer = 0
731+
self.search_iterator = None
669732
self.generate_search_cache_idle_timer = 0
670733

671734
self.action_button_signal_id = 0
@@ -2210,9 +2273,9 @@ def on_back_button_clicked(self, button):
22102273
self.go_back_action()
22112274

22122275
def cancel_running_search(self):
2213-
if self.search_idle_timer > 0:
2214-
GLib.source_remove(self.search_idle_timer)
2215-
self.search_idle_timer = 0
2276+
if self.search_iterator:
2277+
self.search_iterator.cancel()
2278+
self.search_iterator = None
22162279

22172280
def go_back_action(self):
22182281
XApp.set_window_progress(self.main_window, 0)
@@ -2412,35 +2475,34 @@ def show_search_results(self, terms):
24122475
search_in_description = self.settings.get_boolean(prefs.SEARCH_IN_DESCRIPTION)
24132476

24142477
package_type_preference = self.settings.get_string(prefs.PACKAGE_TYPE_PREFERENCE)
2415-
hidden_packages = set()
24162478
allow_unverified_flatpaks = self.settings.get_boolean(prefs.ALLOW_UNVERIFIED_FLATPAKS)
24172479

2418-
list_size = len(listing)
2419-
self.search_progress = 0
2420-
2421-
def idle_search_one_package(pkginfos, list_size):
2422-
try:
2423-
pkginfo = next(pkginfos)
2424-
except StopIteration:
2425-
self.search_idle_timer = 0
2426-
self.search_progress = 0
2480+
def on_finish(list_size, searched_packages, hidden_packages, package_type_preference, **kwargs):
2481+
self.search_iterator = None
24272482

2428-
if package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_APT:
2429-
results = [p for p in searched_packages if not (p.pkg_hash.startswith("f") and p.name in hidden_packages)]
2430-
elif package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_FLATPAK:
2431-
results = [p for p in searched_packages if not (p.pkg_hash.startswith("a") and p.name in hidden_packages)]
2432-
else:
2433-
results = searched_packages
2483+
if package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_APT:
2484+
results = [p for p in searched_packages if not (p.pkg_hash.startswith("f") and p.name in hidden_packages)]
2485+
elif package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_FLATPAK:
2486+
results = [p for p in searched_packages if not (p.pkg_hash.startswith("a") and p.name in hidden_packages)]
2487+
else:
2488+
results = searched_packages
2489+
self.on_search_results_complete(results)
24342490

2435-
GLib.idle_add(self.on_search_results_complete, results)
2436-
return False
2437-
except RuntimeError: # dictionary changed size during iteration
2438-
self.search_idle_timer = 0
2439-
self.search_progress = 0
2491+
def on_error():
2492+
self.search_iterator = None
24402493

2441-
self.go_back_action()
2442-
return False
2494+
self.go_back_action()
24432495

2496+
def search_one_package(
2497+
pkginfo,
2498+
list_size,
2499+
searched_packages,
2500+
hidden_packages,
2501+
allow_unverified_flatpaks,
2502+
package_type_preference,
2503+
search_in_summary,
2504+
search_in_description
2505+
):
24442506
flatpak = pkginfo.pkg_hash.startswith("f")
24452507
is_match = False
24462508

@@ -2487,21 +2549,29 @@ def idle_search_one_package(pkginfos, list_size):
24872549
elif package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_FLATPAK and flatpak:
24882550
hidden_packages.add(DEB_EQUIVS.get(pkginfo.name))
24892551

2490-
self.search_progress = self.search_progress + 1
2491-
self.update_progress(self.search_progress / list_size)
2492-
2493-
return True
2552+
def on_progress(progress):
2553+
self.update_progress(progress)
2554+
2555+
self.search_iterator = CooperativeIterator(
2556+
listing,
2557+
search_one_package,
2558+
on_finish=on_finish,
2559+
on_error=on_error,
2560+
on_progress=on_progress,
2561+
list_size=len(listing),
2562+
searched_packages=[],
2563+
hidden_packages=set(),
2564+
allow_unverified_flatpaks=allow_unverified_flatpaks,
2565+
package_type_preference=package_type_preference,
2566+
search_in_summary=search_in_summary,
2567+
search_in_description=search_in_description
2568+
)
2569+
self.search_iterator.run()
24942570

2495-
self.search_idle_timer = GLib.idle_add(idle_search_one_package, iter(listing), list_size)
24962571

24972572
def update_progress(self, progress):
24982573
progress = max(0.0, min(1.0, progress))
2499-
2500-
def update_progress_ui():
2501-
self.progress_bar.set_fraction(progress)
2502-
return False
2503-
2504-
GLib.idle_add(update_progress_ui)
2574+
self.progress_bar.set_fraction(progress)
25052575

25062576
def on_search_results_complete(self, results):
25072577
self.page_stack.set_visible_child_name(self.PAGE_LIST)

0 commit comments

Comments
 (0)