Skip to content

NestedQueryError("The last query was not closed") for multiple query operations. #126

@ExplosionArt

Description

@ExplosionArt

With reference to issue #120 (#120), The following solution mentioned in issue #3 was implemented for it.

pyswip doesn't support multithreading at all. IMHO, contemporary API (or at least pyswip.prolog module) needs to be redesigned from the scratch, see: http://www.swi-prolog.org/pldoc/man?section=foreignthread

I've devised an ad-hoc non-intrusive solution, just to test things out:

import pyswip, ctypes

class PrologMT(pyswip.Prolog):
    """Multi-threaded (one-to-one) pyswip.Prolog ad-hoc reimpl"""
    _swipl = pyswip.core._lib

    PL_thread_self = _swipl.PL_thread_self
    PL_thread_self.restype = ctypes.c_int

    PL_thread_attach_engine = _swipl.PL_thread_attach_engine
    PL_thread_attach_engine.argtypes = [ctypes.c_void_p]
    PL_thread_attach_engine.restype = ctypes.c_int

    @classmethod
    def _init_prolog_thread(cls):
        pengine_id = cls.PL_thread_self()
        if (pengine_id == -1):
            pengine_id = cls.PL_thread_attach_engine(None)
            print("{INFO} attach pengine to thread: %d" % pengine_id)
        if (pengine_id == -1):
            raise pyswip.prolog.PrologError("Unable to attach new Prolog engine to the thread")
        elif (pengine_id == -2):
            print("{WARN} Single-threaded swipl build, beware!")

    class _QueryWrapper(pyswip.Prolog._QueryWrapper):
        def __call__(self, *args, **kwargs):
            PrologMT._init_prolog_thread()
            return super().__call__(*args, **kwargs)

PrologMT is a drop-in replacement for pyswip.Prolog in your code. On each query, native thread's local storage is scanned for the prolog engine (PL_thread_self). If there is no pengine associated with the current thread, new one will be created (PL_thread_attach_engine), without any further resource management.
You couldn't use any term_t-related functions of libswipl from the thread with no pengine - that leads to SEGFAULT. By default, the thread which imports pyswip first, will be implicitly associated with the single ("main") pengine, as PL_initialise is called on the pyswip.prolog module top-level.

Originally posted by @xpinguin in #3 (comment)

Multithreading was possible after the suggested solution. However, I receive a NestedQueryError("The last query was not closed") exception whenever multiple threads try to perform the "query" operation. So is there any inherent "Queue" mechanism involved for "query" operations to avoid the above error? If not, kindly suggest some other solution for the same.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions