def patch(self):
        if not hasattr(self, "_bundles"):
            return False

        with futures.ThreadPoolExecutor(max_workers=self.threads) as executor:
            bundle_futures = list()
            # download bundles sorted by size
            self._bundles.sort(key=lambda bundle: int(bundle["size"]),
                               reverse=True)
            for bundle in self._bundles:
                bundle_checksum = bundle["checksum"]

                print("* Downloading bundle {0}.".format(bundle_checksum))
                if not self._download_bundle(bundle):
                    return False

                # bundle was downloaded, start extraction in parallel
                print("* Extracting bundle {0}.".format(bundle_checksum))
                bundle_future = executor.submit(self._extract_bundle, bundle)
                bundle_futures.append(bundle_future)

            for completed in futures.as_completed(bundle_futures):
                if not completed.result():
                    # cancel waiting futures
                    for future in bundle_futures:
                        future.cancel()
                    return False

            # if we're here everything has been downloaded and extracted
            return True