Implementing Java -> platform callbacks in C++.

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Implementing Java -> platform callbacks in C++.

Vladimir Ozerov
Igniters,

Lots of our features rely on callbacks from Ignite to user code. This is
essential for task execution, cache invokes, cache store, continuous
queries, messaging, etc..

Ideally from user perspective target class should look somewhat like this:
class MyListener : public IListener<MyKey*, MyVal*> {
public:
    bool Invoke(MyKey*, MyType*);
}

And Java -> C++ linking code will be something like this:
jboolean JniListenerCallback(JNIEnv *env, jclass cls, jlong ptr, _others_) {
    int callbackType = type(_others_);

    switch (callbackType) {
    ...
    case 6:
        MyKey* key = unmarshal<MyKey*>(_others_);
        MyVal* val = unmarshal<MyVal*>(_others_);
        return reinterpret_cast<MyListener>(ptr).Invoke(_others_);
    case 7:
        MyOtherKey* key = unmarshal<MyOtherKey*>(_others_);
        MyOtherVal* val = unmarshal<MyOtherVal*>(_others_);
        return reinterpret_cast<MyOtherListener>(ptr).Invoke(_other_);
    ...
    }
}

Looks like we can implement it as follows:
1) Ask user to provide function pointer (or lib_name + func_name for
standalone node) for specific callback type to configuration.
2) This function, in turn, must be implemented by user with our macros,
somehow like this:
IGNITE_LISTENER_CALLBACK(MyListenerCallback) (
    IGNITE_ADD_LISTENER_CALLBACK(6, MyListener, MyKey*, MyVal*)

  IGNITE_ADD_LISTENER_CALLBACK(7, MyOtherListener, MyOtherKey*, MyOtherVal*)
)

Looks like it should do the trick and enable C++ code execution through
callbacks.

Any comments or ideas? May be we can employ visitor/double-dispatch
technique somehow here? Or something completely different?

Vladimir.
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Atri Sharma
Ugh, JNI.

Aren't we worried about portability here? IMO it's not easy to build robust
applications with JNI but not sure that's a concern for us given that this
is all in user space. ..
On 4 Jun 2015 19:16, "Vladimir Ozerov" <[hidden email]> wrote:

> Igniters,
>
> Lots of our features rely on callbacks from Ignite to user code. This is
> essential for task execution, cache invokes, cache store, continuous
> queries, messaging, etc..
>
> Ideally from user perspective target class should look somewhat like this:
> class MyListener : public IListener<MyKey*, MyVal*> {
> public:
>     bool Invoke(MyKey*, MyType*);
> }
>
> And Java -> C++ linking code will be something like this:
> jboolean JniListenerCallback(JNIEnv *env, jclass cls, jlong ptr, _others_)
> {
>     int callbackType = type(_others_);
>
>     switch (callbackType) {
>     ...
>     case 6:
>         MyKey* key = unmarshal<MyKey*>(_others_);
>         MyVal* val = unmarshal<MyVal*>(_others_);
>         return reinterpret_cast<MyListener>(ptr).Invoke(_others_);
>     case 7:
>         MyOtherKey* key = unmarshal<MyOtherKey*>(_others_);
>         MyOtherVal* val = unmarshal<MyOtherVal*>(_others_);
>         return reinterpret_cast<MyOtherListener>(ptr).Invoke(_other_);
>     ...
>     }
> }
>
> Looks like we can implement it as follows:
> 1) Ask user to provide function pointer (or lib_name + func_name for
> standalone node) for specific callback type to configuration.
> 2) This function, in turn, must be implemented by user with our macros,
> somehow like this:
> IGNITE_LISTENER_CALLBACK(MyListenerCallback) (
>     IGNITE_ADD_LISTENER_CALLBACK(6, MyListener, MyKey*, MyVal*)
>
>   IGNITE_ADD_LISTENER_CALLBACK(7, MyOtherListener, MyOtherKey*,
> MyOtherVal*)
> )
>
> Looks like it should do the trick and enable C++ code execution through
> callbacks.
>
> Any comments or ideas? May be we can employ visitor/double-dispatch
> technique somehow here? Or something completely different?
>
> Vladimir.
>
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Vladimir Ozerov
I do not see any new portability issues JNI can add. I.e. user on Windows
machine put an object to cache using JNI and Ignite libs for Windows. We
marshal that object to portable form. Then Linux user is notified about put
using JNI and Ignite libs for Linux.

