19
19
20
20
import com .rabbitmq .client .amqp .AmqpException ;
21
21
import java .time .Duration ;
22
+ import java .util .HashMap ;
23
+ import java .util .Map ;
22
24
import java .util .concurrent .*;
23
25
import java .util .concurrent .atomic .AtomicBoolean ;
26
+ import java .util .concurrent .atomic .AtomicLong ;
24
27
import java .util .concurrent .atomic .AtomicReference ;
25
28
import java .util .function .Consumer ;
26
29
import java .util .function .Supplier ;
30
+ import java .util .function .UnaryOperator ;
27
31
import org .slf4j .Logger ;
28
32
import org .slf4j .LoggerFactory ;
29
33
30
- final class EventLoop < S > implements AutoCloseable {
34
+ final class EventLoop implements AutoCloseable {
31
35
32
36
private static final Duration TIMEOUT = Duration .ofSeconds (60 );
33
37
private static final Logger LOGGER = LoggerFactory .getLogger (EventLoop .class );
34
38
35
39
private final AtomicBoolean closed = new AtomicBoolean (false );
36
- private final String label ;
37
40
private final Future <?> loop ;
38
41
private final AtomicReference <Thread > loopThread = new AtomicReference <>();
39
- private final AtomicReference <S > stateReference = new AtomicReference <>();
40
- private final BlockingQueue <Consumer <S >> taskQueue = new ArrayBlockingQueue <>(100 );
42
+ private final BlockingQueue <ClientTaskContext <Object >> taskQueue = new ArrayBlockingQueue <>(100 );
41
43
42
- EventLoop (Supplier <S > stateSupplier , String label , ExecutorService executorService ) {
43
- this .label = label ;
44
+ EventLoop (ExecutorService executorService ) {
44
45
CountDownLatch loopThreadSetLatch = new CountDownLatch (1 );
45
46
this .loop =
46
47
executorService .submit (
47
48
() -> {
48
- S state = stateSupplier .get ();
49
49
loopThread .set (Thread .currentThread ());
50
- stateReference .set (state );
51
50
loopThreadSetLatch .countDown ();
51
+ Map <Long , Object > states = new HashMap <>();
52
52
while (!Thread .currentThread ().isInterrupted ()) {
53
53
try {
54
- Consumer <S > task = this .taskQueue .take ();
55
- task .accept (state );
54
+ ClientTaskContext <Object > context = this .taskQueue .take ();
55
+ Object state = states .get (context .client .id );
56
+ state = context .task .apply (state );
57
+ if (state == null ) {
58
+ states .remove (context .client .id );
59
+ } else {
60
+ states .put (context .client .id , state );
61
+ }
56
62
} catch (InterruptedException e ) {
57
63
return ;
58
64
} catch (Exception e ) {
@@ -70,34 +76,49 @@ final class EventLoop<S> implements AutoCloseable {
70
76
}
71
77
}
72
78
73
- void submit (Consumer <S > task ) {
79
+ <S > Client <S > register (Supplier <S > stateSupplier ) {
80
+ Client <S > client = new Client <>(this );
81
+ this .submit (
82
+ client ,
83
+ nullState -> {
84
+ S state = stateSupplier .get ();
85
+ client .stateReference .set (state );
86
+ return state ;
87
+ });
88
+ return client ;
89
+ }
90
+
91
+ private <ST > void submit (Client <ST > client , UnaryOperator <ST > task ) {
74
92
if (this .closed .get ()) {
75
93
throw new IllegalStateException ("Event loop is closed" );
76
94
} else {
77
95
if (Thread .currentThread ().equals (this .loopThread .get ())) {
78
- task .accept ( this .stateReference .get ());
96
+ task .apply ( client .stateReference .get ());
79
97
} else {
80
98
CountDownLatch latch = new CountDownLatch (1 );
81
99
try {
82
- boolean added =
83
- this .taskQueue .offer (
100
+ ClientTaskContext <ST > context =
101
+ new ClientTaskContext <>(
102
+ client ,
84
103
state -> {
85
104
try {
86
- task .accept (state );
105
+ return task .apply (state );
87
106
} catch (Exception e ) {
88
- LOGGER .info ("Error during {} task" , this . label , e );
107
+ LOGGER .info ("Error during task" , e );
89
108
} finally {
90
109
latch .countDown ();
91
110
}
92
- },
93
- TIMEOUT .toMillis (),
94
- TimeUnit .MILLISECONDS );
111
+ return null ;
112
+ });
113
+ boolean added =
114
+ this .taskQueue .offer (
115
+ (ClientTaskContext <Object >) context , TIMEOUT .toMillis (), TimeUnit .MILLISECONDS );
95
116
if (!added ) {
96
- throw new AmqpException ("Enqueueing of %s task timed out" , this . label );
117
+ throw new AmqpException ("Enqueueing of task timed out" );
97
118
}
98
119
} catch (InterruptedException e ) {
99
120
Thread .currentThread ().interrupt ();
100
- throw new AmqpException (this . label + " task enqueueing has been interrupted" , e );
121
+ throw new AmqpException ("Task enqueueing has been interrupted" , e );
101
122
}
102
123
try {
103
124
boolean completed = latch .await (TIMEOUT .toMillis (), TimeUnit .MILLISECONDS );
@@ -106,20 +127,66 @@ void submit(Consumer<S> task) {
106
127
}
107
128
} catch (InterruptedException e ) {
108
129
Thread .currentThread ().interrupt ();
109
- throw new AmqpException (this . label + " Topology task processing has been interrupted" , e );
130
+ throw new AmqpException (" Topology task processing has been interrupted" , e );
110
131
}
111
132
}
112
133
}
113
134
}
114
135
115
- S state () {
116
- return this .stateReference .get ();
117
- }
118
-
119
136
@ Override
120
137
public void close () {
121
138
if (this .closed .compareAndSet (false , true )) {
122
139
this .loop .cancel (true );
123
140
}
124
141
}
142
+
143
+ private static final AtomicLong CLIENT_ID_SEQUENCE = new AtomicLong ();
144
+
145
+ static class Client <S > implements AutoCloseable {
146
+
147
+ private final long id ;
148
+ private final AtomicReference <S > stateReference = new AtomicReference <>();
149
+ private final EventLoop loop ;
150
+ private final AtomicBoolean closed = new AtomicBoolean (false );
151
+
152
+ private Client (EventLoop loop ) {
153
+ this .id = CLIENT_ID_SEQUENCE .getAndIncrement ();
154
+ this .loop = loop ;
155
+ }
156
+
157
+ void submit (Consumer <S > task ) {
158
+ if (this .closed .get ()) {
159
+ throw new IllegalStateException ("Event loop is closed" );
160
+ } else {
161
+ this .loop .submit (
162
+ this ,
163
+ s -> {
164
+ task .accept (s );
165
+ return s ;
166
+ });
167
+ }
168
+ }
169
+
170
+ @ Override
171
+ public void close () {
172
+ if (this .closed .compareAndSet (false , true )) {
173
+ this .loop .submit (this , s -> null );
174
+ }
175
+ }
176
+
177
+ S state () {
178
+ return this .stateReference .get ();
179
+ }
180
+ }
181
+
182
+ private static class ClientTaskContext <S > {
183
+
184
+ private final Client <S > client ;
185
+ private final UnaryOperator <S > task ;
186
+
187
+ private ClientTaskContext (Client <S > client , UnaryOperator <S > task ) {
188
+ this .client = client ;
189
+ this .task = task ;
190
+ }
191
+ }
125
192
}
0 commit comments