@@ -157,14 +157,48 @@ export interface ProcessScope<Event> {
157157
158158export interface ProcessContext < State , Event > extends ProcessScope < Event > {
159159 readonly receive : Effect . Effect < Event >
160- readonly mailbox : Queue . Dequeue < Event >
160+ /** @internal */
161+ readonly mailbox ?: Queue . Dequeue < Event >
162+ /** @internal */
163+ readonly poll ?: Effect . Effect < Option . Option < Event > >
161164 readonly state : Effect . Effect < State >
162165 readonly setState : ( state : State ) => Effect . Effect < void >
163166 readonly updateState : < E , R > (
164167 f : ( state : State ) => Effect . Effect < State , E , R >
165168 ) => Effect . Effect < void , E , R >
166169}
167170
171+ interface CompactProcessMailbox < Event > {
172+ items : Array < Event > | undefined
173+ index : number
174+ closed : boolean
175+ }
176+
177+ const offerCompactMailbox = < Event > ( mailbox : CompactProcessMailbox < Event > , event : Event ) : void => {
178+ const items = mailbox . items ?? [ ]
179+ mailbox . items = items
180+ items . push ( event )
181+ }
182+
183+ const pollCompactMailbox = < Event > ( mailbox : CompactProcessMailbox < Event > ) : Option . Option < Event > => {
184+ if ( mailbox . items === undefined ) {
185+ return Option . none ( )
186+ }
187+ const event = mailbox . items [ mailbox . index ] !
188+ mailbox . index += 1
189+ if ( mailbox . index === mailbox . items . length ) {
190+ mailbox . items = undefined
191+ mailbox . index = 0
192+ }
193+ return Option . some ( event )
194+ }
195+
196+ const closeCompactMailbox = ( mailbox : CompactProcessMailbox < unknown > ) : void => {
197+ mailbox . closed = true
198+ mailbox . items = undefined
199+ mailbox . index = 0
200+ }
201+
168202export interface ProcessLogic <
169203 State ,
170204 Event ,
@@ -1145,11 +1179,24 @@ const startCompiledInternal: <
11451179
11461180 const sessionId = yield * options . runtime . nextSessionId
11471181 const id = options . id ?? sessionId
1148- const queue = yield * Queue . unbounded < Event > ( )
1182+ const onDemand = logic . drain !== undefined
1183+ const queue = onDemand ? undefined : yield * Queue . unbounded < Event > ( )
1184+ // An on-demand drain never blocks on mailbox input: send schedules its owner
1185+ // whenever the FIFO becomes non-empty. Keep persistent custom processes on
1186+ // Queue, but avoid retaining Queue's waiting/backpressure machinery for the
1187+ // compiled protocol that only needs synchronous offer and poll operations.
1188+ const compactMailbox : CompactProcessMailbox < Event > | undefined = onDemand
1189+ ? { items : undefined , index : 0 , closed : false }
1190+ : undefined
1191+ const poll = compactMailbox === undefined
1192+ ? Queue . poll ( queue ! )
1193+ : Effect . sync ( ( ) => pollCompactMailbox ( compactMailbox ) )
1194+ const shutdownMailbox = compactMailbox === undefined
1195+ ? Queue . shutdown ( queue ! )
1196+ : Effect . sync ( ( ) => closeCompactMailbox ( compactMailbox ) )
11491197 const termination = yield * Deferred . make < ProcessTermination > ( )
11501198 const done = yield * Deferred . make < Output , Error | StoppedError > ( )
11511199 const awaitCompletion = Deferred . await ( done ) . pipe ( Effect . exit , Effect . asVoid )
1152- const onDemand = logic . drain !== undefined
11531200 const drainServices = onDemand ? yield * Effect . context < Requirements > ( ) : undefined
11541201 let worker : Fiber . Fiber < any , never > | undefined
11551202 let draining = false
@@ -1158,10 +1205,12 @@ const startCompiledInternal: <
11581205 let terminationRequested = false
11591206 let requestRuntimeTermination = ( requested : ProcessTermination ) : Effect . Effect < boolean > =>
11601207 Deferred . succeed ( termination , requested )
1161- let sendEvent = ( event : Event ) : Effect . Effect < void , StoppedError > =>
1162- Queue . offer ( queue , event ) . pipe (
1163- Effect . flatMap ( ( accepted ) => accepted ? Effect . void : Effect . fail ( new StoppedError ( ) ) )
1164- )
1208+ let sendEvent = compactMailbox !== undefined
1209+ ? ( event : Event ) : Effect . Effect < void , StoppedError > => Effect . sync ( ( ) => offerCompactMailbox ( compactMailbox , event ) )
1210+ : ( event : Event ) : Effect . Effect < void , StoppedError > =>
1211+ Queue . offer ( queue ! , event ) . pipe (
1212+ Effect . flatMap ( ( accepted ) => accepted ? Effect . void : Effect . fail ( new StoppedError ( ) ) )
1213+ )
11651214 let settleRequestedTermination = ( _requested : ProcessTermination ) : Effect . Effect < void > => Effect . void
11661215 const interruptWorker : Effect . Effect < void > = Effect . suspend ( ( ) =>
11671216 worker === undefined
@@ -1410,7 +1459,7 @@ const startCompiledInternal: <
14101459 Effect . asVoid
14111460 )
14121461 return Effect . uninterruptible (
1413- Queue . shutdown ( queue ) . pipe (
1462+ shutdownMailbox . pipe (
14141463 Effect . andThen ( closeChildren ( exit ) ) ,
14151464 Effect . andThen ( setAndPublishSnapshot ( snapshot ) ) ,
14161465 Effect . andThen ( notifyOutcome ) ,
@@ -1536,8 +1585,8 @@ const startCompiledInternal: <
15361585
15371586 const context : ProcessContext < State , Event > = {
15381587 ...scope ,
1539- receive : Queue . take ( queue ) ,
1540- mailbox : queue ,
1588+ receive : queue === undefined ? Effect . never : Queue . take ( queue ) ,
1589+ poll ,
15411590 state : getCurrent . pipe ( Effect . map ( ( current ) => current . snapshot . state ) ) ,
15421591 setState : setActiveState ,
15431592 updateState : ( f ) =>
@@ -1683,13 +1732,11 @@ const startCompiledInternal: <
16831732 sendEvent = ( event ) =>
16841733 Effect . uninterruptible (
16851734 Effect . suspend ( ( ) => {
1686- if ( ! Queue . offerUnsafe ( queue , event ) ) {
1735+ if ( compactMailbox ! . closed || terminationRequested ) {
16871736 return Effect . fail ( new StoppedError ( ) )
16881737 }
1738+ offerCompactMailbox ( compactMailbox ! , event )
16891739 offerRevision += 1
1690- if ( terminationRequested ) {
1691- return Effect . fail ( new StoppedError ( ) )
1692- }
16931740 if ( draining ) {
16941741 return Effect . void
16951742 }
0 commit comments