Synchronous resource release mechanism 22/42622/3 accepted/tizen/mobile/20150722.094357 accepted/tizen/tv/20150722.095031 accepted/tizen/wearable/20150722.094530 submit/tizen/20150722.090021
authorDenis Melenevsky <d.melenevsky@samsung.com>
Tue, 21 Jul 2015 16:04:03 +0000 (19:04 +0300)
committerDenis Melenevsky <d.melenevsky@samsung.com>
Tue, 21 Jul 2015 16:04:03 +0000 (19:04 +0300)
Change-Id: I5d54d013a94acb6c6206d35834a8dacb7bcbf2ce
Signed-off-by: Denis Melenevsky <d.melenevsky@samsung.com>
25 files changed:
m4/websockets.m4
src/common/websocklib.c
src/plugins/plugin-resource-dbus.c
src/plugins/resource-native/libmurphy-resource/api_test.c
src/plugins/resource-native/libmurphy-resource/message.c
src/plugins/resource-native/libmurphy-resource/message.h
src/plugins/resource-native/libmurphy-resource/resource-api.h
src/plugins/resource-native/libmurphy-resource/resource-private.h
src/plugins/resource-native/libmurphy-resource/resource.c
src/plugins/resource-native/libmurphy-resource/rset.c
src/plugins/resource-native/plugin-resource-native.c
src/plugins/resource-wrt/plugin-resource-wrt.c
src/plugins/resource-wrt/resource-api.js
src/plugins/resource-wrt/resource-test.html
src/plugins/resource-wrt/resource-wrt.h
src/resource/client-api.h
src/resource/config-lua.c
src/resource/data-types.h
src/resource/manager-api.h
src/resource/protocol.h
src/resource/resource-owner.c
src/resource/resource-set.c
src/resource/resource-set.h
src/resource/resource.c
src/resource/resource.h

index ab1a41d7f9a27d09ddcd74fc6b81fd5396af8a5b..1daaa42d6e02b62ebf179809cb6557ce2bb206b9 100644 (file)
@@ -49,6 +49,49 @@ if test "$enable_websockets" != "no"; then
             [websockets_cci=no])
         AC_MSG_RESULT([$websockets_cci])
 
+        # Check for ssl_cipher_list in context creation info
+        if test "$websockets_cci" = "yes"; then
+            AC_MSG_CHECKING([for WEBSOCKETS cipher list support])
+            AC_LINK_IFELSE(
+               [AC_LANG_PROGRAM(
+                     [[#include <stdlib.h>
+                       #include <libwebsockets.h>]],
+                     [[struct libwebsocket_context *ctx;
+                       void *ptr;
+                       ptr = &ctx->ssl_cipher_list;]])],
+                [websockets_cipherlist=yes],
+                [websockets_cipherlist=no])
+            AC_MSG_RESULT([$websockets_cipherlist])
+        fi
+
+        # Check for pollargs struct support
+        if test "$websockets_cci" = "yes"; then
+            AC_MSG_CHECKING([for WEBSOCKETS pollargs support])
+            AC_LINK_IFELSE(
+               [AC_LANG_PROGRAM(
+                     [[#include <stdlib.h>
+                       #include <libwebsockets.h>]],
+                     [[struct libwebsocket_pollargs = NULL;]])],
+                [websockets_passfd=pollargs],
+                [websockets_passfd=no])
+            AC_MSG_RESULT([$websockets_passfd])
+        else
+            websockets_passfd=no
+        fi
+
+        # Argh... try to detect the poll fd passing API...
+        if test "$websocket_passfd" != "pollargs"; then
+            AC_MSG_CHECKING([for WEBSOCKETS POLL fd passing API])
+            AC_LINK_IFELSE(
+               [AC_LANG_PROGRAM(
+                     [[#include <stdlib.h>
+                       #include <libwebsockets.h>]],
+                     [[printf("%d\n", LWS_MAX_HEADER_NAME_LENGTH);]])],
+                [websockets_passfd=user],
+                [websockets_passfd=in])
+            AC_MSG_RESULT([$websockets_passfd])
+        fi
+
         # Check for new libwebsockets_get_internal_extensions.
         AC_MSG_CHECKING([for WEBSOCKETS internal extension query API])
         AC_LINK_IFELSE(
@@ -179,6 +222,19 @@ if test "$enable_websockets" != "no"; then
     if test "$websockets_cci" = "yes"; then
         WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_CONTEXT_INFO"
     fi
+    if test "$websockets_cipherlist" = "yes"; then
+        WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_CIPHER_LIST"
+    fi
+    if test "$websockets_passfd" = "pollargs"; then
+        WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_PASSFD_POLLARGS"
+    else
+        if test "$websockets_passfd" = "in"; then
+            WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_PASSFD_IN"
+        else
+            WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_PASSFD_USER"
+        fi
+    fi
+
     if test "$websockets_query_ext" = "yes"; then
         WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_QUERY_EXTENSIONS"
     fi
index 0595c4617191d853f6305c426968cdbc39851285..55c8f2fe338ac9821a001b5f19682b1e304d5610 100644 (file)
@@ -743,7 +743,15 @@ wsl_ctx_t *wsl_create_context(mrp_mainloop_t *ml, wsl_ctx_cfg_t *cfg)
     cci.ssl_cert_filepath        = cfg->ssl_cert;
     cci.ssl_private_key_filepath = cfg->ssl_pkey;
     cci.ssl_ca_filepath          = cfg->ssl_ca;
+#ifdef WEBSOCKETS_CIPHER_LIST
     cci.ssl_cipher_list          = cfg->ssl_ciphers;
+#else
+    if (cfg->ssl_ciphers != NULL) {
+        mrp_log_error("setting libwebsockets cipher list not supported");
+        errno = EOPNOTSUPP;
+        return NULL;
+    }
+#endif
 
     cci.options     = 0;
     cci.ka_time     = cfg->timeout;
@@ -1323,6 +1331,59 @@ static int verify_client_cert(void *user, void *in, size_t len)
 #endif
 
 
+static int getpollfd(void *user, void *in, size_t len, int *fd, int *mask)
+{
+#if defined WEBSOCKETS_PASSFD_POLLARGS
+    struct libwebsocket_pollargs *args;
+
+    MRP_UNUSED(user);
+    MRP_UNUSED(in);
+
+    args = (struct libwebsocket_pollargs *)in;
+
+    *fd = args->fd;
+    if (mask != NULL)
+        *mask = args->events;
+
+#elif defined WEBSOCKETS_PASSFD_IN
+    static int misdetected = 0;
+    int ufd = (ptrdiff_t)user;
+
+    if (!misdetected) {
+        *fd = (ptrdiff_t)in;
+        if (mask != NULL)
+            *mask = (int)len;
+
+        if (0 < ufd && ufd < 4096) {
+            misdetected = 1;
+            mrp_log_error("*** websockets fd passing convention misdetected.");
+            mrp_log_error("*** fixing it up...");
+            goto fixup;
+        }
+    }
+    else {
+    fixup:
+
+        *fd = ufd;
+        if (mask != NULL)
+            *mask = (int)len;
+    }
+
+#else
+    MRP_UNUSED(in);
+
+    *fd = (ptrdiff_t)user;
+    if (mask != NULL)
+        *mask = (int)len;
+
+#endif
+
+    if (*fd != 0)
+        return 0;
+    else
+        return -1;
+}
+
 
 static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
                       void *user, void *in, size_t len)
@@ -1333,6 +1394,9 @@ static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
     const char  *ext, *uri;
     int          fd, mask, status, accepted;
 
+    mrp_debug("ctx: %p, ws: %p, event: %d, user: %p, in: %p, len: %zd",
+              ws_ctx, ws, event, user, in, len);
+
     switch (event) {
     case LWS_CALLBACK_ESTABLISHED:
         mrp_debug("client-handshake completed on websocket %p/%p", ws, user);
@@ -1405,12 +1469,17 @@ static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
 #else /* WEBSOCKETS_CHANGE_MODE_POLL_FD */
 
     case LWS_CALLBACK_ADD_POLL_FD:
-#ifdef WEBSOCKETS_CONTEXT_INFO           /* just brilliant... */
+        getpollfd(user, in, len, &fd, &mask);
+#if 0
+        /* just brilliant... */
+#if defined(WEBSOCKETS_CONTEXT_INFO) && defined(WEBSOCKETS_CIPHER_LIST)
         fd   = (ptrdiff_t)in;
 #else
         fd   = (ptrdiff_t)user;
 #endif
         mask = (int)len;
+#endif
+
         mrp_debug("start polling fd %d for events 0x%x", fd, mask);
         if (add_fd(ctx, fd, mask))
             return LWS_EVENT_OK;
@@ -1418,10 +1487,14 @@ static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
             return LWS_EVENT_ERROR;
 
     case LWS_CALLBACK_DEL_POLL_FD:
-#ifdef WEBSOCKETS_CONTEXT_INFO           /* just brilliant... */
+        getpollfd(user, in, len, &fd, NULL);
+#if 0
+        /* just brilliant... */
+#if defined(WEBSOCKETS_CONTEXT_INFO) && defined(WEBSOCKETS_CIPHER_LIST)
         fd = (ptrdiff_t)in;
 #else
         fd = (ptrdiff_t)user;
+#endif
 #endif
         mrp_debug("stop polling fd %d", fd);
         if (del_fd(ctx, fd))
@@ -1430,12 +1503,16 @@ static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
             return LWS_EVENT_ERROR;
 
     case LWS_CALLBACK_SET_MODE_POLL_FD:
-#ifdef WEBSOCKETS_CONTEXT_INFO           /* just brilliant... */
+        getpollfd(user, in, len, &fd, &mask);
+#if 0
+        /* just brilliant... */
+#if defined(WEBSOCKETS_CONTEXT_INFO) && defined(WEBSOCKETS_CIPHER_LIST)
         fd   = (ptrdiff_t)in;
 #else
         fd   = (ptrdiff_t)user;
 #endif
         mask = (int)len;
+#endif
         mrp_debug("enable poll events 0x%x for fd %d", mask, fd);
         if (mod_fd(ctx, fd, mask, FALSE))
             return LWS_EVENT_OK;
@@ -1443,12 +1520,16 @@ static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
             return LWS_EVENT_ERROR;
 
     case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
-#ifdef WEBSOCKETS_CONTEXT_INFO           /* just brilliant... */
+        getpollfd(user, in, len, &fd, &mask);
+#if 0
+        /* just brilliant... */
+#if defined(WEBSOCKETS_CONTEXT_INFO) && defined(WEBSOCKETS_CIPHER_LIST)
         fd   = (ptrdiff_t)in;
 #else
         fd   = (ptrdiff_t)user;
 #endif
         mask = (int)len;
+#endif
         mrp_debug("disable poll events 0x%x for fd %d", mask, fd);
         if (mod_fd(ctx, fd, mask, TRUE))
             return LWS_EVENT_OK;
index 2f231416f4b7354fbe683f697c04044f05975319..4eae50372e812fa958ba91e67b8d476efa8de2b9 100644 (file)
@@ -64,6 +64,7 @@
 #define RSET_REQUEST                "request"
 #define RSET_RELEASE                "release"
 #define RSET_DELETE                 "delete"
+#define RSET_RELEASING              "releasing"
 
 #define RESOURCE_SET_PROPERTY       "setProperty"
 #define RESOURCE_GET_PROPERTIES     "getProperties"
@@ -753,6 +754,11 @@ static void event_cb(uint32_t request_id, mrp_resource_set_t *set, void *data)
     mrp_log_info("Event for %s: grant 0x%08x, advice 0x%08x",
         rset->path, grant, advice);
 
+    if (mrp_get_resource_set_state(rset->set) == mrp_resource_pending_release) {
+        update_property(rset->status_prop, "releasing");
+        return;
+    }
+
     if (!rset->set || !rset->committed) {
 
         struct deferred_rset_data_s *r_data =
@@ -989,6 +995,8 @@ static void destroy_rset(resource_set_o_t *rset)
 
     mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_DELETE,
             rset_cb, ctx);
+    mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_RELEASING,
+            rset_cb, ctx);
     mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_RELEASE,
             rset_cb, ctx);
     mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_REQUEST,
@@ -1903,6 +1911,20 @@ static int rset_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
         mrp_dbus_send_msg(dbus, reply);
         mrp_dbus_msg_unref(reply);
     }
+    else if (strcmp(member, RSET_RELEASING) == 0) {
+
+        mrp_log_info("Releasing cb rset %s", path);
+
+        mrp_resource_set_did_release(rset->set,0);
+
+        reply = mrp_dbus_msg_method_return(dbus, msg);
+
+        if (!reply)
+            goto error;
+
+        mrp_dbus_send_msg(dbus, reply);
+        mrp_dbus_msg_unref(reply);
+    }
     else if (strcmp(member, RSET_SET_PROPERTY) == 0) {
         char *name = NULL;
         char *value = NULL;
@@ -2109,6 +2131,11 @@ static int mgr_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
             destroy_rset(rset);
             goto error_reply;
         }
+        if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+                    RSET_IFACE, RSET_RELEASING, rset_cb, ctx)) {
+            destroy_rset(rset);
+            goto error_reply;
+        }
 
         mrp_htbl_insert(ctx->mgr->rsets, (void *) rset->path, rset);
         update_property(ctx->mgr->rsets_prop, htbl_keys(ctx->mgr->rsets));
index 2e809156425aaf1783e64f01c8083eb556f9bcb7..b3f780bc89894fd58b8b7f63cac2ca68cd1153dd 100644 (file)
@@ -263,6 +263,9 @@ static char *state_to_str(mrp_res_resource_state_t st)
         case MRP_RES_RESOURCE_PENDING:
             state = "pending";
             break;
+        case MRP_RES_RESOURCE_ABOUT_TO_LOOSE:
+            state = "about to loose";
+            break;
     }
     return state;
 }
