diff --git a/CHANGELOG.md b/CHANGELOG.md
index a73bc421399e29c08873a9e6902237ae5afde524..d11c11e4300285195e40219f0b544cd33d0ee1ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
 # v2.17.4
 
 * warnings for some use cases of Closeable
+* added an optional top limit for IDAllocator
diff --git a/satella/__init__.py b/satella/__init__.py
index 1b1cd0327bf37dc564b76f4d260019b63a2c2878..28312a5e524df9cdff18388cf13c6a68b3e885da 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.17.4a2'
+__version__ = '2.17.4'
diff --git a/satella/coding/concurrent/id_allocator.py b/satella/coding/concurrent/id_allocator.py
index c3b974bad03438f6a0eb70a482b3207a4d73d617..a9d2665398b1c9a6e72fa6160380ec91f5be0778 100644
--- a/satella/coding/concurrent/id_allocator.py
+++ b/satella/coding/concurrent/id_allocator.py
@@ -1,5 +1,7 @@
+import math
+import typing as tp
 from .monitor import Monitor
-from ...exceptions import AlreadyAllocated
+from ...exceptions import AlreadyAllocated, Empty
 
 
 class SequentialIssuer(Monitor):
@@ -57,20 +59,30 @@ class IDAllocator(Monitor):
     Thread-safe.
 
     :param start_at: the lowest integer that the allocator will return
+    :param top_limit: the maximum value that will not be allocated. If used,
+        subsequent calls to :meth:`~satella.coding.concurrent.IDAllocator.allocate_int` will
+        raise :class:`~satella.exceptions.Empty`
     """
-    __slots__ = ('start_at', 'ints_allocated', 'free_ints', 'bound')
+    __slots__ = 'start_at', 'ints_allocated', 'free_ints', 'bound', 'top_limit'
 
-    def __init__(self, start_at: int = 0):
+    def __init__(self, start_at: int = 0, top_limit: tp.Optional[int] = None):
         super().__init__()
         self.start_at = start_at
         self.ints_allocated = set()
         self.free_ints = set()
+        self.top_limit = top_limit or math.inf
         self.bound = 0
 
-    def _extend_the_bound_to(self, x: int):
+    def _extend_the_bound_to(self, x: int) -> int:
+        """Return how many integers added"""
+        if x > self.top_limit:
+            x = self.top_limit
+        how_many = 0
         for i in range(self.bound, x):
             self.free_ints.add(i)
+            how_many += 1
         self.bound = x
+        return how_many
 
     @Monitor.synchronized
     def mark_as_free(self, x: int):
@@ -94,9 +106,11 @@ class IDAllocator(Monitor):
         Return a previously unallocated int, and mark it as allocated
 
         :return: an allocated int
+        :raises Empty: could not allocate an int due to top limit
         """
         if not self.free_ints:
-            self._extend_the_bound_to(self.bound + 10)
+            if self._extend_the_bound_to(self.bound + 10) == 0:
+                raise Empty('No integers remaining!')
         x = self.free_ints.pop()
         self.ints_allocated.add(x)
         return x + self.start_at
@@ -112,6 +126,8 @@ class IDAllocator(Monitor):
         """
         if x < self.start_at:
             raise ValueError('%s is less than start_at' % (x,))
+        if x >= self.top_limit:
+            raise ValueError('Cannot allocate a value greater or equal than top limit!')
         x -= self.start_at
         if x >= self.bound:
             self._extend_the_bound_to(x + 1)
diff --git a/tests/test_coding/test_concurrent.py b/tests/test_coding/test_concurrent.py
index cdbff9c64b67cc2a9594b6608e61e09856522ef0..43ba7c65ffc9fa6b5e6cdfed43ca57b185a07b7c 100644
--- a/tests/test_coding/test_concurrent.py
+++ b/tests/test_coding/test_concurrent.py
@@ -340,6 +340,13 @@ class TestConcurrent(unittest.TestCase):
         self.assertEqual(fut.result(), 5)
         self.assertEqual(a['test'], 2)
 
+    def test_id_allocator_top_limit(self):
+        id_alloc = IDAllocator(top_limit=10)
+        for i in range(10):
+            id_alloc.allocate_int()
+        self.assertRaises(Empty, id_alloc.allocate_int)
+        self.assertRaises(ValueError, lambda: id_alloc.mark_as_allocated(12))
+
     def test_id_allocator(self):
         id_alloc = IDAllocator()
         x = set([id_alloc.allocate_int(), id_alloc.allocate_int(), id_alloc.allocate_int()])