epics-base / p4p

Python bindings for the PVAccess network client and server.
BSD 3-Clause "New" or "Revised" License
27 stars 38 forks source link

Exception on closing context #96

Open mattclarke opened 1 year ago

mattclarke commented 1 year ago

On version 4.1.5 when closing the context or stopping the script I get the following:

P4P atexit completes
AttributeError: '_ClientProvider' object has no attribute 'close'
Exception ignored in: 'p4p._p4p.ClientProvider.__dealloc__'
AttributeError: '_ClientProvider' object has no attribute 'close'

This is on a Mac. I don't see this on 4.1.2, but see it on all later versions.

mdavidsaver commented 1 year ago

What python version is involved? Which concurrency method? (thread, cothread, asyncio, Qt?)

This is probably another instance of GC loop breaking Having partially disassembled the object. The _ClientProvider extension type certainly does have a close() method, at least prior to GC.

I'm tempted to decorate all of my Cython extension types with @cython.no_gc_clear, although this would likely just push GC loop breaking to some other equally unexpected place.

mattclarke commented 1 year ago

Sorry I should have added the python version to the report. Unfortunately, the Mac I saw it on was replaced and I cannot reproduce it on my new one. Let's file it as "cannot reproduce" and if I see it again in the future I'll reopen this with an update report.

coretl commented 1 year ago

I've just stumbled upon this while reporting #115. Running a p4p server then a p4p asyncio client that raises a remote exception seems to trigger it.

Run the server:

# p4p_server.py
from p4p.nt import NTScalar
from p4p.server import Server
from p4p.server.thread import SharedPV

pv = SharedPV(nt=NTScalar('ai'), initial=[1,2,3])  # int32 array

@pv.put
def handle(pv, op):
    v = op.value()
    print(f"Updating to {v}")
    pv.post(v) # just store and update subscribers
    op.done()

Server.forever(providers=[{
    'demo:pv:name':pv, 
}])

Then run the client:

# p4p_client.py
from p4p.client.asyncio import Context
import numpy as np
import asyncio

pv = 'demo:pv:name'

async def f():
    with Context("pva", nt=False) as c:
        print(f"Initial: {(await c.get(pv)).value}")
        await c.put(pv, [4, 5, 6])
        print(f"List works: {(await c.get(pv)).value}")
        await c.put(pv, np.array([7, 8, 9]))
        print(f"Np.int64 array doesn't: {(await c.get(pv)).value}")

asyncio.run(f())

Server gives a remote exception, then client gives output:

Initial: [1 2 3]
List works: [4 5 6]
Exception in Put builder
Traceback (most recent call last):
  File "/venv/lib/python3.10/site-packages/p4p/client/raw.py", line 80, in builder
    nt.assign(V, value)
  File "/venv/lib/python3.10/site-packages/p4p/nt/__init__.py", line 84, in assign
    self._assign(V, value)
  File "/venv/lib/python3.10/site-packages/p4p/nt/__init__.py", line 100, in _default_assign
    V.value = value # assume NTScalar-like
  File "src/p4p/_p4p.pyx", line 223, in p4p._p4p._Value.__setattr__
TypeError: Cannot cast array data from dtype('int64') to dtype('int32') according to the rule 'safe'
Unhandled Exception src/pvxs_client.cpp:67
Traceback (most recent call last):
  File "/venv/lib/python3.10/site-packages/p4p/client/raw.py", line 80, in builder
    nt.assign(V, value)
  File "/venv/lib/python3.10/site-packages/p4p/nt/__init__.py", line 84, in assign
    self._assign(V, value)
  File "/venv/lib/python3.10/site-packages/p4p/nt/__init__.py", line 100, in _default_assign
    V.value = value # assume NTScalar-like
  File "src/p4p/_p4p.pyx", line 223, in p4p._p4p._Value.__setattr__
TypeError: Cannot cast array data from dtype('int64') to dtype('int32') according to the rule 'safe'
Traceback (most recent call last):
  File "/dls/science/users/tmc43/repos/.devcontainer/./p4p_client_asyncio.py", line 15, in <module>
    asyncio.run(f())
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "/dls/science/users/tmc43/repos/.devcontainer/./p4p_client_asyncio.py", line 12, in f
    await c.put(pv, np.array([7, 8, 9]))
  File "/venv/lib/python3.10/site-packages/p4p/client/asyncio.py", line 207, in put
    return (await self._put_one(name, values, request=request, get=get))
  File "/venv/lib/python3.10/site-packages/p4p/client/asyncio.py", line 236, in _put_one
    value = await F
p4p._p4p.RemoteError: Unable to unwrap Value(id:epics:nt/NTScalarArray:1.0, None) with <bound method NTScalar.unwrap of <class 'p4p.nt.scalar.NTScalar'>>
Traceback (most recent call last):
  File "src/p4p/_p4p.pyx", line 634, in p4p._p4p.ClientProvider.close
AttributeError: '_ClientProvider' object has no attribute 'close'
Exception ignored in: 'p4p._p4p.ClientProvider.__dealloc__'
Traceback (most recent call last):
  File "src/p4p/_p4p.pyx", line 634, in p4p._p4p.ClientProvider.close
AttributeError: '_ClientProvider' object has no attribute 'close'

Running on Linux with python 3.10.6, p4p 4.1.9, pvxslibs 1.2.2, epicscorelibs 7.0.7.99.0.2

mdavidsaver commented 1 year ago

As it happens, I managed to trigger this issue today and have made another attempt at fixing with 546b9194151b68c98871304363cab8b4ac8d383a