Closed georgweiss closed 1 month ago
I can't duplicate that exact error. This runs for me without issues on the client side ...
/** Use with for example this database:
# `softIocPVA -d demo.db`
# where demo.db contains this:
record(ai, "demo")
{
}
*
*/
public class PVWriteDemo
{
public static void main(String[] args) throws Exception
{
PV pv = PVPool.getPV("pva://demo");
pv.onValueEvent().subscribe(value ->
{
System.out.println("Got " + value);
if (!PV.isDisconnected(value))
{
double val = Math.random() * 100;
pv.asyncWrite(val);
}
});
while (true)
{
System.out.println("Check...");
Thread.sleep(1000);
}
}
}
... but there's a different problem: The code is basically an infinite loop. PV connects, receives first value. Code writes a number, which triggers another value update, code writes another value, which again triggers a value update, and in my case the IOC(!) then crashes. If you always write the same number "42", the result depends on the MDEL setting of the record, assuming you talk to a basic IOC.
Note that the PV API makes no promises regarding the caching and threading of the underlying implementation (caj, pva, jackie, mqtt, ...). Is the value
that you receive a safe copy of the data, so you can hold on to a reference? Or is the value
a zero-copy reference to data in some network buffer, which would be faster and more memory efficient, but now you need to copy out what you want to keep, because it becomes invalid as soon as you leave the subscribe
consumer?
As for threading, in your stack trace we can see that we're basically called right from TCPHandler.receiver
, that is, with minimal latency right when we receive the update over TCP. But that also means you should probably not start another network operation like PV.write
or PV.asyncWrite
from within there, because the PV implementation could lock. The original JNI JCA implementation was notorious for that; when you called another API method from within a callback, it would lock up.
So if you simply want to write one value (connect, write, close), try something like this, which works for me with both "ca://" and "pva://"
public static void main(String[] args) throws Exception
{
PV pv = PVPool.getPV("pva://demo");
try
{
// Wait until we're connected
CountDownLatch connected = new CountDownLatch(1);
pv.onValueEvent().subscribe(value ->
{
System.out.println("Got " + value);
if (!PV.isDisconnected(value))
connected.countDown();
});
connected.await(2, TimeUnit.SECONDS);
// Write a value, wait for that to complete (if supported)
double val = Math.random() * 100;
System.out.println("Writing " + val);
pv.asyncWrite(val).get(2, TimeUnit.SECONDS);
}
catch (Exception ex)
{
ex.printStackTrace();
}
// Done
PVPool.releasePV(pv);
}
Scratch the "no promises regarding the caching". That's true on the lower CAJ or core-pva level, but the PV API deals with VType
s, and we do provide a copy in PV.read
or PV.onValueEvent
. Still, the threading applies. The subscribe consumer may be called directly from within the thread that reads from the network, so you better avoid other network calls from within there.
I've taken precautions to avoid the infinite loop, no worries. My snippet is just the essential elements for this discussion. However, using pv.asyncWrite
does not present an issue in my code, while pv.write
does as described.
In any case, I've moved to call to pv.write
till after the await
, and that resolves the issue.
Thanks @kasemir
.. pv.write ... after the await
If this is for something like save/restore, you might actually want to wait for all affected PVs to first connect (send the first update) and only then write the values, so you restore all or nothing. Or prompt "Only N out of M PVs are available. Restore only those, or abort?"
So I have something like:
The idea is to connect, write a value and then return the PV to the pool. I notice that with pva, the first call will write the value but also throw a TimeoutException (stack trace below). Subsequent calls do not throw a TimeoutException. Using pv.asyncWrite does not throw exception on first call, nor do I see the exception using ca.