1+ import asyncio
12import typing
23
34from ascetic_ddd .faker .domain .distributors .m2o .interfaces import IM2ODistributor , IM2ODistributorFactory
@@ -42,7 +43,9 @@ class RangeDistributorAdapter(IM2ODistributor[T], typing.Generic[T]):
4243 """
4344 _distributor : IO2MDistributor
4445 _values : dict [int , T ]
46+ _next_position : int
4547 _provider_name : str | None
48+ _lock : asyncio .Lock
4649
4750 def __init__ (self , distributor : IO2MDistributor ):
4851 """
@@ -51,7 +54,9 @@ def __init__(self, distributor: IO2MDistributor):
5154 """
5255 self ._distributor = distributor
5356 self ._values = {}
57+ self ._next_position = 0
5458 self ._provider_name = None
59+ self ._lock = asyncio .Lock ()
5560
5661 async def next (
5762 self ,
@@ -61,41 +66,48 @@ async def next(
6166 """
6267 Returns a value from the dictionary by a random number.
6368
69+ If the value is not found, acquires lock and raises Cursor.
70+ The lock is held until cursor.append() is called, serializing
71+ the creation path to guarantee value ordering.
72+ Portable to Go (mutex.Lock in Next, mutex.Unlock in Append).
73+
6474 Raises:
6575 Cursor(): if there is no value in the dictionary for the slot.
66- cursor.position -- the slot number.
67- cursor.append(session, value) -- add a value.
76+ cursor.append(session, value) -- add a value and release lock.
6877 """
6978 num = self ._distributor .distribute ()
7079
71- if num not in self ._values :
72- raise Cursor (
73- position = num ,
74- callback = self . _append ,
75- )
76-
77- value = self . _values [ num ]
78-
79- # Check specification
80- if not await specification . is_satisfied_by ( session , value ):
81- # Value does not satisfy the specification -- try again
82- return await self .next ( session , specification )
83-
84- return Some ( value )
80+ if num in self ._values :
81+ value = self . _values [ num ]
82+ # Check specification
83+ if not await specification . is_satisfied_by ( session , value ):
84+ # Value does not satisfy the specification -- try again
85+ return await self . next ( session , specification )
86+ return Some ( value )
87+
88+ # Creation path — acquire lock, hold until _locked_append
89+ await self . _lock . acquire ()
90+ raise Cursor (
91+ position = len ( self ._values ),
92+ callback = self . _locked_append ,
93+ )
8594
86- async def _append (self , session : ISession , value : T , position : int ):
95+ async def _locked_append (self , session : ISession , value : T , position : int ):
8796 """
88- Adds a value to the dictionary.
97+ Adds a value to the dictionary and releases the lock .
8998
9099 Args:
91100 session: Session
92101 value: Value to add
93- position: Slot number (key in the dictionary).
102+ position: Sequential position (key in the dictionary).
94103 """
95- self ._values [position ] = value
104+ try :
105+ self ._values [position ] = value
106+ finally :
107+ self ._lock .release ()
96108
97109 async def append (self , session : ISession , value : T ):
98- await self ._append ( session , value , - 1 )
110+ self ._values [ len ( self . _values )] = value
99111
100112 @property
101113 def provider_name (self ) -> str | None :
0 commit comments