As per compatibility between different Java versions, there should not be
any problems given that Java maintains binary compatibility between
versions and we are not going to statically link jvm.so/jvm.dll.

On Thu, Jun 4, 2015 at 4:52 PM, Atri Sharma <[hidden email]> wrote:

> Ugh, JNI.
>
> Aren't we worried about portability here? IMO it's not easy to build robust
> applications with JNI but not sure that's a concern for us given that this
> is all in user space. ..
> On 4 Jun 2015 19:16, "Vladimir Ozerov" <[hidden email]> wrote:
>
> > Igniters,
> >
> > Lots of our features rely on callbacks from Ignite to user code. This is
> > essential for task execution, cache invokes, cache store, continuous
> > queries, messaging, etc..
> >
> > Ideally from user perspective target class should look somewhat like
> this:
> > class MyListener : public IListener<MyKey*, MyVal*> {
> > public:
> >     bool Invoke(MyKey*, MyType*);
> > }
> >
> > And Java -> C++ linking code will be something like this:
> > jboolean JniListenerCallback(JNIEnv *env, jclass cls, jlong ptr,
> _others_)
> > {
> >     int callbackType = type(_others_);
> >
> >     switch (callbackType) {
> >     ...
> >     case 6:
> >         MyKey* key = unmarshal<MyKey*>(_others_);
> >         MyVal* val = unmarshal<MyVal*>(_others_);
> >         return reinterpret_cast<MyListener>(ptr).Invoke(_others_);
> >     case 7:
> >         MyOtherKey* key = unmarshal<MyOtherKey*>(_others_);
> >         MyOtherVal* val = unmarshal<MyOtherVal*>(_others_);
> >         return reinterpret_cast<MyOtherListener>(ptr).Invoke(_other_);
> >     ...
> >     }
> > }
> >
> > Looks like we can implement it as follows:
> > 1) Ask user to provide function pointer (or lib_name + func_name for
> > standalone node) for specific callback type to configuration.
> > 2) This function, in turn, must be implemented by user with our macros,
> > somehow like this:
> > IGNITE_LISTENER_CALLBACK(MyListenerCallback) (
> >     IGNITE_ADD_LISTENER_CALLBACK(6, MyListener, MyKey*, MyVal*)
> >
> >   IGNITE_ADD_LISTENER_CALLBACK(7, MyOtherListener, MyOtherKey*,
> > MyOtherVal*)
> > )
> >
> > Looks like it should do the trick and enable C++ code execution through
> > callbacks.
> >
> > Any comments or ideas? May be we can employ visitor/double-dispatch
> > technique somehow here? Or something completely different?
> >
> > Vladimir.
> >
>
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Branko Čibej
In reply to this post by Vladimir Ozerov
On 04.06.2015 15:45, Vladimir Ozerov wrote:

> Igniters,
>
> Lots of our features rely on callbacks from Ignite to user code. This is
> essential for task execution, cache invokes, cache store, continuous
> queries, messaging, etc..
>
> Ideally from user perspective target class should look somewhat like this:
> class MyListener : public IListener<MyKey*, MyVal*> {
> public:
>     bool Invoke(MyKey*, MyType*);
> }

Please please please do not use raw pointers in the public API. This is
a memory management nightmare waiting to happen. Either use const
references or shared pointers. std::auto_ptr_ref is an option, but not a
very good one.

-- Brane

Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Atri Sharma
Something like a smart pointer might be useful here.

