PLCJ-0.10.0 Concurrency problem : RequestTransactionManager workLog.remove()
cptjackwu opened this issue · comments
What happened?
private void processWorklog() {
while (runningRequests.size() < getNumberOfConcurrentRequests() && !workLog.isEmpty()) {
RequestTransaction next = workLog.remove();
this.runningRequests.add(next);
Future<?> completionFuture = executor.submit(next.operation);
next.setCompletionFuture(completionFuture);
}
}
RequestTransactionManager workLog.remove()
When I write a connection to multiple points at the same time, each point is written once a second, there is a concurrency problem, this time the content in the worklog is empty, and the error is reported when remove
===============================================================
static Map<String, Boolean> map = new HashMap<>();
static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * 2);
public static void main(String[] args) throws Exception{
PlcClient plcClient = new S7Client("s7://**.**.*.***");
plcClient.connect();
Map m1 = new HashMap();
m1.put("stationCode","OP010");
m1.put("address","%DB1150:0.0:BOOL");
Map m2 = new HashMap();
m2.put("stationCode","OP020");
m2.put("address","%DB1100:0.0:BOOL");
Map m3 = new HashMap();
m3.put("stationCode","OP030");
m3.put("address","%DB1200:0.0:BOOL");
List<Map> mapList = Stream.of(m1,m2,m3).collect(Collectors.toList());
mapList.forEach(v->
executorService.scheduleAtFixedRate(()-> {
boolean value = map.get(v.get("stationCode")) == null ? true : map.get(v.get("stationCode"));
plcClient.writeBOOL(new NodeId((String) v.get("address"), value));
map.put((String) v.get("stationCode"), !value);
},0, 1000, TimeUnit.MILLISECONDS)
);
====================================================================================
The error content is as follows:
2023-11-03 00:20:25.432 WARN 7 --- [ nioEventLoopGroup-9-1] io.netty.channel.DefaultChannelPipeline : An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: java.util.NoSuchElementException
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:98)
at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
at io.netty.handler.codec.ByteToMessageCodec.channelRead(ByteToMessageCodec.java:103)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.util.NoSuchElementException: null
at java.util.AbstractQueue.remove(AbstractQueue.java:117)
at org.apache.plc4x.java.spi.transaction.RequestTransactionManager.processWorklog(RequestTransactionManager.java:104)
at org.apache.plc4x.java.spi.transaction.RequestTransactionManager.endRequest(RequestTransactionManager.java:133)
at org.apache.plc4x.java.spi.transaction.RequestTransactionManager.access$200(RequestTransactionManager.java:46)
at org.apache.plc4x.java.spi.transaction.RequestTransactionManager$RequestTransaction.endRequest(RequestTransactionManager.java:160)
at org.apache.plc4x.java.s7.readwrite.protocol.S7ProtocolLogic.lambda$readInternal$18(S7ProtocolLogic.java:272)
at org.apache.plc4x.java.spi.Plc4xNettyWrapper.decode(Plc4xNettyWrapper.java:187)
at io.netty.handler.codec.MessageToMessageCodec$2.decode(MessageToMessageCodec.java:81)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:88)
... 23 common frames omitted
So, How do I fix that concurrent problem
Version
v0.10.0
Programming Languages
- plc4j
- plc4go
- plc4c
- plc4net
Protocols
- AB-Ethernet
- ADS /AMS
- BACnet/IP
- CANopen
- DeltaV
- DF1
- EtherNet/IP
- Firmata
- KNXnet/IP
- Modbus
- OPC-UA
- S7
Admittedly I habe absolutely no idea what you are doing ... it looks as if you're trying to write something to a number of S7 devices. Well we definitely don't support that type of operation in PLC4X ... if you've implemented something fancy inside S7Client, I can't tell you what's going wrong without the full code.
I have changed workLog.remove(); to workLog.poll(), and then judged it and solved the problem。
private void processWorklog() {
while (runningRequests.size() < getNumberOfConcurrentRequests() && !workLog.isEmpty()) {
RequestTransaction next = workLog.poll();
if(next!-null){
this.runningRequests.add(next);
Future<?> completionFuture = executor.submit(next.operation);
next.setCompletionFuture(completionFuture);
}
}
}
the worklog processing changes were integrated at some point of time. If you are running 0.10 (which is over year old) you might need to backport fixes from 0.11 release.