11using System ;
22using System . Collections . Generic ;
3+ using System . Runtime . CompilerServices ;
34using System . Runtime . InteropServices ;
45using System . Threading ;
56using System . Threading . Tasks ;
67using Microsoft . Extensions . Logging ;
7- using Temporalio . Bridge . Interop ;
88
99namespace Temporalio . Bridge
1010{
@@ -14,8 +14,8 @@ namespace Temporalio.Bridge
1414 internal class CustomSlotSupplier : NativeInvokeableClass < Interop . CustomSlotSupplierCallbacks >
1515 {
1616 private readonly ILogger logger ;
17- private readonly Temporalio . Worker . Tuning . ICustomSlotSupplier userSupplier ;
18- private readonly Dictionary < uint , Temporalio . Worker . Tuning . ISlotPermit > permits = new ( ) ;
17+ private readonly Temporalio . Worker . Tuning . CustomSlotSupplier userSupplier ;
18+ private readonly Dictionary < uint , Temporalio . Worker . Tuning . SlotPermit > permits = new ( ) ;
1919 private uint permitId = 1 ;
2020
2121 /// <summary>
@@ -24,7 +24,7 @@ internal class CustomSlotSupplier : NativeInvokeableClass<Interop.CustomSlotSupp
2424 /// <param name="userSupplier">User's slot supplier implementation'.</param>
2525 /// <param name="loggerFactory">Logger factory.</param>
2626 internal unsafe CustomSlotSupplier (
27- Temporalio . Worker . Tuning . ICustomSlotSupplier userSupplier ,
27+ Temporalio . Worker . Tuning . CustomSlotSupplier userSupplier ,
2828 ILoggerFactory loggerFactory )
2929 {
3030 this . logger = loggerFactory . CreateLogger < CustomSlotSupplier > ( ) ;
@@ -37,36 +37,36 @@ internal unsafe CustomSlotSupplier(
3737 try_reserve = FunctionPointer < Interop . CustomTryReserveSlotCallback > ( TryReserve ) ,
3838 mark_used = FunctionPointer < Interop . CustomMarkSlotUsedCallback > ( MarkUsed ) ,
3939 release = FunctionPointer < Interop . CustomReleaseSlotCallback > ( Release ) ,
40+ free = FunctionPointer < Interop . CustomSlotImplFreeCallback > ( Free ) ,
4041 } ;
4142
4243 PinCallbackHolder ( interopCallbacks ) ;
4344 }
4445
45- private static void SetCancelTokenOnCtx ( ref SlotReserveCtx ctx , CancellationTokenSource cancelTokenSrc )
46+ private static Temporalio . Worker . Tuning . SlotInfo SlotInfoFromBridge ( Interop . SlotInfo slotInfo )
4647 {
47- unsafe
48+ return slotInfo . tag switch
4849 {
49- try
50- {
51- var handle = GCHandle . Alloc ( cancelTokenSrc ) ;
52- fixed ( Interop . SlotReserveCtx * p = & ctx )
53- {
54- Interop . Methods . set_reserve_cancel_target ( p , GCHandle . ToIntPtr ( handle ) . ToPointer ( ) ) ;
55- }
56- }
57- catch ( Exception e )
58- {
59- Console . WriteLine ( $ "Error setting cancel token on ctx: { e } ") ;
60- throw ;
61- }
62- }
50+ Interop . SlotInfo_Tag . WorkflowSlotInfo =>
51+ new Temporalio . Worker . Tuning . SlotInfo . WorkflowSlotInfo (
52+ ByteArrayRef . ToUtf8 ( slotInfo . workflow_slot_info . workflow_type ) , slotInfo . workflow_slot_info . is_sticky != 0 ) ,
53+ Interop . SlotInfo_Tag . ActivitySlotInfo =>
54+ new Temporalio . Worker . Tuning . SlotInfo . ActivitySlotInfo (
55+ ByteArrayRef . ToUtf8 ( slotInfo . activity_slot_info . activity_type ) ) ,
56+ Interop . SlotInfo_Tag . LocalActivitySlotInfo =>
57+ new Temporalio . Worker . Tuning . SlotInfo . LocalActivitySlotInfo (
58+ ByteArrayRef . ToUtf8 ( slotInfo . local_activity_slot_info . activity_type ) ) ,
59+ _ => throw new System . ArgumentOutOfRangeException ( nameof ( slotInfo ) ) ,
60+ } ;
6361 }
6462
65- private unsafe void Reserve ( Interop . SlotReserveCtx ctx , void * sender )
63+ private unsafe void Reserve ( Interop . SlotReserveCtx * ctx , void * sender )
6664 {
67- SafeReserve ( ctx , new IntPtr ( sender ) ) ;
65+ SafeReserve ( new IntPtr ( ctx ) , new IntPtr ( sender ) ) ;
6866 }
6967
68+ // Note that this is always called by Rust, either because the call is cancelled or because
69+ // it completed. Therefore the GCHandle is always freed.
7070 private unsafe void CancelReserve ( void * tokenSrc )
7171 {
7272 var handle = GCHandle . FromIntPtr ( new IntPtr ( tokenSrc ) ) ;
@@ -75,49 +75,66 @@ private unsafe void CancelReserve(void* tokenSrc)
7575 handle . Free ( ) ;
7676 }
7777
78- private void SafeReserve ( Interop . SlotReserveCtx ctx , IntPtr sender )
78+ private void SafeReserve ( IntPtr ctx , IntPtr sender )
7979 {
80- var reserveTask = Task . Run ( async ( ) =>
80+ _ = Task . Run ( async ( ) =>
8181 {
82- var cancelTokenSrc = new System . Threading . CancellationTokenSource ( ) ;
83- SetCancelTokenOnCtx ( ref ctx , cancelTokenSrc ) ;
84- while ( true )
82+ using ( var cancelTokenSrc = new System . Threading . CancellationTokenSource ( ) )
8583 {
86- try
84+ unsafe
8785 {
88- var permit = await userSupplier . ReserveSlotAsync (
89- new ( ctx ) , cancelTokenSrc . Token ) . ConfigureAwait ( false ) ;
90- var usedPermitId = AddPermitToMap ( permit ) ;
91- unsafe
92- {
93- Interop . Methods . complete_async_reserve ( sender . ToPointer ( ) , new ( usedPermitId ) ) ;
94- }
95- cancelTokenSrc . Dispose ( ) ;
96- return ;
86+ var srcHandle = GCHandle . Alloc ( cancelTokenSrc ) ;
87+ Interop . Methods . set_reserve_cancel_target (
88+ ( Interop . SlotReserveCtx * ) ctx . ToPointer ( ) ,
89+ GCHandle . ToIntPtr ( srcHandle ) . ToPointer ( ) ) ;
9790 }
98- catch ( OperationCanceledException )
91+ while ( true )
9992 {
100- cancelTokenSrc . Dispose ( ) ;
101- return ;
102- }
93+ try
94+ {
95+ ConfiguredTaskAwaitable < Temporalio . Worker . Tuning . SlotPermit > reserveTask ;
96+ unsafe
97+ {
98+ reserveTask = userSupplier . ReserveSlotAsync (
99+ ReserveCtxFromBridge ( ( Interop . SlotReserveCtx * ) ctx . ToPointer ( ) ) ,
100+ cancelTokenSrc . Token ) . ConfigureAwait ( false ) ;
101+ }
102+ var permit = await reserveTask ;
103+ unsafe
104+ {
105+ var usedPermitId = AddPermitToMap ( permit ) ;
106+ Interop . Methods . complete_async_reserve ( sender . ToPointer ( ) , new ( usedPermitId ) ) ;
107+ }
108+ return ;
109+ }
110+ catch ( OperationCanceledException ) when ( cancelTokenSrc . Token . IsCancellationRequested )
111+ {
112+ unsafe
113+ {
114+ // Always call this to ensure the sender is freed
115+ Interop . Methods . complete_async_reserve ( sender . ToPointer ( ) , new ( 0 ) ) ;
116+ }
117+ return ;
118+ }
103119#pragma warning disable CA1031 // We are ok catching all exceptions here
104- catch ( Exception e )
105- {
120+ catch ( Exception e )
121+ {
106122#pragma warning restore CA1031
107- logger . LogError ( e , "Error reserving slot" ) ;
123+ logger . LogError ( e , "Error reserving slot" ) ;
124+ }
125+ // Wait for a bit to avoid spamming errors
126+ await Task . Delay ( 1000 , cancelTokenSrc . Token ) . ConfigureAwait ( false ) ;
108127 }
109- // Wait for a bit to avoid spamming errors
110- await Task . Delay ( 1000 , cancelTokenSrc . Token ) . ConfigureAwait ( false ) ;
111128 }
112129 } ) ;
113130 }
114131
115- private unsafe UIntPtr TryReserve ( Interop . SlotReserveCtx ctx )
132+ private unsafe UIntPtr TryReserve ( Interop . SlotReserveCtx * ctx )
116133 {
117- Temporalio . Worker . Tuning . ISlotPermit ? maybePermit ;
134+ Temporalio . Worker . Tuning . SlotPermit ? maybePermit ;
118135 try
119136 {
120- maybePermit = userSupplier . TryReserveSlot ( new ( ctx ) ) ;
137+ maybePermit = userSupplier . TryReserveSlot ( ReserveCtxFromBridge ( ctx ) ) ;
121138 }
122139#pragma warning disable CA1031 // We are ok catching all exceptions here
123140 catch ( Exception e )
@@ -135,11 +152,16 @@ private unsafe UIntPtr TryReserve(Interop.SlotReserveCtx ctx)
135152 return new ( usedPermitId ) ;
136153 }
137154
138- private void MarkUsed ( Interop . SlotMarkUsedCtx ctx )
155+ private unsafe void MarkUsed ( Interop . SlotMarkUsedCtx * ctx )
139156 {
140157 try
141158 {
142- userSupplier . MarkSlotUsed ( new ( ctx , permits [ ctx . slot_permit . ToUInt32 ( ) ] ) ) ;
159+ Temporalio . Worker . Tuning . SlotPermit permit ;
160+ lock ( permits )
161+ {
162+ permit = permits [ ( * ctx ) . slot_permit . ToUInt32 ( ) ] ;
163+ }
164+ userSupplier . MarkSlotUsed ( MarkUsedCtxFromBridge ( ctx , permit ) ) ;
143165 }
144166#pragma warning disable CA1031 // We are ok catching all exceptions here
145167 catch ( Exception e )
@@ -149,23 +171,34 @@ private void MarkUsed(Interop.SlotMarkUsedCtx ctx)
149171 }
150172 }
151173
152- private void Release ( Interop . SlotReleaseCtx ctx )
174+ private unsafe void Release ( Interop . SlotReleaseCtx * ctx )
153175 {
154- var permitId = ctx . slot_permit . ToUInt32 ( ) ;
176+ var permitId = ( * ctx ) . slot_permit . ToUInt32 ( ) ;
177+ Temporalio . Worker . Tuning . SlotPermit permit ;
178+ lock ( permits )
179+ {
180+ permit = permits [ permitId ] ;
181+ }
155182 try
156183 {
157- userSupplier . ReleaseSlot ( new ( ctx , permits [ permitId ] ) ) ;
184+ userSupplier . ReleaseSlot ( ReleaseCtxFromBridge ( ctx , permit ) ) ;
158185 }
159186#pragma warning disable CA1031 // We are ok catching all exceptions here
160187 catch ( Exception e )
161188 {
162189#pragma warning restore CA1031
163190 logger . LogError ( e , "Error releasing slot" ) ;
164191 }
165- permits . Remove ( permitId ) ;
192+ finally
193+ {
194+ lock ( permits )
195+ {
196+ permits . Remove ( permitId ) ;
197+ }
198+ }
166199 }
167200
168- private uint AddPermitToMap ( Temporalio . Worker . Tuning . ISlotPermit permit )
201+ private uint AddPermitToMap ( Temporalio . Worker . Tuning . SlotPermit permit )
169202 {
170203 lock ( permits )
171204 {
@@ -175,5 +208,39 @@ private uint AddPermitToMap(Temporalio.Worker.Tuning.ISlotPermit permit)
175208 return usedPermitId ;
176209 }
177210 }
211+
212+ private unsafe Temporalio . Worker . Tuning . SlotReserveContext ReserveCtxFromBridge ( Interop . SlotReserveCtx * ctx )
213+ {
214+ return new (
215+ SlotType : ( * ctx ) . slot_type switch
216+ {
217+ Interop . SlotKindType . WorkflowSlotKindType => Temporalio . Worker . Tuning . SlotType . Workflow ,
218+ Interop . SlotKindType . ActivitySlotKindType => Temporalio . Worker . Tuning . SlotType . Activity ,
219+ Interop . SlotKindType . LocalActivitySlotKindType => Temporalio . Worker . Tuning . SlotType . LocalActivity ,
220+ _ => throw new System . ArgumentOutOfRangeException ( nameof ( ctx ) ) ,
221+ } ,
222+ TaskQueue : ByteArrayRef . ToUtf8 ( ( * ctx ) . task_queue ) ,
223+ WorkerIdentity : ByteArrayRef . ToUtf8 ( ( * ctx ) . worker_identity ) ,
224+ WorkerBuildId : ByteArrayRef . ToUtf8 ( ( * ctx ) . worker_build_id ) ,
225+ IsSticky : ( * ctx ) . is_sticky != 0 ) ;
226+ }
227+
228+ private unsafe Temporalio . Worker . Tuning . SlotReleaseContext ReleaseCtxFromBridge (
229+ Interop . SlotReleaseCtx * ctx ,
230+ Temporalio . Worker . Tuning . SlotPermit permit )
231+ {
232+ return new (
233+ SlotInfo : ( * ctx ) . slot_info is null ? null : SlotInfoFromBridge ( * ( * ctx ) . slot_info ) ,
234+ Permit : permit ) ;
235+ }
236+
237+ private unsafe Temporalio . Worker . Tuning . SlotMarkUsedContext MarkUsedCtxFromBridge (
238+ Interop . SlotMarkUsedCtx * ctx ,
239+ Temporalio . Worker . Tuning . SlotPermit permit )
240+ {
241+ return new (
242+ SlotInfo : SlotInfoFromBridge ( ( * ctx ) . slot_info ) ,
243+ Permit : permit ) ;
244+ }
178245 }
179246}
0 commit comments