I thing that I also feel that the memory handling of the C++ objects might
be the biggest issue here. What I think can happen is having Java objects
*own* C++ objects and clean up in finalize() methods. Only a small idea
though...

On Thu, Jun 4, 2015 at 8:23 PM, Branko Čibej <[hidden email]> wrote:

> On 04.06.2015 15:45, Vladimir Ozerov wrote:
> > Igniters,
> >
> > Lots of our features rely on callbacks from Ignite to user code. This is
> > essential for task execution, cache invokes, cache store, continuous
> > queries, messaging, etc..
> >
> > Ideally from user perspective target class should look somewhat like
> this:
> > class MyListener : public IListener<MyKey*, MyVal*> {
> > public:
> >     bool Invoke(MyKey*, MyType*);
> > }
>
> Please please please do not use raw pointers in the public API. This is
> a memory management nightmare waiting to happen. Either use const
> references or shared pointers. std::auto_ptr_ref is an option, but not a
> very good one.
>
> -- Brane
>
>


--
Regards,

Atri
*l'apprenant*
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Branko Čibej
On 04.06.2015 17:10, Atri Sharma wrote:
> Something like a smart pointer might be useful here.
>
> I thing that I also feel that the memory handling of the C++ objects might
> be the biggest issue here. What I think can happen is having Java objects
> *own* C++ objects and clean up in finalize() methods. Only a small idea
> though...

From my experience, finalize() happens far too late to be useful.

There's a very fundamental impedance mismatch between Java and C++: C++
has destructors, Java doesn't; and finalize() is not a good replacement
for a destructor because it doesn't know squat about scope.

For callbacks, it's probably best to just transfer ownership of objects
from Java to C++: that would imply creating the object in the JNI layer,
then sending an std::auto_ptr (or std::unique_ptr in C++11) to the
callback handler. Once it owns the object the C++ app can do whatever it
wants with it.

If that's not possible, the second option is to use proxy objects
instead of real value objects in the callbacks. Proxy objects are C++
objects that contain a global reference to a Java object and whose
native methods are just wrappers to JNIEnv::Call*Method. The constructor
and assignment operators would always create new global references and
the  destructor would release the global reference and you'd have almost
Java-like garbage collection semantics.

-- Brane

> On Thu, Jun 4, 2015 at 8:23 PM, Branko Čibej <[hidden email]> wrote:
>
>> On 04.06.2015 15:45, Vladimir Ozerov wrote:
>>> Igniters,
>>>
>>> Lots of our features rely on callbacks from Ignite to user code. This is
>>> essential for task execution, cache invokes, cache store, continuous
>>> queries, messaging, etc..
>>>
>>> Ideally from user perspective target class should look somewhat like
>> this:
>>> class MyListener : public IListener<MyKey*, MyVal*> {
>>> public:
>>>     bool Invoke(MyKey*, MyType*);
>>> }
>> Please please please do not use raw pointers in the public API. This is
>> a memory management nightmare waiting to happen. Either use const
>> references or shared pointers. std::auto_ptr_ref is an option, but not a
>> very good one.
>>
>> -- Brane
>>
>>
>

Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Atri Sharma
I like proxy objects although I am wondering if that makes copy by value
any more expensive (I am not sure about deep copying of non native objects)
On 4 Jun 2015 22:01, "Branko Čibej" <[hidden email]> wrote:

