epics-base/p4p

Exception on closing context

Opened this issue · 4 comments

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.

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.

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

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

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