diff --git a/eventlet/__init__.py b/eventlet/__init__.py index 254a46f..be3a322 100644 --- a/eventlet/__init__.py +++ b/eventlet/__init__.py @@ -17,5 +17,5 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -version_info = (0, 8, 11) +version_info = (0, 9, '0pre') __version__ = '%s.%s.%s' % version_info diff --git a/eventlet/pool.py b/eventlet/pool.py index 59b43e4..793279e 100644 --- a/eventlet/pool.py +++ b/eventlet/pool.py @@ -14,6 +14,18 @@ class Pool(object): else: self.results = None + def resize(self, new_max_size): + """ Change the max_size of the pool. + + If the pool gets resized when there are more than new_max_size + coroutines checked out, when they are returned to the pool + they will be discarded. The return value of free() will be + negative in this situation. + """ + max_size_delta = new_max_size - self.max_size + self.sem.counter += max_size_delta + self.max_size = new_max_size + @property def current_size(self): return len(self.procs) diff --git a/greentest/pools_test.py b/greentest/pools_test.py index ec23000..7edb1d1 100644 --- a/greentest/pools_test.py +++ b/greentest/pools_test.py @@ -143,6 +143,22 @@ class TestIntPool(TestCase): finally: timer.cancel() + def test_resize(self): + pool = IntPool(max_size=2) + a = pool.get() + b = pool.get() + self.assertEquals(pool.free(), 0) + + # verify that the pool discards excess items put into it + pool.resize(1) + pool.put(a) + pool.put(b) + self.assertEquals(pool.free(), 1) + + # resize larger and assert that there are more free items + pool.resize(2) + self.assertEquals(pool.free(), 2) + class TestAbstract(TestCase): mode = 'static' diff --git a/greentest/test__pool.py b/greentest/test__pool.py index b64e7e1..a451e25 100644 --- a/greentest/test__pool.py +++ b/greentest/test__pool.py @@ -69,6 +69,56 @@ class TestCoroutinePool(LimitedTestCase): pool.execute_async(reenter_async) evt.wait() + + def assert_pool_has_free(self, pool, num_free): + def wait_long_time(e): + e.wait() + timer = api.exc_after(1, api.TimeoutError) + try: + evt = coros.event() + for x in xrange(num_free): + pool.execute(wait_long_time, evt) + # if the pool has fewer free than we expect, + # then we'll hit the timeout error + finally: + timer.cancel() + + # if the runtime error is not raised it means the pool had + # some unexpected free items + timer = api.exc_after(0, RuntimeError) + self.assertRaises(RuntimeError, pool.execute, wait_long_time, evt) + + # clean up by causing all the wait_long_time functions to return + evt.send(None) + api.sleep(0) + api.sleep(0) + + def test_resize(self): + pool = self.klass(max_size=2) + evt = coros.event() + def wait_long_time(e): + e.wait() + pool.execute(wait_long_time, evt) + pool.execute(wait_long_time, evt) + self.assertEquals(pool.free(), 0) + self.assert_pool_has_free(pool, 0) + + # verify that the pool discards excess items put into it + pool.resize(1) + + # cause the wait_long_time functions to return, which will + # trigger puts to the pool + evt.send(None) + api.sleep(0) + api.sleep(0) + + self.assertEquals(pool.free(), 1) + self.assert_pool_has_free(pool, 1) + + # resize larger and assert that there are more free items + pool.resize(2) + self.assertEquals(pool.free(), 2) + self.assert_pool_has_free(pool, 2) def test_stderr_raising(self): # testing that really egregious errors in the error handling code