> On 04.06.2015 17:10, Atri Sharma wrote:
> > Something like a smart pointer might be useful here.
> >
> > I thing that I also feel that the memory handling of the C++ objects
> might
> > be the biggest issue here. What I think can happen is having Java objects
> > *own* C++ objects and clean up in finalize() methods. Only a small idea
> > though...
>
> From my experience, finalize() happens far too late to be useful.
>
> There's a very fundamental impedance mismatch between Java and C++: C++
> has destructors, Java doesn't; and finalize() is not a good replacement
> for a destructor because it doesn't know squat about scope.
>
> For callbacks, it's probably best to just transfer ownership of objects
> from Java to C++: that would imply creating the object in the JNI layer,
> then sending an std::auto_ptr (or std::unique_ptr in C++11) to the
> callback handler. Once it owns the object the C++ app can do whatever it
> wants with it.
>
> If that's not possible, the second option is to use proxy objects
> instead of real value objects in the callbacks. Proxy objects are C++
> objects that contain a global reference to a Java object and whose
> native methods are just wrappers to JNIEnv::Call*Method. The constructor
> and assignment operators would always create new global references and
> the  destructor would release the global reference and you'd have almost
> Java-like garbage collection semantics.
>
> -- Brane
>
> > On Thu, Jun 4, 2015 at 8:23 PM, Branko Čibej <[hidden email]> wrote:
> >
> >> On 04.06.2015 15:45, Vladimir Ozerov wrote:
> >>> Igniters,
> >>>
> >>> Lots of our features rely on callbacks from Ignite to user code. This
> is
> >>> essential for task execution, cache invokes, cache store, continuous
> >>> queries, messaging, etc..
> >>>
> >>> Ideally from user perspective target class should look somewhat like
> >> this:
> >>> class MyListener : public IListener<MyKey*, MyVal*> {
> >>> public:
> >>>     bool Invoke(MyKey*, MyType*);
> >>> }
> >> Please please please do not use raw pointers in the public API. This is
> >> a memory management nightmare waiting to happen. Either use const
> >> references or shared pointers. std::auto_ptr_ref is an option, but not a
> >> very good one.
> >>
> >> -- Brane
> >>
> >>
> >
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Branko Čibej
On 04.06.2015 18:42, Atri Sharma wrote:
> I like proxy objects although I am wondering if that makes copy by value
> any more expensive (I am not sure about deep copying of non native objects)

Note that the only data a proxy object contains is the Java reference --
a pointer by the current JNI definition. What makes copying more
expensive is the requirement to maintain a Java global reference in each
copy. For this, you can either use JNI's NewGlobalRef/DeleteGlobalRef,
or some kind of native reference counting in C++ -- effectively a shared
pointer to a (wrapped) Java global reference. I have no ides what's
faster, but I suspect that the letting JNI take care of reference
handling is the better option.

-- Brane

> On 4 Jun 2015 22:01, "Branko Čibej" <[hidden email]> wrote:
>
>> On 04.06.2015 17:10, Atri Sharma wrote:
>>> Something like a smart pointer might be useful here.
>>>
>>> I thing that I also feel that the memory handling of the C++ objects
>> might
>>> be the biggest issue here. What I think can happen is having Java objects
>>> *own* C++ objects and clean up in finalize() methods. Only a small idea
>>> though...
>> From my experience, finalize() happens far too late to be useful.
>>
>> There's a very fundamental impedance mismatch between Java and C++: C++
>> has destructors, Java doesn't; and finalize() is not a good replacement
>> for a destructor because it doesn't know squat about scope.
>>
>> For callbacks, it's probably best to just transfer ownership of objects
>> from Java to C++: that would imply creating the object in the JNI layer,
>> then sending an std::auto_ptr (or std::unique_ptr in C++11) to the
>> callback handler. Once it owns the object the C++ app can do whatever it
>> wants with it.
>>
>> If that's not possible, the second option is to use proxy objects
>> instead of real value objects in the callbacks. Proxy objects are C++
>> objects that contain a global reference to a Java object and whose
>> native methods are just wrappers to JNIEnv::Call*Method. The constructor
>> and assignment operators would always create new global references and
>> the  destructor would release the global reference and you'd have almost
>> Java-like garbage collection semantics.
>>
>> -- Brane
>>
>>> On Thu, Jun 4, 2015 at 8:23 PM, Branko Čibej <[hidden email]> wrote:
>>>
>>>> On 04.06.2015 15:45, Vladimir Ozerov wrote:
>>>>> Igniters,
>>>>>
>>>>> Lots of our features rely on callbacks from Ignite to user code. This
>> is
>>>>> essential for task execution, cache invokes, cache store, continuous
>>>>> queries, messaging, etc..
>>>>>
>>>>> Ideally from user perspective target class should look somewhat like
>>>> this:
>>>>> class MyListener : public IListener<MyKey*, MyVal*> {
>>>>> public:
>>>>>     bool Invoke(MyKey*, MyType*);
>>>>> }
>>>> Please please please do not use raw pointers in the public API. This is
>>>> a memory management nightmare waiting to happen. Either use const
>>>> references or shared pointers. std::auto_ptr_ref is an option, but not a
>>>> very good one.
>>>>
>>>> -- Brane
>>>>
>>>>
>>

Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