index 8ae161a8d4c4f2eb285c17a021f2aed9c039d19c..d60fb62106637d26037aa0a8d8036a9409e7bf24 100644 (file)
@@ -66,6 +66,7 @@ bool fetch_resource_set_mask(mrp_msg_t *msg, void **pcursor,
     switch (mask_type) {
     case 0:    expected_tag = RESPROTO_RESOURCE_GRANT;     break;
     case 1:   expected_tag = RESPROTO_RESOURCE_ADVICE;    break;
+    case 2:   expected_tag = RESPROTO_RESOURCE_PENDING;    break;
     default:       /* don't know what to fetch */              return false;
     }
 
@@ -261,6 +262,25 @@ bool fetch_resource_name(mrp_msg_t *msg, void **pcursor,
     return true;
 }
 
+bool fetch_resource_sync_release(mrp_msg_t *msg, void **pcursor,
+                                    bool *psync_release)
+{
+    uint16_t tag;
+    uint16_t type;
+    mrp_msg_value_t value;
+    size_t size;
+
+    if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+        tag != RESPROTO_RESOURCE_SYNC_RELEASE || type != MRP_MSG_FIELD_BOOL)
+    {
+        *psync_release = false;
+        return false;
+    }
+
+    *psync_release = value.bln;
+    return true;
+}
+
 
 static int priv_res_to_mrp_res(uint32_t id, resource_def_t *src, mrp_res_resource_t *dst,
         mrp_res_resource_set_t *set)
