Skip to content

Commit bb2f8d6

Browse files
committed
address review: serialize _open() with asyncio.Lock
Capture critical section in a per-sender lock so concurrent coroutines cannot race on creating and closing self._handler. Re-checks _running inside the lock so the loser of the race no-ops cleanly. Signed-off-by: SAY-5 <say.apm35@gmail.com>
1 parent 258d0bc commit bb2f8d6

1 file changed

Lines changed: 29 additions & 22 deletions

File tree

sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_sender_async.py

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ def __init__(
146146
self._create_attribute(**kwargs)
147147
self._connection = kwargs.get("connection")
148148
self._handler: Union["pyamqp_SendClientAsync", "uamqp_SendClientAsync"]
149+
# Serializes _open() so concurrent callers cannot race on creating
150+
# and closing self._handler (see issue #35618). Initialized lazily
151+
# because the constructor may execute outside a running event loop.
152+
self._open_lock: Optional[asyncio.Lock] = None
149153

150154
async def __aenter__(self) -> "ServiceBusSender":
151155
if self._shutdown.is_set():
@@ -203,28 +207,31 @@ def _create_handler(self, auth: Union["uamqp_JWTTokenAuthAsync", "pyamqp_JWTToke
203207
async def _open(self):
204208
if self._running:
205209
return
206-
if self._handler:
207-
await self._handler.close_async()
208-
auth = None if self._connection else (await create_authentication(self))
209-
self._create_handler(auth)
210-
# Capture a local reference to the handler to guard against concurrent
211-
# coroutines mutating self._handler across awaits (see issue #35618).
212-
handler = self._handler
213-
try:
214-
await handler.open_async(connection=self._connection)
215-
while not await handler.client_ready_async():
216-
await asyncio.sleep(0.05)
217-
self._running = True
218-
self._max_message_size_on_link = (
219-
self._amqp_transport.get_remote_max_message_size(handler) or MAX_MESSAGE_LENGTH_BYTES
220-
)
221-
if self._max_message_size_on_link >= MAX_BATCH_SIZE_PREMIUM:
222-
self._max_batch_size_on_link = MAX_BATCH_SIZE_PREMIUM
223-
else:
224-
self._max_batch_size_on_link = MAX_BATCH_SIZE_STANDARD
225-
except:
226-
await self._close_handler()
227-
raise
210+
if self._open_lock is None:
211+
self._open_lock = asyncio.Lock()
212+
async with self._open_lock:
213+
if self._running:
214+
return
215+
if self._handler:
216+
await self._handler.close_async()
217+
auth = None if self._connection else (await create_authentication(self))
218+
self._create_handler(auth)
219+
handler = self._handler
220+
try:
221+
await handler.open_async(connection=self._connection)
222+
while not await handler.client_ready_async():
223+
await asyncio.sleep(0.05)
224+
self._running = True
225+
self._max_message_size_on_link = (
226+
self._amqp_transport.get_remote_max_message_size(handler) or MAX_MESSAGE_LENGTH_BYTES
227+
)
228+
if self._max_message_size_on_link >= MAX_BATCH_SIZE_PREMIUM:
229+
self._max_batch_size_on_link = MAX_BATCH_SIZE_PREMIUM
230+
else:
231+
self._max_batch_size_on_link = MAX_BATCH_SIZE_STANDARD
232+
except:
233+
await self._close_handler()
234+
raise
228235

229236
async def _send(
230237
self,

0 commit comments

Comments
 (0)