[PATCH] Make the Welcome spoke wait for Geolocation lookup to finish (#975193)

Martin Kolman mkolman at redhat.com
Mon Jul 8 17:33:22 UTC 2013


The GeoIP lookup usually takes between 1 to 2 seconds to finish.
In some cases, such as when running on a live system, the welcome spoke
might show up before the lookup can finish, so the result of the lookup
can not be used to pre-set the language.

To improve this, the geoloc module top level functions now have an
optional wait parameter, that makes the function block until the
lookup is is done or a timeout is reached.

The end result is, that the Welcome spoke will wait up to 3 seconds
for the lookup to finish. Usually it would wait less than 2 seconds or
not at all (if the lookup finishes before the welcome spoke is even
initialized).

Signed-off-by: Martin Kolman <mkolman at gmail.com>
---
 pyanaconda/constants.py             |  3 ++
 pyanaconda/geoloc.py                | 69 +++++++++++++++++++++++++++++++++----
 pyanaconda/ui/gui/spokes/welcome.py |  2 +-
 3 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index ee9ffdb..175dc36 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -141,6 +141,9 @@ GEOLOC_GEOCODER_NOMINATIM = "geocoder_nominatim"
 # default providers
 GEOLOC_DEFAULT_PROVIDER = GEOLOC_PROVIDER_FEDORA_GEOIP
 GEOLOC_DEFAULT_GEOCODER = GEOLOC_GEOCODER_NOMINATIM
+# timeout (in seconds)
+GEOLOC_TIMEOUT = 3
+
 
 ANACONDA_ENVIRON = "anaconda"
 FIRSTBOOT_ENVIRON = "firstboot"
diff --git a/pyanaconda/geoloc.py b/pyanaconda/geoloc.py
index 5a6da14..d859621 100644
--- a/pyanaconda/geoloc.py
+++ b/pyanaconda/geoloc.py
@@ -116,6 +116,8 @@ from pyanaconda.threads import AnacondaThread, threadMgr
 from pyanaconda import nm
 
 location_info_instance = None
+refresh_condition = threading.Condition()
+refresh_in_progress = False
 
 
 def init_geolocation(provider_id=constants.GEOLOC_DEFAULT_PROVIDER):
@@ -143,7 +145,7 @@ def refresh():
         log.debug("Geoloc: refresh() called before init_geolocation()")
 
 
-def get_territory_code():
+def get_territory_code(wait=False):
     """This function returns the current country code
     or None, if:
     - no results were found
@@ -151,16 +153,17 @@ def get_territory_code():
     - the geolocation module was not activated (init & refresh were not called)
      - this is for example the case during image and directory installs
 
+    :param wait: wait for wait seconds for lookup in progress to finish
     :return: current country code or None if not known
     :rtype: string or None
     """
-    if location_info_instance:
+    if _get_location_info_instance(wait):
         return location_info_instance.get_territory_code()
     else:
         return None
 
 
-def get_timezone():
+def get_timezone(wait=False):
     """This function returns the current time zone
     or None, if:
     - no timezone was found
@@ -168,28 +171,29 @@ def get_timezone():
     - the geolocation module was not activated (init & refresh were not called)
      - this is for example the case during image and directory installs
 
+    :param wait: wait for wait seconds for lookup in progress to finish
     :return: current timezone or None if not known
     :rtype: string or None
     """
-    if location_info_instance:
+    if _get_location_info_instance(wait):
         return location_info_instance.get_timezone()
     else:
         return None
 
 
-def get_result():
+def get_result(wait=False):
     """Returns the current geolocation result wrapper
     or None, if:
     - no results were found
     - the refresh is still in progress
     - the geolocation module was not activated (init & refresh were not called)
-
      - this is for example the case during image and directory installs
 
+    :param wait: wait for wait seconds for lookup in progress to finish
     :return: LocationResult instance or None if location is unknown
     :rtype: LocationResult or None
     """
-    if location_info_instance:
+    if _get_location_info_instance(wait):
         return location_info_instance.get_result()
     else:
         return None
@@ -236,6 +240,50 @@ def _get_provider(provider_id):
     return provider()
 
 
+def _get_location_info_instance(wait=False):
+    """Return instance of the location info object
+    and optionally wait for the Geolocation thread to finish
+
+    If there is no lookup in progress (no Geolocation refresh thread
+    is running), this function returns at once).
+
+    Meaning of the wait parameter:
+    - False or <=0: don't wait
+    - True : wait for default number of seconds specified by
+    the GEOLOC_TIMEOUT constant
+    - >0 : wait for a given number of seconds
+
+    :param wait: specifies if this function should wait
+    for the lookup to finish before returning the instance
+    :type wait: bool or integer or float
+    """
+    if not wait:
+        # just returns the instance
+        return location_info_instance
+
+    # check if wait is a boolean or a number
+    if wait is True:
+        # use the default waiting period
+        wait = constants.GEOLOC_TIMEOUT
+    # check if there is a refresh in progress
+    start_time = time.time()
+    refresh_condition.acquire()
+    if refresh_in_progress:
+        refresh_condition.wait(timeout=wait)
+        if refresh_in_progress:
+            log.info("Waiting for Geolocation timed out after %d seconds."
+                     % wait)
+            # please note that this does not mean that the actual
+            # geolocation lookup was stopped in any way, it just
+            # means the caller was unblocked after the waiting period
+            # ended while the lookup thread is still running
+        else:
+            elapsed_time = time.time() - start_time
+            log.info("Waited %1.2f seconds for Geolocation" % elapsed_time)
+    refresh_condition.release()
+    return location_info_instance
+
+
 class GeolocationError(Exception):
     """Exception class for geolocation related errors"""
     pass
@@ -413,6 +461,9 @@ class GeolocationBackend(object):
         if force is True or self._result is None:
             log.info("Starting geolocation lookup")
             log.info("Geolocation provider: %s" % self.get_name())
+            global refresh_in_progress
+            with refresh_condition:
+                    refresh_in_progress = True
             try:
                 start_time = time.time()
                 self._refresh()
@@ -422,6 +473,10 @@ class GeolocationBackend(object):
                 message = "GeoIP lookup ended with exception"
                 message += "\n%s" % e
                 log.debug(message)
+            with refresh_condition:
+                refresh_in_progress = False
+                refresh_condition.notify_all()
+            # check if there were any results
             result = self.get_result()
             if result:
                 log.info("%s" % result)
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index 5c3a47f..fec9b68 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -156,7 +156,7 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
 
         # We can use the territory from geolocation here
         # to preselect the translation, when it's available.
-        territory = geoloc.get_territory_code()
+        territory = geoloc.get_territory_code(wait=True)
         self.language = Language(LOCALE_PREFERENCES, territory=territory)
 
         # check if there is one and only one locale for the territory
-- 
1.8.3.1



More information about the anaconda-patches mailing list