@@ -272,6 +292,7 @@ static int priv_res_to_mrp_res(uint32_t id, resource_def_t *src, mrp_res_resourc
 
     dst->priv->server_id = id;
 
+    dst->priv->sync_release = src->sync_release;
     dst->priv->num_attributes = src->num_attrs;
     dst->priv->attrs = src->attrs;
     dst->priv->set = set;
@@ -303,6 +324,10 @@ mrp_res_resource_set_t *resource_query_response(mrp_res_context_t *cx,
         while (fetch_resource_name(msg, pcursor, &rdef[dim].name)) {
             int n_attrs = 0;
 
+            if (!fetch_resource_sync_release(msg, pcursor, &rdef[dim].sync_release)) {
+                goto failed;
+            }
+
             if (!fetch_attribute_array(msg, pcursor, ATTRIBUTE_MAX+1,
                     attrs, &n_attrs))
                 goto failed;
@@ -514,6 +539,38 @@ error:
     return -1;
 }
 
+int did_release_resource_set_request(mrp_res_context_t *cx,
+        mrp_res_resource_set_t *rset)
+{
+    mrp_msg_t *msg = NULL;
+
+    if (!cx->priv->connected)
+        return -1;
+
+    msg = mrp_msg_create(
+            RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, cx->priv->next_seqno,
+            RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16,
+                    RESPROTO_DID_RELEASE_RESOURCE_SET,
+            RESPROTO_RESOURCE_SET_ID, MRP_MSG_FIELD_UINT32, rset->priv->id,
+            RESPROTO_MESSAGE_END);
+
+    if (!msg)
+        return -1;
+
+    rset->priv->seqno = cx->priv->next_seqno;
+    cx->priv->next_seqno++;
+
+    if (!mrp_transport_send(cx->priv->transp, msg))
+        goto error;
+
+    mrp_msg_unref(msg);
+    return 0;
+
+error:
+    mrp_msg_unref(msg);
+    return -1;
+}
+
 
 int create_resource_set_request(mrp_res_context_t *cx,
         mrp_res_resource_set_t *rset)
index 5b372129ec5988bb564f08282170a8e081ed36de..5241b3bb4afba260a1fbadc59b707fecdefc4b70 100644 (file)
@@ -89,4 +89,8 @@ int get_application_classes_request(mrp_res_context_t *cx);
 
 int get_available_resources_request(mrp_res_context_t *cx);
 
+int did_release_resource_set_request(mrp_res_context_t *cx,
+        mrp_res_resource_set_t *rset);
+
+
 #endif
index f4dae1e917978dcd03004b24029255013038ef52..98cc5b2878897c4ff1b7587b45d524097ca4e218 100644 (file)
@@ -60,6 +60,7 @@ typedef enum {
     MRP_RES_RESOURCE_PENDING,
     MRP_RES_RESOURCE_ACQUIRED,
     MRP_RES_RESOURCE_AVAILABLE,
+    MRP_RES_RESOURCE_ABOUT_TO_LOOSE,
 } mrp_res_resource_state_t;
 
 typedef enum {
@@ -266,6 +267,35 @@ bool mrp_res_equal_resource_set(const mrp_res_resource_set_t *a,
  */
 int mrp_res_acquire_resource_set(const mrp_res_resource_set_t *rs);
 
+/**
+ * Prototype for a callback that will be called
+ * when the client is asked to free the resources.
+ * Upon returning from this callback, the client must
+ * stop using the resource and clean up the infrastructure
+ * related to it, e.g. close opened devices.
+ *
+ * @param cx murphy connection context.
+ * @param set resource set with the resources you must free.
+ * @param userdata data you gave when setting the release callback.
+ */
+typedef void (*mrp_res_resource_release_callback_t) (mrp_res_context_t *cx,
+                                          const mrp_res_resource_set_t *rs,
+                                                                  void *userdata);
+
+/**
+ * Set the resource release callback.
+ * This callback will be called to ask client to free the resources.
+ *
+ * @param rs resource set to update.
+ * @param release_cb resource release callback.
+ * @param userdata data you want to access in resource release callback.
+ *
+ * @return true if the release callback was set successfully, false - otherwise.
+ */
+bool mrp_res_set_release_callback(const mrp_res_resource_set_t *rs,
+                           mrp_res_resource_release_callback_t  release_cb,
+                                                          void *userdata);
+
 /**
  * Release a resource set. Releasing a set of resources
  * will not stop delivery of Resource callbacks for that
@@ -283,7 +313,6 @@ int mrp_res_acquire_resource_set(const mrp_res_resource_set_t *rs);
  */
 int mrp_res_release_resource_set(mrp_res_resource_set_t *rs);
 
-
 /**
  * Get a resource set unique server-side id. The id information is
  * normally available only after mrp_res_acquire_resource_set or
index 18ec44b076d39384885c9adb81bc538e67ac4e66..2466921f950436d50fec84503bd809966ad4b797 100644 (file)
@@ -62,6 +62,7 @@ typedef struct {
 
 typedef struct {
     const char *name;
+    bool sync_release;
     int num_attrs;
     mrp_res_attribute_t *attrs;
 } resource_def_t;
@@ -75,6 +76,7 @@ struct mrp_res_resource_private_s {
     mrp_res_resource_t *pub; /* composition */
     mrp_res_resource_set_t *set; /* owning set */
 
+    bool sync_release;
     bool mandatory;
     bool shared;
     int num_attributes;
@@ -93,7 +95,9 @@ struct mrp_res_resource_set_private_s {
     bool autorelease;
 
     mrp_res_resource_callback_t cb;
+    mrp_res_resource_release_callback_t release_cb;
     void *user_data;
+    void *release_cb_user_data;
 
     uint32_t num_resources;
     mrp_res_resource_t **resources;
index 57a18b0f7d68669d7bc487d438ba544fde42f0b6..20ba61a1c3e6e69d2348dd2e85022e7e419ee14d 100644 (file)
@@ -89,7 +89,7 @@ static void resource_event(mrp_msg_t *msg,
         void **pcursor)
 {
     uint32_t rset_id;
-    uint32_t grant, advice;
+    uint32_t grant, advice, pending;
     mrp_resproto_state_t state;
     uint16_t tag;
     uint16_t type;
@@ -102,13 +102,15 @@ static void resource_event(mrp_msg_t *msg,
     uint32_t mask, all = 0x0, mandatory = 0x0;
     uint32_t i;
     mrp_res_resource_set_t *rset;
+    bool should_release_resource;
 
     mrp_res_info("Resource event (request no %u):", seqno);
 
     if (!fetch_resource_set_id(msg, pcursor, &rset_id) ||
         !fetch_resource_set_state(msg, pcursor, &state) ||
         !fetch_resource_set_mask(msg, pcursor, 0, &grant) ||
-        !fetch_resource_set_mask(msg, pcursor, 1, &advice)) {
+        !fetch_resource_set_mask(msg, pcursor, 1, &advice) ||
+        !fetch_resource_set_mask(msg, pcursor, 2, &pending)) {
         mrp_res_error("failed to fetch data from message");
         goto ignore;
     }
@@ -197,6 +199,9 @@ static void resource_event(mrp_msg_t *msg,
         if (grant & mask) {
             res->state = MRP_RES_RESOURCE_ACQUIRED;
         }
+        else if (pending & mask) {
+            res->state = MRP_RES_RESOURCE_ABOUT_TO_LOOSE;
+        }
         else {
             res->state = MRP_RES_RESOURCE_LOST;
         }
@@ -205,7 +210,12 @@ static void resource_event(mrp_msg_t *msg,
     mrp_res_info("advice = 0x%08x, grant = 0x%08x, mandatory = 0x%08x, all = 0x%08x",
             advice, grant, mandatory, all);
 
-    if (grant) {
+    should_release_resource = false;
+    if (pending) {
+        rset->state = MRP_RES_RESOURCE_ABOUT_TO_LOOSE;
+        should_release_resource = true;
+    }
+    else if (grant) {
         rset->state = MRP_RES_RESOURCE_ACQUIRED;
     }
     else if (advice == mandatory) {
@@ -224,10 +234,19 @@ static void resource_event(mrp_msg_t *msg,
     print_resource_set(rset);
 #endif
     if (!rset->priv->seqno) {
-        if (rset->priv->cb) {
-            increase_ref(cx, rset);
+        increase_ref(cx, rset);
+        if (should_release_resource) {
+            if (rset->priv->release_cb) {
+                rset->priv->release_cb(cx, rset, rset->priv->release_cb_user_data);
+            }
+        }
+        else if (rset->priv->cb) {
             rset->priv->cb(cx, rset, rset->priv->user_data);
-            decrease_ref(cx, rset);
+        }
+        decrease_ref(cx, rset);
+
+        if (should_release_resource) {
+            did_release_resource_set_request(cx, rset);
         }
     }
 
@@ -362,6 +381,21 @@ static void recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
 
             break;
         }
+        case RESPROTO_DID_RELEASE_RESOURCE_SET:
+        {
+            mrp_res_resource_set_t *rset;
+            mrp_res_info("received DID_RELEASE_RESOURCE_SET response");
+
+            rset = acquire_resource_set_response(msg, cx, &cursor);
+
+            if (!rset) {
+                goto error;
+            }
+
+            rset->priv->seqno = 0;
+
+            break;
+        }
         case RESPROTO_RESOURCES_EVENT:
             mrp_res_info("received RESOURCES_EVENT response");
 
index 1c788b8f1a2145a19405f76632c08273c5cdb7ff..c1021ba52e83b003fd6ad3ca98efe774ce36d644 100644 (file)
@@ -50,6 +50,9 @@ static char *state_to_str(mrp_res_resource_state_t st)
         case MRP_RES_RESOURCE_PENDING:
             state = "pending";
             break;
+        case MRP_RES_RESOURCE_ABOUT_TO_LOOSE:
+            state = "about to loose";
+            break;
     }
     return state;
 }
@@ -443,6 +446,8 @@ static int update_library_resource_set(mrp_res_context_t *cx,
     rset->priv->resources = resources;
     rset->priv->num_resources = num_resources;
     rset->priv->autorelease = original->priv->autorelease;
+    rset->priv->release_cb = original->priv->release_cb;
+    rset->priv->release_cb_user_data = original->priv->release_cb_user_data;
 
     return 0;
 
@@ -516,6 +521,7 @@ mrp_res_resource_t *mrp_res_create_resource(
         goto error;
 
     res->priv->server_id = server_id;
+    res->priv->sync_release = proto->priv->sync_release;
     res->priv->mandatory = mandatory;
     res->priv->shared = shared;
     res->priv->pub = res;
@@ -580,7 +586,6 @@ const mrp_res_resource_set_t * mrp_res_list_resources(
     return cx->priv->master_resource_set;
 }
 
-
 int mrp_res_release_resource_set(mrp_res_resource_set_t *original)
 {
     mrp_res_resource_set_t *internal_set = NULL;
@@ -787,11 +792,33 @@ bool mrp_res_set_autorelease(bool status,
     return TRUE;
 }
 
+bool mrp_res_set_release_callback(const mrp_res_resource_set_t *rs,
+                           mrp_res_resource_release_callback_t  release_cb,
+                                                          void *userdata)
+{
+    bool result = false;
+    mrp_res_resource_t *resource;
+    uint32_t i;
 
-int mrp_res_acquire_resource_set(
-                const mrp_res_resource_set_t *original)
+    if (rs && rs->state == MRP_RES_RESOURCE_PENDING) {
+        for (i = 0; i < rs->priv->num_resources; i++) {
+            resource = rs->priv->resources[i];
+            if (resource->priv->sync_release) {
+                result = true;
+                rs->priv->release_cb = release_cb;
+                rs->priv->release_cb_user_data = userdata;
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+static mrp_res_resource_set_t *acquire_resource_set(const mrp_res_resource_set_t *original)
 {
-    mrp_res_resource_set_t *rset;
+    mrp_res_resource_set_t *rset = NULL;
+
     mrp_res_context_t *cx = original->priv->cx;
 
     if (!cx->priv->connected) {
@@ -799,6 +826,15 @@ int mrp_res_acquire_resource_set(
         goto error;
     }
 
+    if (!original->priv->release_cb) {
+        for (uint32_t i = 0; i < original->priv->num_resources; i++) {
+            if (original->priv->resources[i]->priv->sync_release) {
+                mrp_res_error("release callback is not set");
+                goto error;
+            }
+        }
+    }
+
     rset = mrp_htbl_lookup(cx->priv->internal_rset_mapping,
             u_to_p(original->priv->internal_id));
 
@@ -826,7 +862,7 @@ int mrp_res_acquire_resource_set(
         }
         else {
             /* re-acquire a lost or released set */
-            return acquire_resource_set_request(cx, rset);
+            acquire_resource_set_request(cx, rset);
         }
     }
     else {
@@ -860,11 +896,23 @@ int mrp_res_acquire_resource_set(
         }
     }
 
-    return 0;
+    return rset;
 
 error:
     mrp_log_error("error acquiring a resource set");
-    return -1;
+    return NULL;
+}
+
+int mrp_res_acquire_resource_set(
+                const mrp_res_resource_set_t *original)
+{
+    int result = -1;
+
+    if (acquire_resource_set(original)) {
+        result = 0;
+    }
+
+    return result;
 }
 
 
index 13afd55dea846cb1d86da5e9272f5911c6cdabb8..9eb2f5c52f493bd9de5d3165a59fed2c3fb7dd0c 100644 (file)
@@ -153,7 +153,6 @@ static void print_zones_cb(mrp_console_t *c, void *user_data,
         for (i = 0;  zone_names[i];  i++)
             printf("   %s\n", zone_names[i]);
 
-
         mrp_free(zone_names);
     }
 }
@@ -409,6 +408,7 @@ static void query_resources_request(client_t *client, mrp_msg_t *req)
     mrp_plugin_t     *plugin = data->plugin;
     const char      **names;
     mrp_attr_t       *attrs;
+    bool              sync_release;
     mrp_attr_t        buf[ATTRIBUTE_MAX];
     uint32_t          resid;
 
@@ -421,8 +421,10 @@ static void query_resources_request(client_t *client, mrp_msg_t *req)
             for (resid = 0;   names[resid];   resid++) {
                 attrs = mrp_resource_definition_read_all_attributes(
                                                     resid, ATTRIBUTE_MAX, buf);
+                sync_release = mrp_resource_definition_get_sync_release(resid);
 
                 if (!PUSH(req, RESOURCE_NAME, STRING, names[resid]) ||
+                    !PUSH(req, RESOURCE_SYNC_RELEASE, BOOL, sync_release) ||
                     !write_attributes(req, attrs))
                     goto failed;
             }
@@ -760,6 +762,38 @@ static void acquire_resource_set_request(client_t *client, mrp_msg_t *req,
         mrp_resource_set_release(rset, seqno);
 }
 
+static void did_release_resource_set_request(client_t *client, mrp_msg_t *req,
+                                             uint32_t seqno, void **pcurs)
+{
+    uint16_t            tag;
+    uint16_t            type;
+    size_t              size;
+    mrp_msg_value_t     value;
+    uint32_t            rset_id;
+    mrp_resource_set_t *rset;
+
+    MRP_ASSERT(client, "invalid argument");
+    MRP_ASSERT(client->rscli, "confused with data structures");
+
+    if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+        tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
+    {
+        reply_with_status(client, req, EINVAL);
+        return;
+    }
+
+    rset_id = value.u32;
+
+    if (!(rset = mrp_resource_client_find_set(client->rscli, rset_id))) {
+        reply_with_status(client, req, ENOENT);
+        return;
+    }
+
+    reply_with_status(client, req, 0);
+
+    mrp_resource_set_did_release(rset, seqno);
+}
+
 static void connection_evt(mrp_transport_t *listen, void *user_data)
 {
     static uint32_t  id;
@@ -891,6 +925,10 @@ static void recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
         acquire_resource_set_request(client, msg, seqno, false, &cursor);
         break;
 
+    case RESPROTO_DID_RELEASE_RESOURCE_SET:
+        did_release_resource_set_request(client, msg, seqno, &cursor);
+        break;
+
     default:
         mrp_log_warning("%s: unsupported request type %d",
                         plugin->instance, reqtyp);
@@ -919,6 +957,7 @@ static void resource_event_handler(uint32_t reqid, mrp_resource_set_t *rset,
     uint16_t            state;
     mrp_resource_mask_t grant;
     mrp_resource_mask_t advice;
+    mrp_resource_mask_t pending;
     mrp_resource_mask_t mask;
     mrp_resource_mask_t all;
     mrp_msg_t          *msg;
@@ -937,6 +976,7 @@ static void resource_event_handler(uint32_t reqid, mrp_resource_set_t *rset,
     id     = mrp_get_resource_set_id(rset);
     grant  = mrp_get_resource_set_grant(rset);
     advice = mrp_get_resource_set_advice(rset);
+    pending = mrp_get_resource_set_pending_release(rset);
 
     if (mrp_get_resource_set_state(rset) == mrp_resource_acquire)
         state = RESPROTO_ACQUIRE;
@@ -949,6 +989,7 @@ static void resource_event_handler(uint32_t reqid, mrp_resource_set_t *rset,
                          FIELD( RESOURCE_STATE , UINT16, state  ),
                          FIELD( RESOURCE_GRANT , UINT32, grant  ),
                          FIELD( RESOURCE_ADVICE, UINT32, advice ),
+                         FIELD( RESOURCE_PENDING, UINT32, pending ),
                          RESPROTO_MESSAGE_END                   );
 
     if (!msg)
index 36d062f77f682ca1162e1211adeab461ddf0e923..3fd5f9a0cae98134c8a56bcd41f70ab85523f521 100644 (file)
@@ -220,6 +220,7 @@ static void query_resources(wrt_client_t *c, mrp_json_t *req)
     mrp_attr_t  *attrs, *a;
     mrp_attr_t   buf[ATTRIBUTE_MAX];
     uint32_t     id;
+    bool         sync_release;
 
     if (!mrp_json_get_integer(req, "seq", &seq)) {
         ignore_invalid_request(c, req, "missing 'seq' field");
@@ -253,6 +254,10 @@ static void query_resources(wrt_client_t *c, mrp_json_t *req)
         if (!mrp_json_add_string (r, "name", resources[id]))
             goto fail;
 
+        sync_release = mrp_resource_definition_get_sync_release(id);
+        if (!mrp_json_add_boolean(r, "sync_release", sync_release))
+            goto fail;
+
         attrs = mrp_resource_definition_read_all_attributes(id,
                                                             ATTRIBUTE_MAX, buf);
 
@@ -605,7 +610,7 @@ static void emit_resource_set_event(wrt_client_t *c, uint32_t reqid,
     mrp_json_t     *msg, *rarr, *r;
     int             rsid;
     const char     *state;
-    int             grant, advice, all, mask;
+    int             grant, pending_release, pending_acquire, advice, all, mask;
     errbuf_t        e;
     mrp_resource_t *res;
     void           *it;
@@ -619,14 +624,21 @@ static void emit_resource_set_event(wrt_client_t *c, uint32_t reqid,
         return;
     }
 
-    if (mrp_get_resource_set_state(rset) == mrp_resource_acquire)
+    pending_release = (int)mrp_get_resource_set_pending_release(rset);
+    pending_acquire = (int)mrp_get_resource_set_pending_acquire(rset);
+
+    if (pending_acquire && !pending_release) {
+        mrp_debug("not emitting event for resource set that is pending acquisition");
+        return;
+    }
+    else if (mrp_get_resource_set_state(rset) == mrp_resource_acquire)
         state = RESWRT_STATE_GRANTED;
     else
         state = RESWRT_STATE_RELEASE;
 
-    rsid   = (int)mrp_get_resource_set_id(rset);
-    grant  = (int)mrp_get_resource_set_grant(rset);
-    advice = (int)mrp_get_resource_set_advice(rset);
+    rsid    = (int)mrp_get_resource_set_id(rset);
+    grant   = (int)mrp_get_resource_set_grant(rset);
+    advice  = (int)mrp_get_resource_set_advice(rset);
 
     msg = alloc_reply(type, seq);
 
@@ -635,10 +647,11 @@ static void emit_resource_set_event(wrt_client_t *c, uint32_t reqid,
 
     rarr = r = NULL;
 
-    if (mrp_json_add_integer(msg, "id"    , rsid ) &&
-        mrp_json_add_string (msg, "state" , state) &&
-        mrp_json_add_integer(msg, "grant" , grant) &&
-        mrp_json_add_integer(msg, "advice", advice)) {
+    if (mrp_json_add_integer(msg, "id"      , rsid   ) &&
+        mrp_json_add_string (msg, "state"   , state  ) &&
+        mrp_json_add_integer(msg, "grant"   , grant  ) &&
+        mrp_json_add_integer(msg, "pending" , pending_release) &&
+        mrp_json_add_integer(msg, "advice"  , advice)) {
 
         all = grant | advice;
         it  = NULL;
@@ -969,6 +982,42 @@ static void release_set(wrt_client_t *c, mrp_json_t *req)
 }
 
 
+static void did_release_set(wrt_client_t *c, mrp_json_t *req)
+{
+    const char         *type = RESWRT_DID_RELEASE_SET;
+    int                 seq;
+    mrp_json_t         *reply;
+    mrp_resource_set_t *rset;
+    uint32_t            rsid;
+
+    if (!mrp_json_get_integer(req, "seq", &seq)) {
+        ignore_invalid_request(c, req, "missing 'seq' field");
+        return;
+    }
+
+    /* get resource set id */
+    if (!mrp_json_get_integer(req, "id", &rsid)) {
+        error_reply(c, type, seq, EINVAL, "missing id");
+        return;
+    }
+
+    rset = mrp_resource_client_find_set(c->rsc, rsid);
+
+    if (rset != NULL) {
+        reply = alloc_reply(type, seq);
+
+        if (reply != NULL) {
+            if (mrp_json_add_integer(reply, "status", 0))
+                send_message(c, reply);
+        }
+
+        mrp_resource_set_did_release(rset, (uint32_t)seq);
+    }
+    else
+        error_reply(c, type, seq, ENOENT, "resource set %d not found", rsid);
+}
+
+
 static wrt_client_t *create_client(wrt_data_t *data, mrp_transport_t *lt)
 {
     wrt_client_t *c;
@@ -1098,6 +1147,8 @@ static void recv_evt(mrp_transport_t *t, void *data, void *user_data)
             acquire_set(c, req);
         else if (!strcmp(type, RESWRT_RELEASE_SET))
             release_set(c, req);
+        else if (!strcmp(type, RESWRT_DID_RELEASE_SET))
+            did_release_set(c, req);
         else
             ignore_unknown_request(c, req, type);
     }
index a739e6b8081ec5c068ebe3f9ac419606aae93cee..75e8abcd0d03610e98a3f1cc4295ffecb44bcc4f 100644 (file)
@@ -196,6 +196,25 @@ WrtResourceManager.prototype.sckmessage = function (message) {
 }
 
 
+/** Map resource definitions to names. */
+WrtResourceManager.prototype.map_resources_by_name = function (resources) {
+    if (this.resource_by_name) {
+        return;
+    }
+
+    this.resource_by_name = {};
+
+    for (var i in resources) {
+        var resource = resources[i];
+        var name = resource.name;
+        this.resource_by_name[name] = {
+            sync_release: resource.sync_release,
+            attributes: resource.attributes,
+        };
+    }
+}
+
+
 /** Resource set constructor. */
 function WrtResourceSet (mgr, reqno) {
     this.manager = mgr;
@@ -213,12 +232,20 @@ WrtResourceSet.prototype.notify = function (msg) {
         if (!this.resources)
             this.resources = msg.resources;
 
-        this.state  = msg.state;
-        this.grant  = msg.grant;
-        this.advice = msg.advice;
+        this.state   = msg.state;
+        this.grant   = msg.grant;
+        this.pending = msg.pending;
+        this.advice  = msg.advice;
 
-        if (this.onstatechanged)
-            this.onstatechanged(this.grant);
+        if (this.pending) {
+            if (this.onrelease) {
+                this.onrelease(this.grant);
+            }
+            this.didRelease();
+        }
+        else if (this.onstatechanged) {
+            this.onstatechanged(this.pending);
+        }
     }
     else if (type == 'create') {
         var status = msg.status;
@@ -243,6 +270,12 @@ WrtResourceSet.prototype.notify = function (msg) {
 }
 
 
+/** The resource set was freed by client. */
+WrtResourceSet.prototype.didRelease = function () {
+    this.manager.send_request({ type: 'did_release', id: this.id });
+}
+
+
 /** Map resources to names. */
 WrtResourceSet.prototype.ensure_resource_map = function () {
     var r;
@@ -265,6 +298,24 @@ WrtResourceSet.prototype.ensure_resource_map = function () {
 }
 
 
+/** Check if the resource set release callback is present if it is needed. */
+WrtResourceSet.prototype.isReleaseCallbackOK = function () {
+    var result = true;
+
+    if (!this.onrelease) {
+        for (var i in this.resources) {
+            var name = this.resources[i].name;
+            if (this.manager.resource_by_name[name].sync_release) {
+                result = false;
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+
 /** Pending request constructor. */
 function WrtPendingRequest (mgr, reqno) {
     this.manager = mgr;
@@ -302,6 +353,11 @@ WrtPendingRequest.prototype.notify = function (msg) {
     if (status == 0) {
         if (this.onsuccess && (m = evtmap[msg.type])) {
             event = m.map ? m.map(msg[m.field]) : msg[m.field];
+
+            if ('resources' == m.field) {
+                this.manager.map_resources_by_name(msg.resources);
+            }
+
             this.onsuccess(event);
         }
     }
@@ -450,7 +506,12 @@ WrtResourceManager.prototype.socketUri = function (http_uri) {
 
 /** Acquire the resource set. */
 WrtResourceSet.prototype.acquire = function () {
-    this.manager.send_request({ type: 'acquire', id: this.id });
+    if (this.isReleaseCallbackOK()) {
+        this.manager.send_request({ type: 'acquire', id: this.id });
+    }
+    else {
+        throw new WrtResourceError("Release callback is not set when it should be.");
+    }
 }
 
 
@@ -560,6 +621,19 @@ WrtResourceSet.prototype.isGranted = function (name) {
 }
 
 
+/** Check if the named resource is pending for release/acqusition. */
+WrtResourceSet.prototype.isPending = function (name) {
+    this.ensure_resource_map();
+
+    r = this.resource_by_name[name];
+
+    if (this.pending & r.mask)
+        return true;
+    else
+        return false;
+}
+
+
 /** Check if the named resource can be allocated. */
 WrtResourceSet.prototype.isAllocable = function (name) {
     this.ensure_resource_map();
index f87323bb1981e3bc1ce5e50fbccf05b78c64e892..3859a076e0026320f53a162f30d7bfda08f979f6 100644 (file)
@@ -150,6 +150,20 @@ function managerConnected () {
         rset.onsuccess      = resourceSetCreated;
         rset.onerror        = resourceSetFailed;
         rset.onstatechanged = resourceSetEvent;
+        rset.onrelease      = function (grant) {
+            var names = this.getResourceNames();
+            for (var i in names) {
+                if (this.isPending(names[i])) {
+                    console.log("Resource '" + names[i] + "' must be released.");
+                }
+            }
+
+            window.alert("Resource must be released!!");
+
+            if (this.isPending('audio_playback')) {
+                stopAudio();
+            }
+        };
     }
     catch (e) {
         console.log("Query failed: " +
index f0df6722a73e3ae96b31b666a8851ca748c33e5f..dede7b70b9af1adb6d95e1d530d08a59bc6dddd9 100644 (file)
@@ -38,6 +38,7 @@
 #define RESWRT_DESTROY_SET     "destroy"
 #define RESWRT_ACQUIRE_SET     "acquire"
 #define RESWRT_RELEASE_SET     "release"
+#define RESWRT_DID_RELEASE_SET "did_release"
 
 #define RESWRT_EVENT           "event"
 #define RESWRT_STATE_GRANTED   "acquire"
index 5f56dfaac7ec2c45c445d72fdc386711d00e0240..00b65549d5ab5db4a545418bb0582cb949f009fb 100644 (file)
@@ -51,6 +51,7 @@ mrp_attr_t *
 mrp_resource_definition_read_all_attributes(uint32_t resource_id,
                                             uint32_t buflen,
                                             mrp_attr_t *buf);
+bool mrp_resource_definition_get_sync_release(uint32_t resource_id);
 
 const char **mrp_application_class_get_all_names(uint32_t buflen,
                                                  const char **buf);
@@ -68,6 +69,7 @@ mrp_resource_set_t *mrp_resource_set_create(mrp_resource_client_t *client,
                                             void *user_data);
 void mrp_resource_set_destroy(mrp_resource_set_t *resource_set);
 
+
 uint32_t mrp_get_resource_set_id(mrp_resource_set_t *resource_set);
 
 mrp_resource_state_t
@@ -79,6 +81,12 @@ mrp_get_resource_set_grant(mrp_resource_set_t *resource_set);
 mrp_resource_mask_t
 mrp_get_resource_set_advice(mrp_resource_set_t *resource_set);
 
+mrp_resource_mask_t
+mrp_get_resource_set_pending_release(mrp_resource_set_t *resource_set);
+
+mrp_resource_mask_t
+mrp_get_resource_set_pending_acquire(mrp_resource_set_t *resource_set);
+
 mrp_resource_client_t *
 mrp_get_resource_set_client(mrp_resource_set_t *resource_set);
 
@@ -111,6 +119,9 @@ void mrp_resource_set_acquire(mrp_resource_set_t *resource_set,
 void mrp_resource_set_release(mrp_resource_set_t *resource_set,
                               uint32_t request_id);
 
+void mrp_resource_set_did_release(mrp_resource_set_t *resource_set,
+                                  uint32_t request_id);
+
 mrp_resource_t *
 mrp_resource_set_iterate_resources(mrp_resource_set_t *resource_set,void **it);
 
index 5a0a5d72ca41bb59201313706eb51a9bfeb4383e..25b8534384b302813ccade7c0d6db06cd6ce789a 100644 (file)
@@ -79,6 +79,7 @@ enum field_e {
     PRIORITY,
     SHAREABLE,
     MANDATORY,
+    SYNC_RELEASE,
     MODAL,
     SHARE,
     GRANT,
@@ -1031,6 +1032,7 @@ static int resclass_create_from_lua(lua_State *L)
     const char *name = NULL;
     mrp_attr_def_t *attrs = NULL;
     bool shareable = false;
+    bool sync_release = false;
     mrp_resource_mgr_ftbl_t *ftbl = NULL;
     void *mgrdata = NULL;
     attr_def_t *adef;
@@ -1051,6 +1053,13 @@ static int resclass_create_from_lua(lua_State *L)
             shareable = lua_toboolean(L, -1);
             break;
 
+        case SYNC_RELEASE:
+            luaL_argcheck(L, lua_isboolean(L,-1), 2, "attempt to assign "
+                          "non-boolean value to 'sync_release' field");
+            sync_release = lua_toboolean(L, -1);
+            break;
+
+
         case ATTRIBUTES:
             attrs = check_attrdefs(L, -1, &nattr);
             break;
@@ -1064,7 +1073,7 @@ static int resclass_create_from_lua(lua_State *L)
     if (!name)
         luaL_error(L, "missing or wrong name field");
 
-    id = mrp_resource_definition_create(name, shareable, attrs,ftbl,mgrdata);
+    id = mrp_resource_definition_create_with_sync_release(name, shareable, sync_release, attrs,ftbl,mgrdata);
 
     MRP_ASSERT(id < MRP_RESOURCE_MAX, "resource id is out of range");
 
@@ -1107,10 +1116,11 @@ static int resclass_getfield(lua_State *L)
         lua_pushnil(L);
     else {
         switch (fld) {
-        case NAME:       lua_pushstring(L, rd->name);         break;
-        case ID:         lua_pushinteger(L, rd->id + 1);      break;
-        case SHAREABLE:  lua_pushboolean(L, rd->shareable);   break;
-        default:         lua_pushnil(L);                      break;
+        case NAME:          lua_pushstring(L, rd->name);            break;
+        case ID:            lua_pushinteger(L, rd->id + 1);         break;
+        case SHAREABLE:     lua_pushboolean(L, rd->shareable);      break;
+        case SYNC_RELEASE:  lua_pushboolean(L, rd->sync_release);   break;
+        default:            lua_pushnil(L);                         break;
         }
     }
 
@@ -1717,6 +1727,11 @@ static field_t field_name_to_type(const char *name, size_t len)
             return ATTRIBUTES;
         break;
 
+    case 12:
+        if (!strcmp(name, "sync_release"))
+            return SYNC_RELEASE;
+        break;
+
     default:
         break;
     }
index 7a347a0ea23bee9323185a7acdb2a669f233e70c..ede35b55df3b9e362672e6467e8dc6d256049810 100644 (file)
@@ -85,6 +85,7 @@ enum mrp_resource_state_e {
     mrp_resource_no_request = 0,
     mrp_resource_release,
     mrp_resource_acquire,
+    mrp_resource_pending_release,
 };
 
 
index c91f05418cc660e5a73ba067aaa67f203a1de152..010f8d34f052fe01789520d8421e8c7a531d6e42 100644 (file)
@@ -51,6 +51,12 @@ uint32_t mrp_resource_definition_create(const char *name,
                                         mrp_attr_def_t *attrdefs,
                                         mrp_resource_mgr_ftbl_t *manager,
                                         void *manager_data);
+uint32_t mrp_resource_definition_create_with_sync_release(const char *name,
+                                                          bool shareable,
+                                                          bool sync_release,
+                                                          mrp_attr_def_t *attrdefs,
+                                                          mrp_resource_mgr_ftbl_t *manager,
+                                                          void *manager_data);
 void mrp_lua_resclass_create_from_c(uint32_t id);
 
 
index fa03484d0d8aaf73e87440b28ba4b60642314fee..44075e8857eab7ca1f144e23c0086d90228cdc90 100644 (file)
 #define RESPROTO_RESOURCE_STATE       RESPROTO_TAG(7)
 #define RESPROTO_RESOURCE_GRANT       RESPROTO_TAG(8)
 #define RESPROTO_RESOURCE_ADVICE      RESPROTO_TAG(9)
-#define RESPROTO_RESOURCE_ID          RESPROTO_TAG(10)
-#define RESPROTO_RESOURCE_NAME        RESPROTO_TAG(11)
-#define RESPROTO_RESOURCE_FLAGS       RESPROTO_TAG(12)
-#define RESPROTO_RESOURCE_PRIORITY    RESPROTO_TAG(13)
-#define RESPROTO_CLASS_NAME           RESPROTO_TAG(14)
-#define RESPROTO_ZONE_NAME            RESPROTO_TAG(15)
-#define RESPROTO_ATTRIBUTE_INDEX      RESPROTO_TAG(16)
-#define RESPROTO_ATTRIBUTE_NAME       RESPROTO_TAG(17)
-#define RESPROTO_ATTRIBUTE_VALUE      RESPROTO_TAG(18)
+#define RESPROTO_RESOURCE_PENDING     RESPROTO_TAG(10)
+
+#define RESPROTO_RESOURCE_ID            RESPROTO_TAG(11)
+#define RESPROTO_RESOURCE_NAME          RESPROTO_TAG(12)
+#define RESPROTO_RESOURCE_FLAGS         RESPROTO_TAG(13)
+#define RESPROTO_RESOURCE_PRIORITY      RESPROTO_TAG(14)
+#define RESPROTO_CLASS_NAME             RESPROTO_TAG(15)
+#define RESPROTO_ZONE_NAME              RESPROTO_TAG(16)
+#define RESPROTO_ATTRIBUTE_INDEX        RESPROTO_TAG(17)
+#define RESPROTO_ATTRIBUTE_NAME         RESPROTO_TAG(18)
+#define RESPROTO_ATTRIBUTE_VALUE        RESPROTO_TAG(19)
+#define RESPROTO_RESOURCE_SYNC_RELEASE  RESPROTO_TAG(20)
 
 typedef enum {
-    RESPROTO_QUERY_RESOURCES,
+    RESPROTO_QUERY_RESOURCES,           // 0
     RESPROTO_QUERY_CLASSES,
     RESPROTO_QUERY_ZONES,
     RESPROTO_CREATE_RESOURCE_SET,
     RESPROTO_DESTROY_RESOURCE_SET,
-    RESPROTO_ACQUIRE_RESOURCE_SET,
+    RESPROTO_ACQUIRE_RESOURCE_SET,      // 5
     RESPROTO_RELEASE_RESOURCE_SET,
+    RESPROTO_DID_RELEASE_RESOURCE_SET,
     RESPROTO_RESOURCES_EVENT,
 } mrp_resproto_request_t;
 
index aa5d5c01166ff7637bed28b13c321610a8273d0e..a441fcd8aacc3bc5a58f8e296a4711574b0ed2b6 100644 (file)
@@ -69,6 +69,7 @@ static mrp_resource_owner_t  resource_owners[MRP_ZONE_MAX * MRP_RESOURCE_MAX];
 static mqi_handle_t          owner_tables[MRP_RESOURCE_MAX];
 
 static mrp_resource_owner_t *get_owner(uint32_t, uint32_t);
+static void restore_owner(mrp_resource_owner_t *owner, mrp_resource_owner_t *oldowner);
 static void reset_owners(uint32_t, mrp_resource_owner_t *);
 static bool grant_ownership(mrp_resource_owner_t *, mrp_zone_t *,
                             mrp_application_class_t *, mrp_resource_set_t *,
@@ -184,7 +185,11 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
     mrp_resource_mask_t mask;
     mrp_resource_mask_t mandatory;
     mrp_resource_mask_t grant;
+    mrp_resource_mask_t old_grant;
     mrp_resource_mask_t advice;
+    mrp_resource_mask_t pending_acquire = 0;
+    mrp_resource_mask_t pending_release = 0;
+    mrp_resource_state_t new_state;
     void *clc, *rsc, *rc;
     uint32_t rid;
     uint32_t rcnt;
@@ -195,6 +200,8 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
     uint32_t replyid;
     uint32_t nevent, maxev;
     event_t *events, *ev, *lastev;
+    bool resource_granted_by_manager;
+    bool owned_by_other;
 
     MRP_ASSERT(zoneid < MRP_ZONE_MAX, "invalid argument");
 
@@ -223,33 +230,73 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
             force_release = false;
             mandatory = rset->resource.mask.mandatory;
             grant = 0;
+            old_grant = rset->resource.mask.grant;
             advice = 0;
+            pending_acquire = 0;
+            pending_release = 0;
             rc = NULL;
 
+            if ((mrp_resource_pending_release == rset->state) && !rset->resource.mask.pending.release) {
+                // Release the resource that was pending to be released.
+
+                if (!rset->resource.mask.grant && rset->auto_release.current) {
+                    rset->state = mrp_resource_release;
+                    rset->auto_release.current = rset->auto_release.client;
+                }
+                else {
+                    rset->state = mrp_resource_acquire;
+                }
+            }
+
             switch (rset->state) {
 
             case mrp_resource_acquire:
                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
+                    resource_granted_by_manager = false;
+
                     rdef  = res->def;
                     rid   = rdef->id;
                     owner = get_owner(zoneid, rid);
 
+                    mask = (mrp_resource_mask_t)1 << rid;
+
                     backup[rid] = *owner;
 
-                    if (grant_ownership(owner, zone, class, rset, res))
+                    /*
+                     * Here we first check if the resource is owned by some other client.
+                     * If not - we will grant it as ordinary,
+                     * otherwise - we will mark it as pending.
+                     */
+                    old = oldowners + rid;
+                    owned_by_other = old->rset && old->rset != rset &&
+                        ((old->rset->state == mrp_resource_acquire) ||
+                         (old->rset->state == mrp_resource_pending_release && old->rset->resource.mask.pending.release));
+
+                    resource_granted_by_manager = grant_ownership(owner, zone, class, rset, res);
+                    if (resource_granted_by_manager)
                         grant |= ((mrp_resource_mask_t)1 << rid);
-                    else {
-                        if (owner->rset != rset)
-                            force_release |= owner->modal;
+                    else if (owner->rset != rset) {
+                        force_release |= owner->modal;
+                    }
+
+
+                    if (rdef->sync_release && (old_grant & mask) && !(grant & mask)) {
+                        pending_release |= mask;
+                    }
+
+                    if (rdef->sync_release && (grant & mask) && owned_by_other) {
+                        pending_acquire |= mask;
                     }
                 }
                 owners = get_owner(zoneid, 0);
+
+
                 if ((grant & mandatory) == mandatory &&
-                    mrp_resource_lua_veto(zone, rset, owners, grant, reqset))
-                {
+                    mrp_resource_lua_veto(zone, rset, owners, grant, reqset)) {
                     advice = grant;
                 }
                 else {
+
                     /* rollback, ie. restore the backed up state */
                     rc = NULL;
                     while ((res=mrp_resource_set_iterate_resources(rset,&rc))){
@@ -260,8 +307,9 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
                         *owner = backup[rid];
 
                         if ((grant & mask)) {
-                            if ((ftbl = rdef->manager.ftbl) && ftbl->free)
+                            if ((ftbl = rdef->manager.ftbl) && ftbl->free) {
                                 ftbl->free(zone, res, rdef->manager.userdata);
+                            }
                         }
 
                         if (advice_ownership(owner, zone, class, rset, res))
@@ -275,7 +323,8 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
 
                     mrp_resource_lua_set_owners(zone, owners);
                 }
-                break;
+                break; // mrp_resource_acquire
+
 
             case mrp_resource_release:
                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
@@ -290,6 +339,13 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
                     advice = 0;
                 break;
 
+            case mrp_resource_pending_release:
+                // Preserve the resource state while it is being freed by the client
+                grant = rset->resource.mask.grant;
+                pending_release = rset->resource.mask.pending.release;
+                pending_acquire = rset->resource.mask.pending.acquire;
+                break;
+
             default:
                 break;
             }
@@ -299,47 +355,61 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
             notify  = 0;
             replyid = (reqset == rset && reqid == rset->request.id) ? reqid:0;
 
+            new_state = rset->state;
 
             if (force_release) {
                 move = (rset->state != mrp_resource_release);
                 notify = move ? MRP_RESOURCE_EVENT_RELEASE : 0;
                 changed = move || rset->resource.mask.grant;
-                rset->state = mrp_resource_release;
                 rset->resource.mask.grant = 0;
+                new_state = mrp_resource_release;
+            }
+            else if (!pending_release && grant && pending_acquire && (mrp_resource_acquire == rset->state)) {
+                /* Resource is pending for acquisition.
+                 * It will be actually granted when the current owner frees it.
+                 * No event is emitted at this point. */
+                rset->resource.mask.grant = 0;
+                replyid = 0;
             }
             else {
                 if (grant == rset->resource.mask.grant) {
-                    if (rset->state == mrp_resource_acquire &&
-                        !grant && rset->dont_wait.current)
-                    {
-                        rset->state = mrp_resource_release;
-                        rset->dont_wait.current = rset->dont_wait.client;
+                    if (rset->state == mrp_resource_acquire && !grant) {
+                        if (pending_acquire != rset->resource.mask.pending.acquire && !pending_acquire) {
+                            changed = true;
+                        }
+                        else if (rset->dont_wait.current) {
+                            new_state = mrp_resource_pending_release;
+                            rset->dont_wait.current = rset->dont_wait.client;
 
-                        notify = MRP_RESOURCE_EVENT_RELEASE;
-                        move = true;
+                            notify = MRP_RESOURCE_EVENT_RELEASE;
+                        }
                     }
                 }
                 else {
                     rset->resource.mask.grant = grant;
                     changed = true;
 
-                    if (rset->state != mrp_resource_release &&
-                        !grant && rset->auto_release.current)
-                    {
-                        rset->state = mrp_resource_release;
-                        rset->auto_release.current = rset->auto_release.client;
-
+                    if (rset->state == mrp_resource_acquire && pending_release) {
                         notify = MRP_RESOURCE_EVENT_RELEASE;
-                        move = true;
+                        new_state = mrp_resource_pending_release;
                     }
                 }
+
+                if (!grant && new_state == mrp_resource_acquire && rset->auto_release.current) {
+                    new_state = mrp_resource_release;
+                    rset->auto_release.current = rset->auto_release.client;
+                }
             }
+            rset->state = new_state;
+
+            rset->resource.mask.pending.acquire = pending_acquire;
+            rset->resource.mask.pending.release = pending_release;
 
             if (notify) {
                 mrp_resource_set_notify(rset, notify);
             }
 
-            if (advice != rset->resource.mask.advice) {
+            if (advice != rset->resource.mask.advice && !pending_release && !pending_acquire) {
                 rset->resource.mask.advice = advice;
                 changed = true;
             }
@@ -354,6 +424,15 @@ void mrp_resource_owner_update_zone(uint32_t zoneid,
         } /* while rset */
     } /* while class */
 
+    for (rid = 0; rid < rcnt; rid++) {
+        owner = get_owner(zoneid, rid);
+
+        if (owner->rset && owner->rset->resource.mask.pending.acquire) {
+            old = oldowners + rid;
+            restore_owner(owner, old);
+        }
+    }
+
     manager_end_transaction(zone);
 
     for (lastev = (ev = events) + nevent;     ev < lastev;     ev++) {
@@ -476,6 +555,11 @@ static mrp_resource_owner_t *get_owner(uint32_t zone, uint32_t resid)
     return resource_owners + (zone * MRP_RESOURCE_MAX + resid);
 }
 
+static void restore_owner(mrp_resource_owner_t *owner, mrp_resource_owner_t *oldowner)
+{
+    memcpy(owner, oldowner, sizeof(mrp_resource_owner_t));
+}
+
 static void reset_owners(uint32_t zone, mrp_resource_owner_t *oldowners)
 {
     mrp_resource_owner_t *owners = get_owner(zone, 0);
index 12e56d70d4bbdad14cbd1806f45be188286b7263..d933e276245a277bd232b9d6cf5d492c09c02011 100644 (file)
@@ -197,6 +197,20 @@ mrp_resource_mask_t mrp_get_resource_set_advice(mrp_resource_set_t *rset)
     return rset->resource.mask.advice;
 }
 
+mrp_resource_mask_t mrp_get_resource_set_pending_release(mrp_resource_set_t *rset)
+{
+    MRP_ASSERT(rset, "invalid argument");
+
+    return rset->resource.mask.pending.release;
+}
+
+mrp_resource_mask_t mrp_get_resource_set_pending_acquire(mrp_resource_set_t *rset)
+{
+    MRP_ASSERT(rset, "invalid argument");
+
+    return rset->resource.mask.pending.acquire;
+}
+
 mrp_resource_client_t *mrp_get_resource_set_client(mrp_resource_set_t *rset)
 {
     MRP_ASSERT(rset, "invalid argument");
@@ -367,6 +381,8 @@ void mrp_resource_set_release(mrp_resource_set_t *rset, uint32_t reqid)
         }
         else {
             rset->state = mrp_resource_release;
+            rset->resource.mask.pending.release = 0;
+            rset->resource.mask.pending.acquire = 0;
             rset->request.id = reqid;
             rset->request.stamp = get_request_stamp();
 
@@ -381,6 +397,32 @@ void mrp_resource_set_release(mrp_resource_set_t *rset, uint32_t reqid)
     }
 }
 
+void mrp_resource_set_did_release(mrp_resource_set_t *rset, uint32_t reqid)
+{
+    mqi_handle_t trh;
+
+    MRP_ASSERT(rset, "invalid argument");
+
+    mrp_debug("releasing resource set #%d", rset->id);
+
+    if (!rset->class.ptr)
+        rset->state = mrp_resource_release;
+    else {
+        rset->state = mrp_resource_pending_release;
+        rset->resource.mask.pending.release = 0;
+        rset->request.id = reqid;
+        rset->request.stamp = get_request_stamp();
+
+        mrp_application_class_move_resource_set(rset);
+
+        mrp_resource_set_notify(rset, MRP_RESOURCE_EVENT_RELEASE);
+
+        trh = mqi_begin_transaction();
+        mrp_resource_owner_update_zone(rset->zone, rset, reqid);
+        mqi_commit_transaction(trh);
+    }
+}
+
 void mrp_resource_set_updated(mrp_resource_set_t *rset)
 {
     mrp_resource_t *res;
index 088aad12744e1e77527c80f0bd8200631b225ba3..02809539b813ddd0737f890dc5cbb461e389546d 100644 (file)
@@ -61,6 +61,10 @@ struct mrp_resource_set_s {
             mrp_resource_mask_t mandatory;
             mrp_resource_mask_t grant;
             mrp_resource_mask_t advice;
+            struct {
+                mrp_resource_mask_t acquire;
+                mrp_resource_mask_t release;
+            } pending;
         } mask;
         mrp_list_hook_t list;
         bool share;
index 25eb8651467f2949212bc1040a5f225dc6ebfdd0..0629c7db77f010fc6f50b9a48654d5fbe88ebe83 100644 (file)
@@ -77,7 +77,7 @@ static mrp_resource_def_t *resource_def_table[RESOURCE_MAX];
 static MRP_LIST_HOOK(manager_list);
 static mqi_handle_t        resource_user_table[RESOURCE_MAX];
 
-static uint32_t add_resource_definition(const char *, bool, uint32_t,
+static uint32_t add_resource_definition(const char *, bool, bool, uint32_t,
                                         mrp_resource_mgr_ftbl_t *, void *);
 
 #if 0
@@ -102,6 +102,17 @@ uint32_t mrp_resource_definition_create(const char *name, bool shareable,
                                         mrp_attr_def_t *attrdefs,
                                         mrp_resource_mgr_ftbl_t *manager,
                                         void *mgrdata)
+{
+    return mrp_resource_definition_create_with_sync_release(name, shareable,
+                                        false, attrdefs,
+                                        manager, mgrdata);
+}
+
+uint32_t mrp_resource_definition_create_with_sync_release(const char *name, bool shareable,
+                                        bool sync_release,
+                                        mrp_attr_def_t *attrdefs,
+                                        mrp_resource_mgr_ftbl_t *manager,
+                                        void *mgrdata)
 {
     uint32_t nattr;
     uint32_t id;
@@ -117,7 +128,7 @@ uint32_t mrp_resource_definition_create(const char *name, bool shareable,
     for (nattr = 0;  attrdefs && attrdefs[nattr].name;  nattr++)
         ;
 
-    id = add_resource_definition(name, shareable, nattr, manager, mgrdata);
+    id = add_resource_definition(name, shareable, sync_release, nattr, manager, mgrdata);
 
     if (id != MRP_RESOURCE_ID_INVALID) {
         def = mrp_resource_definition_find_by_id(id);
@@ -239,6 +250,18 @@ mrp_attr_t *mrp_resource_definition_read_all_attributes(uint32_t resid,
     return retval;
 }
 
+bool mrp_resource_definition_get_sync_release(uint32_t resid)
+{
+    bool result = false;
+
+    mrp_resource_def_t *rdef   = mrp_resource_definition_find_by_id(resid);
+    if (rdef) {
+        result = rdef->sync_release;
+    }
+
+    return result;
+}
+
 
 
 mrp_resource_t *mrp_resource_create(const char *name,
@@ -539,6 +562,7 @@ int mrp_resource_attribute_print(mrp_resource_t *res, char *buf, int len)
 
 static uint32_t add_resource_definition(const char *name,
                                         bool        shareable,
+                                        bool        sync_release,
                                         uint32_t    nattr,
                                         mrp_resource_mgr_ftbl_t *mgrftbl,
                                         void       *mgrdata)
@@ -564,10 +588,11 @@ static uint32_t add_resource_definition(const char *name,
 
     id = resource_def_count++;
 
-    def->id        = id;
-    def->name      = dup_name;
-    def->shareable = shareable;
-    def->nattr     = nattr;
+    def->id           = id;
+    def->name         = dup_name;
+    def->shareable    = shareable;
+    def->sync_release = sync_release;
+    def->nattr        = nattr;
 
     if (mgrftbl) {
         def->manager.ftbl = mrp_alloc(sizeof(mrp_resource_mgr_ftbl_t));
index 3c01fd9f37a384c63f43868168dfb4079ddb2ef5..442d02e4525a52e800164eb114d5e005bf65157b 100644 (file)
@@ -39,6 +39,7 @@ struct mrp_resource_def_s {
     uint32_t            id;
     const char         *name;
     bool                shareable;
+    bool                sync_release;
     struct {
         mrp_list_hook_t list;
         mrp_resource_mgr_ftbl_t *ftbl;