@@ -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