dsetrakyan
In reply to this post by Vladimir Ozerov
Vova,

Is this change backward compatible?

D.

On Thu, Jun 4, 2015 at 4:45 PM, Vladimir Ozerov <[hidden email]>
wrote:

> Igniters,
>
> Lots of our features rely on callbacks from Ignite to user code. This is
> essential for task execution, cache invokes, cache store, continuous
> queries, messaging, etc..
>
> Ideally from user perspective target class should look somewhat like this:
> class MyListener : public IListener<MyKey*, MyVal*> {
> public:
>     bool Invoke(MyKey*, MyType*);
> }
>
> And Java -> C++ linking code will be something like this:
> jboolean JniListenerCallback(JNIEnv *env, jclass cls, jlong ptr, _others_)
> {
>     int callbackType = type(_others_);
>
>     switch (callbackType) {
>     ...
>     case 6:
>         MyKey* key = unmarshal<MyKey*>(_others_);
>         MyVal* val = unmarshal<MyVal*>(_others_);
>         return reinterpret_cast<MyListener>(ptr).Invoke(_others_);
>     case 7:
>         MyOtherKey* key = unmarshal<MyOtherKey*>(_others_);
>         MyOtherVal* val = unmarshal<MyOtherVal*>(_others_);
>         return reinterpret_cast<MyOtherListener>(ptr).Invoke(_other_);
>     ...
>     }
> }
>
> Looks like we can implement it as follows:
> 1) Ask user to provide function pointer (or lib_name + func_name for
> standalone node) for specific callback type to configuration.
> 2) This function, in turn, must be implemented by user with our macros,
> somehow like this:
> IGNITE_LISTENER_CALLBACK(MyListenerCallback) (
>     IGNITE_ADD_LISTENER_CALLBACK(6, MyListener, MyKey*, MyVal*)
>
>   IGNITE_ADD_LISTENER_CALLBACK(7, MyOtherListener, MyOtherKey*,
> MyOtherVal*)
> )
>
> Looks like it should do the trick and enable C++ code execution through
> callbacks.
>
> Any comments or ideas? May be we can employ visitor/double-dispatch
> technique somehow here? Or something completely different?
>
> Vladimir.
>
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Java -> platform callbacks in C++.

Branko Čibej
In reply to this post by Branko Čibej
On 04.06.2015 18:49, Branko Čibej wrote:

> On 04.06.2015 18:42, Atri Sharma wrote:
>> I like proxy objects although I am wondering if that makes copy by value
>> any more expensive (I am not sure about deep copying of non native objects)
> Note that the only data a proxy object contains is the Java reference --
> a pointer by the current JNI definition. What makes copying more
> expensive is the requirement to maintain a Java global reference in each
> copy. For this, you can either use JNI's NewGlobalRef/DeleteGlobalRef,
> or some kind of native reference counting in C++ -- effectively a shared
> pointer to a (wrapped) Java global reference. I have no ides what's
> faster, but I suspect that the letting JNI take care of reference
> handling is the better option.

Something like the attached prototype.

-- Brane


proxy-mixin.cpp (1K) Download Attachment