diff --git a/bindings/python/fdb/impl.py b/bindings/python/fdb/impl.py index 7adeedbbac..fb248eb44a 100644 --- a/bindings/python/fdb/impl.py +++ b/bindings/python/fdb/impl.py @@ -30,6 +30,7 @@ import datetime import platform import os import sys +import multiprocessing from fdb import six @@ -38,6 +39,8 @@ _network_thread_reentrant_lock = threading.RLock() _open_file = open +_thread_local_storage = threading.local() + import weakref @@ -593,7 +596,27 @@ class Future(_FDBBase): return bool(self.capi.fdb_future_is_ready(self.fpointer)) def block_until_ready(self): - self.capi.fdb_future_block_until_ready(self.fpointer) + # Checking readiness is faster than using the callback, so it saves us time if we are already + # ready. It also doesn't add much to the cost of this function + if not self.is_ready(): + # Blocking in the native client from the main thread prevents Python from handling signals. + # To avoid that behavior, we implement the blocking in Python using semaphores and on_ready. + # Using a Semaphore is faster than an Event, and we create only one per thread to avoid the + # cost of creating one every time. + semaphore = getattr(_thread_local_storage, 'future_block_semaphore', None) + if semaphore is None: + semaphore = multiprocessing.Semaphore(0) + _thread_local_storage.future_block_semaphore = semaphore + + self.on_ready(lambda self: semaphore.release()) + + try: + semaphore.acquire() + except: + # If this semaphore didn't actually get released, then we need to replace our thread-local + # copy so that later callers still function correctly + _thread_local_storage.future_block_semaphore = multiprocessing.Semaphore(0) + raise def on_ready(self, callback): def cb_and_delref(ignore): diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 9b34c64757..cf27f68de9 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -30,6 +30,7 @@ Bindings * Golang: Deprecated ``fdb.StartNetwork``, ``fdb.Open``, ``fdb.MustOpen``, and ``fdb.CreateCluster`` and added ``fdb.OpenDatabase`` and ``fdb.MustOpenDatabase``. The preferred way to start the network and get a ``Database`` is by using ``FDB.OpenDatabase`` or ``FDB.OpenDefault``. `(PR #942) `_ * Flow: Deprecated ``API::createCluster`` and ``Cluster`` and added ``API::createDatabase``. The preferred way to get a ``Database`` is by using ``API::createDatabase``. `(PR #942) `_ * Golang: Added ``fdb.Printable`` to print a human-readable string for a given byte array. Add ``Key.String()``, which converts the ``Key`` to a ``string`` using the ``Printable`` function. `(PR #1010) `_ +* Python: Python signal handling didn't work when waiting on a future. In particular, pressing Ctrl-C would not successfully interrupt the program. `(PR #1138) `_ Other Changes -------------