From 3c4df59973fe7c11e82d039d5057bf3968ea4d0d Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Fri, 14 Aug 2020 17:21:45 -0300 Subject: [PATCH 1/7] Create LivelinessLostEvent Signed-off-by: Ivan Santiago Paunovic --- rcljava/CMakeLists.txt | 4 + ...org_ros2_rcljava_events_EventHandlerImpl.h | 3 +- ...events_publisher_statuses_LivelinessLost.h | 63 ++++++++++++++++ ...org_ros2_rcljava_publisher_PublisherImpl.h | 9 +++ ...g_ros2_rcljava_events_EventHandlerImpl.cpp | 3 +- ...ents_publisher_statuses_LivelinessLost.cpp | 73 +++++++++++++++++++ ...g_ros2_rcljava_publisher_PublisherImpl.cpp | 30 ++++++++ .../org/ros2/rcljava/events/EventHandler.java | 10 --- .../ros2/rcljava/events/EventHandlerImpl.java | 65 +++++------------ .../rcljava/events/PublisherEventStatus.java | 2 +- .../events/SubscriptionEventStatus.java | 2 +- .../publisher_statuses/LivelinessLost.java | 66 +++++++++++++++++ .../org/ros2/rcljava/publisher/Publisher.java | 23 ++++++ .../ros2/rcljava/publisher/PublisherImpl.java | 50 +++++++++++++ .../ros2/rcljava/publisher/PublisherTest.java | 19 +++++ 15 files changed, 363 insertions(+), 59 deletions(-) create mode 100644 rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h create mode 100644 rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp create mode 100644 rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java diff --git a/rcljava/CMakeLists.txt b/rcljava/CMakeLists.txt index d22853a2..6c278879 100644 --- a/rcljava/CMakeLists.txt +++ b/rcljava/CMakeLists.txt @@ -60,6 +60,7 @@ set(${PROJECT_NAME}_jni_sources "src/main/cpp/org_ros2_rcljava_contexts_ContextImpl.cpp" "src/main/cpp/org_ros2_rcljava_executors_BaseExecutor.cpp" "src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp" + "src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp" "src/main/cpp/org_ros2_rcljava_node_NodeImpl.cpp" "src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp" "src/main/cpp/org_ros2_rcljava_service_ServiceImpl.cpp" @@ -129,6 +130,9 @@ set(${PROJECT_NAME}_sources "src/main/java/org/ros2/rcljava/events/EventHandler.java" "src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java" "src/main/java/org/ros2/rcljava/events/EventStatus.java" + "src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java" + "src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java" + "src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java" "src/main/java/org/ros2/rcljava/executors/AnyExecutable.java" "src/main/java/org/ros2/rcljava/executors/BaseExecutor.java" "src/main/java/org/ros2/rcljava/executors/Executor.java" diff --git a/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h b/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h index 2416ca08..8dbf6ce2 100644 --- a/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h +++ b/rcljava/include/org_ros2_rcljava_events_EventHandlerImpl.h @@ -27,7 +27,8 @@ extern "C" { * Signature: (J)V */ JNIEXPORT void -JNICALL Java_org_ros2_rcljava_events_EventHandlerImpl_nativeDispose(JNIEnv *, jclass, jlong event_handle); +JNICALL Java_org_ros2_rcljava_events_EventHandlerImpl_nativeDispose( + JNIEnv *, jclass, jlong event_handle); /* * Class: org_ros2_rcljava_events_EventHandlerImpl diff --git a/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h b/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h new file mode 100644 index 00000000..2a4bbe80 --- /dev/null +++ b/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h @@ -0,0 +1,63 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +/* Header for class org_ros2_rcljava_events_publisher_statuses_LivelinessLost */ + +#ifndef ORG_ROS2_RCLJAVA_EVENTS_PUBLISHER_STATUSES_LIVELINESSLOST_H_ +#define ORG_ROS2_RCLJAVA_EVENTS_PUBLISHER_STATUSES_LIVELINESSLOST_H_ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost + * Method: allocateRCLStatusEvent + * Signature: ()J + */ +JNIEXPORT jlong JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeAllocateRCLStatusEvent( + JNIEnv *, jclass); + +/* + * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost + * Method: deallocateRCLStatusEvent + * Signature: ()J + */ +JNIEXPORT void JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeDeallocateRCLStatusEvent( + JNIEnv *, jclass, jlong); + +/* + * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost + * Method: nativeFromRCLEvent + * Signature: (J)V + */ +JNIEXPORT void JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeFromRCLEvent( + JNIEnv *, jobject, jlong); + +/* + * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost + * Method: nativeGetPublisherEventType + * Signature: (V)I + */ +JNIEXPORT jint JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeGetPublisherEventType( + JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif // ORG_ROS2_RCLJAVA_EVENTS_PUBLISHER_STATUSES_LIVELINESSLOST_H_ diff --git a/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h b/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h index befe7447..9236fdda 100644 --- a/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h +++ b/rcljava/include/org_ros2_rcljava_publisher_PublisherImpl.h @@ -38,6 +38,15 @@ JNIEXPORT void JNICALL Java_org_ros2_rcljava_publisher_PublisherImpl_nativeDispose( JNIEnv *, jclass, jlong, jlong); +/* + * Class: org_ros2_rcljava_publisher_PublisherImpl + * Method: nativeCreateEvent + * Signature: (JJ)J + */ +JNIEXPORT jlong +JNICALL Java_org_ros2_rcljava_publisher_PublisherImpl_nativeCreateEvent( + JNIEnv *, jclass, jlong, jint); + #ifdef __cplusplus } #endif diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp index c23fd533..89e2faba 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_events_EventHandlerImpl.cpp @@ -47,7 +47,8 @@ Java_org_ros2_rcljava_events_EventHandlerImpl_nativeDispose( } JNIEXPORT void JNICALL -Java_org_ros2_rcljava_events_EventHandlerImpl_nativeTake(JNIEnv *env, jclass, jlong event_handle, jlong event_status_handle) +Java_org_ros2_rcljava_events_EventHandlerImpl_nativeTake( + JNIEnv * env, jclass, jlong event_handle, jlong event_status_handle) { auto * event = reinterpret_cast(event_handle); if (!event) { diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp new file mode 100644 index 00000000..ab3d5004 --- /dev/null +++ b/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp @@ -0,0 +1,73 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h" + +#include +#include + +#include "rmw/events_statuses/liveliness_lost.h" +#include "rcl/event.h" +#include "rcljava_common/exceptions.hpp" + +using rcljava_common::exceptions::rcljava_throw_exception; + +JNIEXPORT jlong JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeAllocateRCLStatusEvent( + JNIEnv * env, jclass) +{ + void * p = malloc(sizeof(rmw_liveliness_lost_status_t)); + if (!p) { + rcljava_throw_exception( + env, "java/lang/OutOfMemoryError", "failed to allocate liveliness lost status"); + } + return reinterpret_cast(p); +} + +JNIEXPORT void JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeDeallocateRCLStatusEvent( + JNIEnv *, jclass, jlong handle) +{ + free(reinterpret_cast(handle)); +} + +JNIEXPORT void JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeFromRCLEvent( + JNIEnv * env, jobject self, jlong handle) +{ + auto * p = reinterpret_cast(handle); + if (!p) { + rcljava_throw_exception( + env, "java/lang/IllegalArgumentException", "passed rmw object handle is NULL"); + } + // TODO(ivanpauno): class and field lookup could be done at startup time + jclass clazz = env->GetObjectClass(self); + jfieldID total_count_fid = env->GetFieldID(clazz, "total_count", "I"); + if (env->ExceptionCheck()) { + return; + } + jfieldID total_count_change_fid = env->GetFieldID(clazz, "total_count_change", "I"); + if (env->ExceptionCheck()) { + return; + } + env->SetIntField(self, total_count_fid, p->total_count); + env->SetIntField(self, total_count_change_fid, p->total_count_change); +} + +JNIEXPORT jint JNICALL +org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeGetPublisherEventType( + JNIEnv *, jclass) +{ + return RCL_PUBLISHER_LIVELINESS_LOST; +} diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp index 6cef82c2..ea5d9feb 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_publisher_PublisherImpl.cpp @@ -20,6 +20,7 @@ #include #include "rcl/error_handling.h" +#include "rcl/event.h" #include "rcl/node.h" #include "rcl/rcl.h" #include "rmw/rmw.h" @@ -29,6 +30,7 @@ #include "org_ros2_rcljava_publisher_PublisherImpl.h" +using rcljava_common::exceptions::rcljava_throw_exception; using rcljava_common::exceptions::rcljava_throw_rclexception; using rcljava_common::signatures::convert_from_java_signature; using rcljava_common::signatures::destroy_ros_message_signature; @@ -90,3 +92,31 @@ Java_org_ros2_rcljava_publisher_PublisherImpl_nativeDispose( rcljava_throw_rclexception(env, ret, msg); } } + +JNIEXPORT jlong JNICALL +Java_org_ros2_rcljava_publisher_PublisherImpl_nativeCreateEvent( + JNIEnv * env, jclass, jlong publisher_handle, jint event_type) +{ + auto * publisher = reinterpret_cast(publisher_handle); + if (!publisher) { + rcljava_throw_exception( + env, "java/lang/IllegalArgumentException", "passed rcl_publisher_t handle is NULL"); + return 0; + } + auto * event = static_cast(malloc(sizeof(rcl_event_t))); + if (!event) { + rcljava_throw_exception(env, "java/lang/OutOfMemoryError", "failed to allocate rcl_event_t"); + return 0; + } + *event = rcl_get_zero_initialized_event(); + rcl_ret_t ret = rcl_publisher_event_init( + event, publisher, static_cast(event_type)); + if (RCL_RET_OK != ret) { + std::string msg = "Failed to create event: " + std::string(rcl_get_error_string().str); + rcl_reset_error(); + rcljava_throw_rclexception(env, ret, msg); + free(event); + return 0; + } + return reinterpret_cast(event); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java index 7df0f4fb..6f6c7985 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandler.java @@ -29,16 +29,6 @@ * @param The parent class type. */ public interface EventHandler extends Disposable { - /** - * @return The event status type. - */ - Class getEventStatusType(); - - /** - * @return The parent entity type. - */ - Class getParentType(); - /** * @return A weak reference to the parent. */ diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java index 013ec29a..f80a55f0 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java @@ -16,6 +16,7 @@ import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; +import java.util.function.Supplier; import org.ros2.rcljava.common.JNIUtils; import org.ros2.rcljava.consumers.Consumer; @@ -53,46 +54,25 @@ public class EventHandlerImpl< /** * Constructor. * - * @param parentType The Class type of the parent. - * It can be either a @{link org.ros2.rcljava.Publisher} or a - * @{link org.ros2.rcljava.Subscription} class. * @param parentReference A {@link java.lang.ref.WeakReference} to the * @{link org.ros2.rcljava.Publisher} or @{link org.ros2.rcljava.Subscription} * that created this event handler. * @param handle A pointer to the underlying ROS 2 event structure, as an integer. * Must not be zero. - * @param eventStatusType The Class of the messages that this - * subscription will receive. We need this because of Java's type erasure, - * which doesn't allow us to use the generic parameter of - * @{link org.ros2.rcljava.Subscription} directly. + * @param eventStatusFactory Factory of an event status. * @param callback The callback function that will be called when the event * is triggered. */ public EventHandlerImpl( - final Class parentType, final WeakReference parentReference, final long handle, - final Class eventStatusType, + final Supplier eventStatusFactory, final Consumer callback) { - this.parentType = parentType; this.parentReference = parentReference; this.handle = handle; - this.eventStatusType = eventStatusType; + this.eventStatusFactory = eventStatusFactory; this.callback = callback; - } - - /** - * {@inheritDoc} - */ - public final Class getEventStatusType() { - return this.eventStatusType; - } - - /** - * {@inheritDoc} - */ - public final Class getParentType() { - return this.parentType; + this.handleMutex = new Object(); } /** @@ -121,8 +101,12 @@ public final long getHandle() { * {@inheritDoc} */ public final void dispose() { - nativeDispose(this.handle); - this.handle = 0; + synchronized(this.handleMutex) { + if (this.handle != 0) { + nativeDispose(this.handle); + } + this.handle = 0; + } } /** @@ -138,28 +122,19 @@ public final void dispose() { * {@inheritDoc} */ public final void executeCallback() { - T eventStatus = null; - try { - eventStatus = this.eventStatusType.getDeclaredConstructor().newInstance(); - } catch (NoSuchMethodException nme) { - nme.printStackTrace(); - } catch (InvocationTargetException ite) { - ite.printStackTrace(); - } catch (InstantiationException ie) { - ie.printStackTrace(); - } catch (IllegalAccessException iae) { - iae.printStackTrace(); + synchronized(this.handleMutex) { + T eventStatus = eventStatusFactory.get(); + long nativeEventStatusHandle = eventStatus.allocateRCLStatusEvent(); + nativeTake(this.handle, nativeEventStatusHandle); + eventStatus.fromRCLEvent(nativeEventStatusHandle); + eventStatus.deallocateRCLStatusEvent(nativeEventStatusHandle); + callback.accept(eventStatus); } - long nativeEventStatusHandle = eventStatus.allocateRCLStatusEvent(); - nativeTake(this.handle, nativeEventStatusHandle); - eventStatus.fromRCLEvent(nativeEventStatusHandle); - eventStatus.deallocateRCLStatusEvent(nativeEventStatusHandle); - callback.accept(eventStatus); } - private final Class eventStatusType; - private final Class parentType; + private final Supplier eventStatusFactory; private final WeakReference parentReference; + private final Object handleMutex; private long handle; private final Consumer callback; } diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java b/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java index b6c9881b..a1eb3375 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/PublisherEventStatus.java @@ -24,5 +24,5 @@ public interface PublisherEventStatus extends EventStatus { /** * @return The status event type. */ - int get_publisher_event_type(); + int getPublisherEventType(); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java b/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java index 224e9e53..55d93372 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/SubscriptionEventStatus.java @@ -24,5 +24,5 @@ public interface SubscriptionEventStatus extends EventStatus { /** * @return The status event type. */ - int get_subscription_event_type(); + int getSubscriptionEventType(); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java b/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java new file mode 100644 index 00000000..6318faa4 --- /dev/null +++ b/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java @@ -0,0 +1,66 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.ros2.rcljava.events.publisher_statuses; + +import java.util.function.Supplier; + +import org.ros2.rcljava.common.JNIUtils; +import org.ros2.rcljava.events.PublisherEventStatus; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class serves as a bridge between a rmw_liveliness_lost_status_t and RCLJava. + */ +public class LivelinessLost implements PublisherEventStatus { + int total_count; + int total_count_change; + + public final long allocateRCLStatusEvent() { + return nativeAllocateRCLStatusEvent(); + } + public final void deallocateRCLStatusEvent(long handle) { + nativeDeallocateRCLStatusEvent(handle); + } + public final void fromRCLEvent(long handle) { + nativeFromRCLEvent(handle); + } + public final int getPublisherEventType() { + // return 0; + return nativeGetPublisherEventType(); + } + // TODO(ivanpauno): Remove this when -source 8 can be used (method references for the win) + public static final Supplier factory = new Supplier() { + public LivelinessLost get() { + return new LivelinessLost(); + } + }; + + private static final Logger logger = LoggerFactory.getLogger(LivelinessLost.class); + static { + try { + JNIUtils.loadImplementation(LivelinessLost.class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + private static native long nativeAllocateRCLStatusEvent(); + private static native void nativeDeallocateRCLStatusEvent(long handle); + private native void nativeFromRCLEvent(long handle); + private static native int nativeGetPublisherEventType(); +} diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java index 0d2427b7..b2be4c43 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java @@ -16,9 +16,13 @@ package org.ros2.rcljava.publisher; import java.lang.ref.WeakReference; +import java.util.function.Supplier; +import org.ros2.rcljava.consumers.Consumer; import org.ros2.rcljava.interfaces.Disposable; import org.ros2.rcljava.interfaces.MessageDefinition; +import org.ros2.rcljava.events.EventHandler; +import org.ros2.rcljava.events.PublisherEventStatus; import org.ros2.rcljava.node.Node; /** @@ -41,4 +45,23 @@ public interface Publisher extends Disposable { * that created this publisher. */ WeakReference getNodeReference(); + + /** + * Register an event handler. + * + * @param A publisher event status type. + * @param factory A factory that can instantiate an event status of type T. + * @param callback Callback that will be called when the event is triggered. + */ + EventHandler registerEventHandler( + Supplier factory, Consumer callback); + + /** + * Dispose a previously registered event handler. + * + * @param A publisher event status type. + * @param eventHandler An event handler that was registered previously in this object. + */ + void disposeEventHandler( + EventHandler eventHandler); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java index f3fa717b..de77bebe 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java @@ -16,10 +16,17 @@ package org.ros2.rcljava.publisher; import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.ros2.rcljava.consumers.Consumer; +import org.ros2.rcljava.events.EventHandler; +import org.ros2.rcljava.events.EventHandlerImpl; +import org.ros2.rcljava.events.PublisherEventStatus; import org.ros2.rcljava.RCLJava; import org.ros2.rcljava.common.JNIUtils; import org.ros2.rcljava.interfaces.MessageDefinition; @@ -53,6 +60,8 @@ public class PublisherImpl implements Publisher */ private final String topic; + private final Collection eventHandlers; + /** * Constructor. * @@ -67,6 +76,7 @@ public PublisherImpl( this.nodeReference = nodeReference; this.handle = handle; this.topic = topic; + this.eventHandlers = new LinkedBlockingQueue(); } /** @@ -101,6 +111,42 @@ public final WeakReference getNodeReference() { return this.nodeReference; } + /** + * {@inheritDoc} + */ + public final + EventHandler + registerEventHandler(Supplier factory, Consumer callback) { + T status = factory.get(); + long eventHandle = nativeCreateEvent(this.handle, status.getPublisherEventType()); + EventHandler eventHandler = new EventHandlerImpl( + new WeakReference(this), eventHandle, factory, callback); + this.eventHandlers.add(eventHandler); + return eventHandler; + } + + /** + * {@inheritDoc} + */ + public final + void disposeEventHandler( + EventHandler eventHandler) + { + if (!this.eventHandlers.remove(eventHandler)) { + throw new IllegalArgumentException("The passed eventHandler wasn't created by this publisher"); + } + eventHandler.dispose(); + } + + /** + * Create a publisher event (rcl_event_t) + * + * @param handle A pointer to the underlying ROS2 publisher structure. + * Must not be zero. + * @param eventType The rcl event type. + */ + private static native long nativeCreateEvent(long handle, int eventType); + /** * Destroy a ROS2 publisher (rcl_publisher_t). * @@ -115,6 +161,10 @@ public final WeakReference getNodeReference() { * {@inheritDoc} */ public final void dispose() { + for (EventHandler eventHandler : this.eventHandlers) { + eventHandler.dispose(); + } + this.eventHandlers.clear(); Node node = this.nodeReference.get(); if (node != null) { node.removePublisher(this); diff --git a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java index bfacdd2a..23338a54 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java @@ -22,6 +22,9 @@ import org.junit.Test; import org.ros2.rcljava.RCLJava; +import org.ros2.rcljava.consumers.Consumer; +import org.ros2.rcljava.events.EventHandler; +import org.ros2.rcljava.events.publisher_statuses.LivelinessLost; import org.ros2.rcljava.node.Node; public class PublisherTest { @@ -42,4 +45,20 @@ public final void testCreate() { assertNotEquals(0, publisher.getHandle()); RCLJava.shutdown(); } + + @Test + public final void testCreateLivelinessLostEvent() { + RCLJava.rclJavaInit(); + Node node = RCLJava.createNode("test_node"); + Publisher publisher = + node.createPublisher(std_msgs.msg.String.class, "test_topic"); + EventHandler eventHandler = publisher.registerEventHandler( + LivelinessLost.factory, new Consumer() { + public void accept(final LivelinessLost status) {} + } + ); + assertNotEquals(0, eventHandler.getHandle()); + RCLJava.shutdown(); + assertEquals(0, eventHandler.getHandle()); + } } From 7b624c9daccc59c3f8e2aa8036b9d8099d52a137 Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Fri, 14 Aug 2020 18:10:41 -0300 Subject: [PATCH 2/7] Fix linking issue Signed-off-by: Ivan Santiago Paunovic --- ...va_events_publisher_statuses_LivelinessLost.h | 16 ++++++++-------- ..._events_publisher_statuses_LivelinessLost.cpp | 8 ++++---- .../publisher_statuses/LivelinessLost.java | 1 - 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h b/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h index 2a4bbe80..53126f7b 100644 --- a/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h +++ b/rcljava/include/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.h @@ -23,20 +23,20 @@ extern "C" { /* * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost - * Method: allocateRCLStatusEvent + * Method: nativeAllocateRCLStatusEvent * Signature: ()J */ JNIEXPORT jlong JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeAllocateRCLStatusEvent( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeAllocateRCLStatusEvent( JNIEnv *, jclass); /* * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost - * Method: deallocateRCLStatusEvent - * Signature: ()J + * Method: nativeDeallocateRCLStatusEvent + * Signature: (J)V */ JNIEXPORT void JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeDeallocateRCLStatusEvent( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeDeallocateRCLStatusEvent( JNIEnv *, jclass, jlong); /* @@ -45,16 +45,16 @@ org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeDeallocateRCLSta * Signature: (J)V */ JNIEXPORT void JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeFromRCLEvent( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeFromRCLEvent( JNIEnv *, jobject, jlong); /* * Class: org_ros2_rcljava_events_publisher_statuses_LivelinessLost * Method: nativeGetPublisherEventType - * Signature: (V)I + * Signature: ()I */ JNIEXPORT jint JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeGetPublisherEventType( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeGetPublisherEventType( JNIEnv *, jclass); #ifdef __cplusplus diff --git a/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp b/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp index ab3d5004..95bad689 100644 --- a/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp +++ b/rcljava/src/main/cpp/org_ros2_rcljava_events_publisher_statuses_LivelinessLost.cpp @@ -24,7 +24,7 @@ using rcljava_common::exceptions::rcljava_throw_exception; JNIEXPORT jlong JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeAllocateRCLStatusEvent( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeAllocateRCLStatusEvent( JNIEnv * env, jclass) { void * p = malloc(sizeof(rmw_liveliness_lost_status_t)); @@ -36,14 +36,14 @@ org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeAllocateRCLStatu } JNIEXPORT void JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeDeallocateRCLStatusEvent( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeDeallocateRCLStatusEvent( JNIEnv *, jclass, jlong handle) { free(reinterpret_cast(handle)); } JNIEXPORT void JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeFromRCLEvent( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeFromRCLEvent( JNIEnv * env, jobject self, jlong handle) { auto * p = reinterpret_cast(handle); @@ -66,7 +66,7 @@ org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeFromRCLEvent( } JNIEXPORT jint JNICALL -org_ros2_rcljava_events_publisher_statuses_LivelinessLost_nativeGetPublisherEventType( +Java_org_ros2_rcljava_events_publisher_1statuses_LivelinessLost_nativeGetPublisherEventType( JNIEnv *, jclass) { return RCL_PUBLISHER_LIVELINESS_LOST; diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java b/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java index 6318faa4..615126be 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/publisher_statuses/LivelinessLost.java @@ -39,7 +39,6 @@ public final void fromRCLEvent(long handle) { nativeFromRCLEvent(handle); } public final int getPublisherEventType() { - // return 0; return nativeGetPublisherEventType(); } // TODO(ivanpauno): Remove this when -source 8 can be used (method references for the win) From 372edb1544f7ea8a838f6717399f9b9407cc476c Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 18 Aug 2020 11:29:11 -0300 Subject: [PATCH 3/7] Rename disposeEventHandler to removeEventHandler Signed-off-by: Ivan Santiago Paunovic --- rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java | 2 +- .../src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java index b2be4c43..abcaa9df 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java @@ -62,6 +62,6 @@ EventHandler registerEventHandler * @param A publisher event status type. * @param eventHandler An event handler that was registered previously in this object. */ - void disposeEventHandler( + void removeEventHandler( EventHandler eventHandler); } diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java index de77bebe..1971f884 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java @@ -129,7 +129,7 @@ public final WeakReference getNodeReference() { * {@inheritDoc} */ public final - void disposeEventHandler( + void removeEventHandler( EventHandler eventHandler) { if (!this.eventHandlers.remove(eventHandler)) { From fb342cca664e8df957bed7c0377df565c8bac38c Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 18 Aug 2020 11:59:28 -0300 Subject: [PATCH 4/7] Use synchronized methods in EventHandlerImpl Signed-off-by: Ivan Santiago Paunovic --- .../ros2/rcljava/events/EventHandlerImpl.java | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java index f80a55f0..d2d382f8 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java @@ -72,7 +72,6 @@ public EventHandlerImpl( this.handle = handle; this.eventStatusFactory = eventStatusFactory; this.callback = callback; - this.handleMutex = new Object(); } /** @@ -100,13 +99,11 @@ public final long getHandle() { /** * {@inheritDoc} */ - public final void dispose() { - synchronized(this.handleMutex) { - if (this.handle != 0) { - nativeDispose(this.handle); - } - this.handle = 0; + public synchronized final void dispose() { + if (this.handle != 0) { + nativeDispose(this.handle); } + this.handle = 0; } /** @@ -121,20 +118,17 @@ public final void dispose() { /** * {@inheritDoc} */ - public final void executeCallback() { - synchronized(this.handleMutex) { - T eventStatus = eventStatusFactory.get(); - long nativeEventStatusHandle = eventStatus.allocateRCLStatusEvent(); - nativeTake(this.handle, nativeEventStatusHandle); - eventStatus.fromRCLEvent(nativeEventStatusHandle); - eventStatus.deallocateRCLStatusEvent(nativeEventStatusHandle); - callback.accept(eventStatus); - } + public synchronized final void executeCallback() { + T eventStatus = eventStatusFactory.get(); + long nativeEventStatusHandle = eventStatus.allocateRCLStatusEvent(); + nativeTake(this.handle, nativeEventStatusHandle); + eventStatus.fromRCLEvent(nativeEventStatusHandle); + eventStatus.deallocateRCLStatusEvent(nativeEventStatusHandle); + callback.accept(eventStatus); } private final Supplier eventStatusFactory; private final WeakReference parentReference; - private final Object handleMutex; private long handle; private final Consumer callback; } From 9b76bb1d12ab69e15a870126365987133931c3cc Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 18 Aug 2020 13:20:40 -0300 Subject: [PATCH 5/7] Use createEventHandler instead of registerEventHanlder Signed-off-by: Ivan Santiago Paunovic --- .../src/main/java/org/ros2/rcljava/publisher/Publisher.java | 4 ++-- .../main/java/org/ros2/rcljava/publisher/PublisherImpl.java | 2 +- .../test/java/org/ros2/rcljava/publisher/PublisherTest.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java index abcaa9df..a5acc338 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java @@ -47,13 +47,13 @@ public interface Publisher extends Disposable { WeakReference getNodeReference(); /** - * Register an event handler. + * Create an event handler. * * @param A publisher event status type. * @param factory A factory that can instantiate an event status of type T. * @param callback Callback that will be called when the event is triggered. */ - EventHandler registerEventHandler( + EventHandler createEventHandler( Supplier factory, Consumer callback); /** diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java index 1971f884..ebbd0d85 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java @@ -116,7 +116,7 @@ public final WeakReference getNodeReference() { */ public final EventHandler - registerEventHandler(Supplier factory, Consumer callback) { + createEventHandler(Supplier factory, Consumer callback) { T status = factory.get(); long eventHandle = nativeCreateEvent(this.handle, status.getPublisherEventType()); EventHandler eventHandler = new EventHandlerImpl( diff --git a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java index 23338a54..472d8ea0 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/publisher/PublisherTest.java @@ -52,7 +52,7 @@ public final void testCreateLivelinessLostEvent() { Node node = RCLJava.createNode("test_node"); Publisher publisher = node.createPublisher(std_msgs.msg.String.class, "test_topic"); - EventHandler eventHandler = publisher.registerEventHandler( + EventHandler eventHandler = publisher.createEventHandler( LivelinessLost.factory, new Consumer() { public void accept(final LivelinessLost status) {} } From 31dbc230bebd2663f98ec9e1c6fbbf25d9ca7c65 Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 18 Aug 2020 13:22:16 -0300 Subject: [PATCH 6/7] nit Signed-off-by: Ivan Santiago Paunovic --- rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java index a5acc338..d21eece2 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/Publisher.java @@ -57,7 +57,7 @@ EventHandler createEventHandler( Supplier factory, Consumer callback); /** - * Dispose a previously registered event handler. + * Remove a previously registered event handler. * * @param A publisher event status type. * @param eventHandler An event handler that was registered previously in this object. From 60e1a001e535b89efe30c03efc670198a44378f3 Mon Sep 17 00:00:00 2001 From: Ivan Santiago Paunovic Date: Tue, 18 Aug 2020 15:34:32 -0300 Subject: [PATCH 7/7] Clarify ownership of the event handle Signed-off-by: Ivan Santiago Paunovic --- .../main/java/org/ros2/rcljava/events/EventHandlerImpl.java | 2 ++ .../main/java/org/ros2/rcljava/publisher/PublisherImpl.java | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java index d2d382f8..25545c7f 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/events/EventHandlerImpl.java @@ -59,6 +59,8 @@ public class EventHandlerImpl< * that created this event handler. * @param handle A pointer to the underlying ROS 2 event structure, as an integer. * Must not be zero. + * Ownership of the passed `handle` is taken, and this object is responsible + * of disposing it. That can be done using @{link EventHandler#dispose()}. * @param eventStatusFactory Factory of an event status. * @param callback The callback function that will be called when the event * is triggered. diff --git a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java index ebbd0d85..2bba3892 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/publisher/PublisherImpl.java @@ -139,7 +139,10 @@ void removeEventHandler( } /** - * Create a publisher event (rcl_event_t) + * Create a publisher event (rcl_event_t). + * + * The ownership of the created event handle will immediately be transferred to an + * @{link EventHandlerImpl}, that will be responsible of disposing it. * * @param handle A pointer to the underlying ROS2 publisher structure. * Must not be zero.