1- /*
2- * Copyright 2025 JetLinks https://www.jetlinks.cn
3- *
4- * Licensed under the Apache License, Version 2.0 (the "License");
5- * you may not use this file except in compliance with the License.
6- * You may obtain a copy of the License at
7- *
8- * http://www.apache.org/licenses/LICENSE-2.0
9- *
10- * Unless required by applicable law or agreed to in writing, software
11- * distributed under the License is distributed on an "AS IS" BASIS,
12- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13- * See the License for the specific language governing permissions and
14- * limitations under the License.
15- */
161package org .jetlinks .community .lock ;
172
3+ import lombok .AllArgsConstructor ;
4+ import lombok .EqualsAndHashCode ;
5+ import lombok .Getter ;
6+ import lombok .extern .slf4j .Slf4j ;
187import org .reactivestreams .Publisher ;
198import org .reactivestreams .Subscription ;
209import reactor .core .CoreSubscriber ;
3423import java .util .concurrent .atomic .AtomicReferenceFieldUpdater ;
3524import java .util .function .Consumer ;
3625
26+ @ Slf4j
3727class DefaultReactiveLock implements ReactiveLock {
3828 @ SuppressWarnings ("all" )
3929 static final AtomicReferenceFieldUpdater <DefaultReactiveLock , LockingSubscriber >
@@ -42,8 +32,22 @@ class DefaultReactiveLock implements ReactiveLock {
4232
4333 final Deque <LockingSubscriber <?>> queue = new ConcurrentLinkedDeque <>();
4434
35+ protected final LockName lockName ;
36+
4537 volatile LockingSubscriber <?> pending ;
4638
39+ static final AtomicIntegerFieldUpdater <DefaultReactiveLock > WIP =
40+ AtomicIntegerFieldUpdater .newUpdater (DefaultReactiveLock .class , "wip" );
41+
42+ volatile int wip ;
43+
44+ DefaultReactiveLock (String lockName ) {
45+ this .lockName = new LockName (lockName );
46+ }
47+
48+ public boolean isEmpty () {
49+ return queue .isEmpty ();
50+ }
4751
4852 @ Override
4953 public <T > Flux <T > lock (Flux <T > job ) {
@@ -76,28 +80,34 @@ public <T> Mono<T> lock(Mono<T> mono, Duration timeout, Mono<? extends T> fallba
7680 }
7781
7882 protected void drain () {
79- if (PENDING . get (this ) != null ) {
83+ if (WIP . getAndIncrement (this ) != 0 ) {
8084 return ;
8185 }
8286
8387 LockingSubscriber <?> locking ;
8488
85- for (; ; ) {
86- locking = queue .pollFirst ();
87- if (locking == null ) {
88- return ;
89- }
90- if (locking .isDisposed ()) {
91- continue ;
92- }
93- if (PENDING .compareAndSet (this , null , locking )) {
94- //使用单独的线程池来调度,防止参与锁太多导致栈溢出.
95- Schedulers .parallel ().schedule (locking ::subscribe );
96- } else {
97- queue .addLast (locking );
89+ do {
90+ for (; ; ) {
91+ locking = queue .pollFirst ();
92+ if (locking == null ) {
93+ break ;
94+ }
95+ if (locking .isDisposed ()) {
96+ continue ;
97+ }
98+ if (PENDING .compareAndSet (this , null , locking )) {
99+ try {
100+ locking .subscribe ();
101+ } catch (Throwable e ) {
102+ PENDING .compareAndSet (this , locking , null );
103+ queue .addLast (locking );
104+ }
105+ } else {
106+ queue .addLast (locking );
107+ }
108+ break ;
98109 }
99- break ;
100- }
110+ } while (WIP .decrementAndGet (this ) != 0 );
101111
102112 }
103113
@@ -151,6 +161,12 @@ protected LockingFlux(DefaultReactiveLock main, Flux<? extends T> source, Durati
151161
152162 @ Override
153163 public void subscribe (@ Nonnull CoreSubscriber <? super T > actual ) {
164+ if (actual .currentContext ().hasKey (main .lockName )) {
165+ log .debug ("reactive lock {} already locked in current context, skip." , main .lockName );
166+ //如果当前上下文已经有锁了,则不再重复注册订阅者
167+ source .subscribe (actual );
168+ return ;
169+ }
154170 Consumer <CoreSubscriber <? super T >> subscribeCallback = source ::subscribe ;
155171 main .registerSubscriber (actual , subscribeCallback , timeout , timeoutFallback );
156172 }
@@ -183,14 +199,20 @@ protected LockingMono(DefaultReactiveLock main, Mono<? extends T> source, Durati
183199
184200 @ Override
185201 public void subscribe (@ Nonnull CoreSubscriber <? super T > actual ) {
202+ if (actual .currentContext ().hasKey (main .lockName )) {
203+ log .debug ("reactive lock {} already locked in current context, skip." , main .lockName );
204+ //如果当前上下文已经有锁了,则不再重复注册订阅者
205+ source .subscribe (actual );
206+ return ;
207+ }
186208 Consumer <CoreSubscriber <? super T >> subscribeCallback = source ::subscribe ;
187209 main .registerSubscriber (actual , subscribeCallback , timeout , fallback );
188210 }
189211
190212
191213 }
192214
193- static class LockingSubscriber <T > extends BaseSubscriber <T > {
215+ static class LockingSubscriber <T > extends BaseSubscriber <T > implements Runnable {
194216 protected final DefaultReactiveLock main ;
195217 protected final CoreSubscriber <? super T > actual ;
196218 private final Consumer <CoreSubscriber <? super T >> subscriber ;
@@ -201,6 +223,7 @@ static class LockingSubscriber<T> extends BaseSubscriber<T> {
201223 AtomicIntegerFieldUpdater .newUpdater (LockingSubscriber .class , "status" );
202224
203225 private volatile int status ;
226+ private final Context context ;
204227
205228 //初始
206229 private static final int INIT = 0 ;
@@ -223,6 +246,11 @@ public LockingSubscriber(DefaultReactiveLock main,
223246 this .main = main ;
224247 this .subscriber = subscriber ;
225248 this .timeoutFallback = timeoutFallback ;
249+ this .context = actual
250+ .currentContext ()
251+ .put (DefaultReactiveLock .class , main )
252+ .put (main .lockName , true );
253+
226254 if (timeout != null ) {
227255 this .timeoutTask = Schedulers
228256 .parallel ()
@@ -238,8 +266,12 @@ private void onTimeout() {
238266 if (timeoutFallback != null ) {
239267 timeoutFallback .subscribe (actual );
240268 } else {
241- this .onError (new TimeoutException ("Lock timed out" ));
269+ Operators .error (
270+ actual , new TimeoutException ("Lock [" + main .lockName + "] timeout" )
271+ );
242272 }
273+ } else {
274+ main .drain ();
243275 }
244276 }
245277
@@ -249,15 +281,21 @@ protected void subscribe() {
249281 timeoutTask .dispose ();
250282 }
251283 subscriber .accept (this );
284+ } else {
285+ main .drain ();
252286 }
287+
253288 }
254289
255290 protected void complete () {
256- if (statusUpdater .compareAndSet (this , INIT , UN_SUB ) || statusUpdater .compareAndSet (this , SUB_SOURCE , UN_SUB )) {
291+ if (statusUpdater .compareAndSet (this , INIT , UN_SUB )
292+ || statusUpdater .compareAndSet (this , SUB_SOURCE , UN_SUB )) {
257293 if (timeoutTask != null && !timeoutTask .isDisposed ()) {
258294 timeoutTask .dispose ();
259295 }
260296 doComplete ();
297+ } else {
298+ main .drain ();
261299 }
262300 }
263301
@@ -266,9 +304,8 @@ protected void doComplete() {
266304 if (!this .isDisposed ()) {
267305 this .cancel ();
268306 }
269- if (PENDING .compareAndSet (main , this , null )) {
270- main .drain ();
271- }
307+ PENDING .compareAndSet (main , this , null );
308+ main .drain ();
272309 }
273310
274311 @ Override
@@ -304,8 +341,25 @@ protected final void hookFinally(@Nonnull SignalType type) {
304341 @ Override
305342 @ Nonnull
306343 public Context currentContext () {
307- return actual .currentContext ();
344+ return context ;
345+ }
346+
347+ @ Override
348+ public void run () {
349+ subscribe ();
308350 }
309351 }
310352
353+
354+ @ Getter
355+ @ AllArgsConstructor
356+ @ EqualsAndHashCode
357+ protected static class LockName {
358+ final String name ;
359+
360+ @ Override
361+ public String toString () {
362+ return name ;
363+ }
364+ }
311365}
0 commit comments