Skip to content
This repository has been archived by the owner on May 2, 2021. It is now read-only.

Commit

Permalink
Merge pull request #37 from smitchell556/issue_36_race_condition
Browse files Browse the repository at this point in the history
Fix race condition in `get_resource`
  • Loading branch information
spenceforce committed Dec 2, 2019
2 parents 1c363a7 + 00984e1 commit cdc9a52
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ project adheres to [Semantic Versioning](http://semver.org).
- Dropped support for Python 3.3. This does not mean it won't work for Python
3.3, but it will no longer be tested.
- Add support for Python 3.7 and 3.8.
- Fix race condition in `get_resource` see [issue #36](https://github.com/smitchell556/cuttlepool/issues/36).

## [0.8.0] - 2018-02-28
### Added
Expand Down
81 changes: 41 additions & 40 deletions cuttlepool.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,51 +297,52 @@ def get_resource(self, resource_wrapper=None):
if resource_wrapper is None:
resource_wrapper = self._resource_wrapper

if self.empty():
self._harvest_lost_resources()

try:
rtracker = self._get(0)
except PoolEmptyError:
pass

if rtracker is None:
# Could not find resource, try to make one.
try:
rtracker = self._make_resource()
except PoolFullError:
pass
with self._lock:
if self.empty():
self._harvest_lost_resources()

if rtracker is None:
# Could not find or make resource, so must wait for a resource
# to be returned to the pool.
try:
rtracker = self._get(timeout=self._timeout)
rtracker = self._get(0)
except PoolEmptyError:
pass

if rtracker is None:
raise PoolEmptyError

# Ensure resource is active.
if not self.ping(rtracker.resource):
# Lock here to prevent another thread creating a resource in the
# index that will have this resource removed. This ensures there
# will be space for _make_resource() to place a newly created
# resource.
with self._lock:
self._remove(rtracker)
rtracker = self._make_resource()

# Ensure all resources leave pool with same attributes.
# normalize_connection() is used since it calls
# normalize_resource(), so if a user implements either one, the
# resource will still be normalized. This will be changed in 1.0 to
# call normalize_resource() when normalize_connection() is
# removed.
self.normalize_connection(rtracker.resource)

return rtracker.wrap_resource(self, resource_wrapper)
if rtracker is None:
# Could not find resource, try to make one.
try:
rtracker = self._make_resource()
except PoolFullError:
pass

if rtracker is None:
# Could not find or make resource, so must wait for a resource
# to be returned to the pool.
try:
rtracker = self._get(timeout=self._timeout)
except PoolEmptyError:
pass

if rtracker is None:
raise PoolEmptyError

# Ensure resource is active.
if not self.ping(rtracker.resource):
# Lock here to prevent another thread creating a resource in the
# index that will have this resource removed. This ensures there
# will be space for _make_resource() to place a newly created
# resource.
with self._lock:
self._remove(rtracker)
rtracker = self._make_resource()

# Ensure all resources leave pool with same attributes.
# normalize_connection() is used since it calls
# normalize_resource(), so if a user implements either one, the
# resource will still be normalized. This will be changed in 1.0 to
# call normalize_resource() when normalize_connection() is
# removed.
self.normalize_connection(rtracker.resource)

return rtracker.wrap_resource(self, resource_wrapper)

def normalize_connection(self, connection):
"""For compatibility with older versions, will be removed in 1.0."""
Expand Down

0 comments on commit cdc9a52

Please sign in to comment.