Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reentrant caching? #56

Open
ghost opened this issue Jun 5, 2020 · 3 comments
Open

Reentrant caching? #56

ghost opened this issue Jun 5, 2020 · 3 comments

Comments

@ghost
Copy link

ghost commented Jun 5, 2020

Is JPL supposed to be multi-threaded capabel, i.e. reentrant? Because this code doesn’t work as a cache: There might be race conditions where two or more jpl_iref_type_cache/2 are asserted. A reentrant cache is a little bit more difficult to implement and needs a mutex.

jpl_object_to_type(Ref, Type) :-
    jpl_is_object(Ref),
    (   jpl_iref_type_cache(Ref, T)
    ->  true                                % T is Tag's type
    ;   jpl_object_to_class(Ref, Cobj),     % else get ref to class obj
        jpl_class_to_type(Cobj, T),         % get type of class it denotes
        jpl_assert(jpl_iref_type_cache(Ref,T))
    ),
    Type = T.

One way to do it, is as follows. But it has the drawback that time is spent inside the mutx by ‘jpl_object_to_class/2’ etc… Using destructive operations which would be also seen through facts, this can be even done outside of the mutex, and would be faster. So this is only an imperfect sketch:

jpl_object_to_type(Ref, Type) :-
    jpl_is_object(Ref),
    (   jpl_iref_type_cache(Ref, T)
    ->  true                                % T is Tag's type
    ;   with_mutex(jpl_iref_type_cache_mutex, 
               (jpl_iref_type_cache(Ref, T)        % need to do check again
               ->  true;
               jpl_object_to_class(Ref, Cobj),     % else get ref to class obj
               jpl_class_to_type(Cobj, T),         % get type of class it denotes
               jpl_assert(jpl_iref_type_cache(Ref,T)))
        )
    ),
    Type = T.

Maybe alternatively an elegant approach could be using SWI-Prolog shared tabling.

@anionic
Copy link
Contributor

anionic commented Jun 6, 2020

JPL now aims to support multi threads (with much help from Jan), although it didn't originally, and I didn't review the (various) caching :-/

Couldn't we mutex just the jpl_assert? or am I missing something.

Also, any caching was speculative, and not demonstrably compute-saving...

@ghost
Copy link
Author

ghost commented Jun 6, 2020

Yeah, I guess this would be also a form of caching:


jpl_object_to_type(Ref, Type) :-
    jpl_is_object(Ref),
    (   jpl_iref_type_cache(Ref, T)
    ->  true                                % T is Tag's type
    ;   jpl_object_to_class(Ref, Cobj),     % else get ref to class obj
        jpl_class_to_type(Cobj, T),         % get type of class it denotes
        with_mutex(jpl_iref_type_cache_mutex, 
                  (retractall(jpl_iref_type_cache(Ref,_)),
                  assertz(jpl_iref_type_cache(Ref,T)))
        )
    ),
    Type = T.

In the above a race condition might appear that two or more
threads calculate T, but in the end there will be only one entry.
I am using retractall/1 to remove a previous entry. You

could also use a if-then-else check instead, i.e. a conditional
asssertz/1. As long as there is a mutex, the thread will have
the critical section exclusively, and can determine whether it

needs to add a cache entry or not. Problem is how to box it
into a meta call jpl_assert/1 or better jpl_update/1. You need
to know what are the primary key arguments and what are the

data arguments of the item you want to update.

@JanWielemaker
Copy link
Member

This issue has been mentioned on SWI-Prolog. There might be relevant details there:

https://swi-prolog.discourse.group/t/global-variables-and-threads/2519/5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants