aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabor X Toth <*@tg-x.net>2014-07-27 13:17:03 +0000
committerGabor X Toth <*@tg-x.net>2014-07-27 13:17:03 +0000
commitb63820a52b63d264bead047d0d6f4b76a94c4030 (patch)
treea678202b91694d3582ae61c57a7badac483693d1 /src
parent2f0367bf715c7c0abd12b3e167f329311a35f6e2 (diff)
downloadgnunet-b63820a52b63d264bead047d0d6f4b76a94c4030.tar.gz
gnunet-b63820a52b63d264bead047d0d6f4b76a94c4030.zip
psyc: membership store
Diffstat (limited to 'src')
-rw-r--r--src/include/gnunet_protocols.h7
-rw-r--r--src/include/gnunet_psycstore_service.h57
-rw-r--r--src/psyc/gnunet-service-psyc.c43
-rw-r--r--src/psyc/psyc.h21
-rw-r--r--src/psyc/psyc_api.c26
-rw-r--r--src/psyc/test_psyc.c39
-rw-r--r--src/psycstore/gnunet-service-psycstore.c2
-rw-r--r--src/psycstore/psycstore.h8
-rw-r--r--src/psycstore/psycstore_api.c59
9 files changed, 158 insertions, 104 deletions
diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h
index 4063b2efd..5626e1e5e 100644
--- a/src/include/gnunet_protocols.h
+++ b/src/include/gnunet_protocols.h
@@ -2152,11 +2152,10 @@ extern "C"
2152#define GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION 688 2152#define GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION 688
2153 2153
2154 2154
2155/** C->S: request to remove channel slave from the membership database. */ 2155/** C->S: request to add/remove channel slave in the membership database. */
2156#define GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_ADD 689 2156#define GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE 689
2157 2157
2158/** C->S: request to add channel slave to the membership database */ 2158/* 690 */
2159#define GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_RM 690
2160 2159
2161/** S<--C: PSYC message which contains one or more message parts. */ 2160/** S<--C: PSYC message which contains one or more message parts. */
2162#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE 691 2161#define GNUNET_MESSAGE_TYPE_PSYC_MESSAGE 691
diff --git a/src/include/gnunet_psycstore_service.h b/src/include/gnunet_psycstore_service.h
index 5a9969bce..84d69c24d 100644
--- a/src/include/gnunet_psycstore_service.h
+++ b/src/include/gnunet_psycstore_service.h
@@ -121,18 +121,27 @@ typedef void
121 * Store join/leave events for a PSYC channel in order to be able to answer 121 * Store join/leave events for a PSYC channel in order to be able to answer
122 * membership test queries later. 122 * membership test queries later.
123 * 123 *
124 * @param h Handle for the PSYCstore. 124 * @param h
125 * @param channel_key The channel where the event happened. 125 * Handle for the PSYCstore.
126 * @param slave_key Public key of joining/leaving slave. 126 * @param channel_key
127 * @param did_join #GNUNET_YES on join, #GNUNET_NO on part. 127 * The channel where the event happened.
128 * @param announced_at ID of the message that announced the membership change. 128 * @param slave_key
129 * @param effective_since Message ID this membership change is in effect since. 129 * Public key of joining/leaving slave.
130 * @param did_join
131 * #GNUNET_YES on join, #GNUNET_NO on part.
132 * @param announced_at
133 * ID of the message that announced the membership change.
134 * @param effective_since
135 * Message ID this membership change is in effect since.
130 * For joins it is <= announced_at, for parts it is always 0. 136 * For joins it is <= announced_at, for parts it is always 0.
131 * @param group_generation In case of a part, the last group generation the 137 * @param group_generation
132 * slave has access to. It has relevance when a larger message have 138 * In case of a part, the last group generation the slave has access to.
133 * fragments with different group generations. 139 * It has relevance when a larger message have fragments with different
134 * @param rcb Callback to call with the result of the storage operation. 140 * group generations.
135 * @param rcb_cls Closure for the callback. 141 * @param rcb
142 * Callback to call with the result of the storage operation.
143 * @param rcb_cls
144 * Closure for the callback.
136 * 145 *
137 * @return Operation handle that can be used to cancel the operation. 146 * @return Operation handle that can be used to cancel the operation.
138 */ 147 */
@@ -156,16 +165,22 @@ GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
156 * is also used when handling join requests to determine whether the slave is 165 * is also used when handling join requests to determine whether the slave is
157 * currently admitted to the channel. 166 * currently admitted to the channel.
158 * 167 *
159 * @param h Handle for the PSYCstore. 168 * @param h
160 * @param channel_key The channel we are interested in. 169 * Handle for the PSYCstore.
161 * @param slave_key Public key of slave whose membership to check. 170 * @param channel_key
162 * @param message_id Message ID for which to do the membership test. 171 * The channel we are interested in.
163 * @param group_generation Group generation of the fragment of the message to 172 * @param slave_key
164 * test. It has relevance if the message consists of multiple fragments 173 * Public key of slave whose membership to check.
165 * with different group generations. 174 * @param message_id
166 * FIXME: not needed if there are no overlapping messages. 175 * Message ID for which to do the membership test.
167 * @param rcb Callback to call with the test result. 176 * @param group_generation
168 * @param rcb_cls Closure for the callback. 177 * Group generation of the fragment of the message to test.
178 * It has relevance if the message consists of multiple fragments with
179 * different group generations.
180 * @param rcb
181 * Callback to call with the test result.
182 * @param rcb_cls
183 * Closure for the callback.
169 * 184 *
170 * @return Operation handle that can be used to cancel the operation. 185 * @return Operation handle that can be used to cancel the operation.
171 */ 186 */
diff --git a/src/psyc/gnunet-service-psyc.c b/src/psyc/gnunet-service-psyc.c
index 9d6f12420..e7020bc69 100644
--- a/src/psyc/gnunet-service-psyc.c
+++ b/src/psyc/gnunet-service-psyc.c
@@ -2048,24 +2048,46 @@ client_recv_psyc_message (void *cls, struct GNUNET_SERVER_Client *client,
2048 2048
2049 2049
2050/** 2050/**
2051 * Client requests to add a slave to the membership database. 2051 * Received result of GNUNET_PSYCSTORE_membership_store()
2052 */ 2052 */
2053static void 2053static void
2054client_recv_slave_add (void *cls, struct GNUNET_SERVER_Client *client, 2054store_recv_membership_store_result (void *cls, int64_t result, const char *err_msg)
2055 const struct GNUNET_MessageHeader *msg)
2056{ 2055{
2057 2056 struct GNUNET_MULTICAST_MembershipTestHandle *mth = cls;
2057 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2058 "%p GNUNET_PSYCSTORE_membership_store() returned %" PRId64 " (%s)\n",
2059 mth, result, err_msg);
2060 /* FIXME: send result to client */
2058} 2061}
2059 2062
2060 2063
2061/** 2064/**
2062 * Client requests to remove a slave from the membership database. 2065 * Client requests to add/remove a slave in the membership database.
2063 */ 2066 */
2064static void 2067static void
2065client_recv_slave_remove (void *cls, struct GNUNET_SERVER_Client *client, 2068client_recv_membership_store (void *cls, struct GNUNET_SERVER_Client *client,
2066 const struct GNUNET_MessageHeader *msg) 2069 const struct GNUNET_MessageHeader *msg)
2067{ 2070{
2071 struct Channel *
2072 chn = GNUNET_SERVER_client_get_user_context (client, struct Channel);
2073 GNUNET_assert (NULL != chn);
2074
2075 const struct ChannelMembershipStoreRequest *
2076 req = (const struct ChannelMembershipStoreRequest *) msg;
2077
2078 uint64_t announced_at = GNUNET_ntohll (req->announced_at);
2079 uint64_t effective_since = GNUNET_ntohll (req->effective_since);
2080 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2081 "%p Received membership store request from client.\n", chn);
2082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2083 "%p did_join: %u, announced_at: %" PRIu64 ", effective_since: %" PRIu64 "\n",
2084 chn, req->did_join, announced_at, effective_since);
2068 2085
2086 GNUNET_PSYCSTORE_membership_store (store, &chn->pub_key, &req->slave_key,
2087 req->did_join, announced_at, effective_since,
2088 0, /* FIXME: group_generation */
2089 &store_recv_membership_store_result, chn);
2090 GNUNET_SERVER_receive_done (client, GNUNET_OK);
2069} 2091}
2070 2092
2071 2093
@@ -2115,11 +2137,8 @@ static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
2115 { &client_recv_psyc_message, NULL, 2137 { &client_recv_psyc_message, NULL,
2116 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 }, 2138 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, 0 },
2117 2139
2118 { &client_recv_slave_add, NULL, 2140 { &client_recv_membership_store, NULL,
2119 GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_ADD, 0 }, 2141 GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE, 0 },
2120
2121 { &client_recv_slave_remove, NULL,
2122 GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_RM, 0 },
2123 2142
2124 { &client_recv_story_request, NULL, 2143 { &client_recv_story_request, NULL,
2125 GNUNET_MESSAGE_TYPE_PSYC_STORY_REQUEST, 0 }, 2144 GNUNET_MESSAGE_TYPE_PSYC_STORY_REQUEST, 0 },
diff --git a/src/psyc/psyc.h b/src/psyc/psyc.h
index 82800a334..21131e7d3 100644
--- a/src/psyc/psyc.h
+++ b/src/psyc/psyc.h
@@ -105,35 +105,22 @@ struct SlaveJoinRequest
105}; 105};
106 106
107 107
108struct ChannelSlaveAddRequest 108struct ChannelMembershipStoreRequest
109{ 109{
110 /** 110 /**
111 * Type: GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_ADD 111 * Type: GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE
112 */ 112 */
113 struct GNUNET_MessageHeader header; 113 struct GNUNET_MessageHeader header;
114 114
115 uint32_t reserved; 115 uint32_t reserved;
116 116
117 struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key; 117 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
118 118
119 uint64_t announced_at; 119 uint64_t announced_at;
120 120
121 uint64_t effective_since; 121 uint64_t effective_since;
122};
123
124 122
125struct ChannelSlaveRemoveRequest 123 uint8_t did_join;
126{
127 /**
128 * Type: GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_RM
129 */
130 struct GNUNET_MessageHeader header;
131
132 uint32_t reserved;
133
134 struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key;
135
136 uint64_t announced_at;
137}; 124};
138 125
139 126
diff --git a/src/psyc/psyc_api.c b/src/psyc/psyc_api.c
index a43b1ef5f..ca25b1b01 100644
--- a/src/psyc/psyc_api.c
+++ b/src/psyc/psyc_api.c
@@ -810,12 +810,14 @@ GNUNET_PSYC_channel_slave_add (struct GNUNET_PSYC_Channel *chn,
810 uint64_t announced_at, 810 uint64_t announced_at,
811 uint64_t effective_since) 811 uint64_t effective_since)
812{ 812{
813 struct ChannelSlaveAddRequest *add = GNUNET_malloc (sizeof (*add)); 813 struct ChannelMembershipStoreRequest *req = GNUNET_malloc (sizeof (*req));
814 add->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_ADD); 814 req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE);
815 add->header.size = htons (sizeof (*add)); 815 req->header.size = htons (sizeof (*req));
816 add->announced_at = GNUNET_htonll (announced_at); 816 req->slave_key = *slave_key;
817 add->effective_since = GNUNET_htonll (effective_since); 817 req->announced_at = GNUNET_htonll (announced_at);
818 GNUNET_CLIENT_MANAGER_transmit (chn->client, &add->header); 818 req->effective_since = GNUNET_htonll (effective_since);
819 req->did_join = GNUNET_YES;
820 GNUNET_CLIENT_MANAGER_transmit (chn->client, &req->header);
819} 821}
820 822
821 823
@@ -845,11 +847,13 @@ GNUNET_PSYC_channel_slave_remove (struct GNUNET_PSYC_Channel *chn,
845 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key, 847 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
846 uint64_t announced_at) 848 uint64_t announced_at)
847{ 849{
848 struct ChannelSlaveRemoveRequest *rm = GNUNET_malloc (sizeof (*rm)); 850 struct ChannelMembershipStoreRequest *req = GNUNET_malloc (sizeof (*req));
849 rm->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_SLAVE_RM); 851 req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE);
850 rm->header.size = htons (sizeof (*rm)); 852 req->header.size = htons (sizeof (*req));
851 rm->announced_at = GNUNET_htonll (announced_at); 853 req->slave_key = *slave_key;
852 GNUNET_CLIENT_MANAGER_transmit (chn->client, &rm->header); 854 req->announced_at = GNUNET_htonll (announced_at);
855 req->did_join = GNUNET_NO;
856 GNUNET_CLIENT_MANAGER_transmit (chn->client, &req->header);
853} 857}
854 858
855 859
diff --git a/src/psyc/test_psyc.c b/src/psyc/test_psyc.c
index da69f40b1..5eadef62c 100644
--- a/src/psyc/test_psyc.c
+++ b/src/psyc/test_psyc.c
@@ -413,20 +413,8 @@ slave_join ();
413 413
414 414
415void 415void
416join_decision_cb (void *cls, 416slave_transmit ()
417 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
418 int is_admitted,
419 const struct GNUNET_PSYC_Message *join_msg)
420{ 417{
421 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
422 "Slave got join decision: %d\n", is_admitted);
423
424 if (GNUNET_YES != is_admitted)
425 { /* First join request is refused, retry. */
426 GNUNET_assert (1 == join_req_count);
427 slave_join ();
428 return;
429 }
430 418
431 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Slave sending request to master.\n"); 419 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Slave sending request to master.\n");
432 420
@@ -450,6 +438,26 @@ join_decision_cb (void *cls,
450 438
451 439
452void 440void
441join_decision_cb (void *cls,
442 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
443 int is_admitted,
444 const struct GNUNET_PSYC_Message *join_msg)
445{
446 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
447 "Slave got join decision: %d\n", is_admitted);
448
449 if (GNUNET_YES != is_admitted)
450 { /* First join request is refused, retry. */
451 GNUNET_assert (1 == join_req_count);
452 slave_join ();
453 return;
454 }
455
456 slave_transmit ();
457}
458
459
460void
453join_request_cb (void *cls, 461join_request_cb (void *cls,
454 const struct GNUNET_PSYC_JoinRequestMessage *req, 462 const struct GNUNET_PSYC_JoinRequestMessage *req,
455 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key, 463 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
@@ -465,6 +473,11 @@ join_request_cb (void *cls,
465 /* Reject first request */ 473 /* Reject first request */
466 int is_admitted = (0 < join_req_count++) ? GNUNET_YES : GNUNET_NO; 474 int is_admitted = (0 < join_req_count++) ? GNUNET_YES : GNUNET_NO;
467 GNUNET_PSYC_join_decision (jh, is_admitted, 0, NULL, NULL); 475 GNUNET_PSYC_join_decision (jh, is_admitted, 0, NULL, NULL);
476
477 /* Membership store */
478 struct GNUNET_PSYC_Channel *chn = GNUNET_PSYC_master_get_channel (mst);
479 GNUNET_PSYC_channel_slave_add (chn, slave_key, 2, 2);
480 GNUNET_PSYC_channel_slave_remove (chn, &slave_pub_key, 2);
468} 481}
469 482
470 483
diff --git a/src/psycstore/gnunet-service-psycstore.c b/src/psycstore/gnunet-service-psycstore.c
index 87ace92be..7d27ea29b 100644
--- a/src/psycstore/gnunet-service-psycstore.c
+++ b/src/psycstore/gnunet-service-psycstore.c
@@ -240,7 +240,7 @@ handle_membership_store (void *cls,
240 (const struct MembershipStoreRequest *) msg; 240 (const struct MembershipStoreRequest *) msg;
241 241
242 int ret = db->membership_store (db->cls, &req->channel_key, &req->slave_key, 242 int ret = db->membership_store (db->cls, &req->channel_key, &req->slave_key,
243 ntohl (req->did_join), 243 req->did_join,
244 GNUNET_ntohll (req->announced_at), 244 GNUNET_ntohll (req->announced_at),
245 GNUNET_ntohll (req->effective_since), 245 GNUNET_ntohll (req->effective_since),
246 GNUNET_ntohll (req->group_generation)); 246 GNUNET_ntohll (req->group_generation));
diff --git a/src/psycstore/psycstore.h b/src/psycstore/psycstore.h
index 65d32d9fa..17905f422 100644
--- a/src/psycstore/psycstore.h
+++ b/src/psycstore/psycstore.h
@@ -177,10 +177,10 @@ struct MembershipStoreRequest
177 */ 177 */
178 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key; 178 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
179 179
180 uint64_t announced_at GNUNET_PACKED; 180 uint64_t announced_at;
181 uint64_t effective_since GNUNET_PACKED; 181 uint64_t effective_since;
182 uint64_t group_generation GNUNET_PACKED; 182 uint64_t group_generation;
183 int did_join GNUNET_PACKED; 183 uint8_t did_join;
184}; 184};
185 185
186 186
diff --git a/src/psycstore/psycstore_api.c b/src/psycstore/psycstore_api.c
index f034ff475..9df55888d 100644
--- a/src/psycstore/psycstore_api.c
+++ b/src/psycstore/psycstore_api.c
@@ -638,18 +638,27 @@ GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle *op)
638 * Store join/leave events for a PSYC channel in order to be able to answer 638 * Store join/leave events for a PSYC channel in order to be able to answer
639 * membership test queries later. 639 * membership test queries later.
640 * 640 *
641 * @param h Handle for the PSYCstore. 641 * @param h
642 * @param channel_key The channel where the event happened. 642 * Handle for the PSYCstore.
643 * @param slave_key Public key of joining/leaving slave. 643 * @param channel_key
644 * @param did_join #GNUNET_YES on join, #GNUNET_NO on part. 644 * The channel where the event happened.
645 * @param announced_at ID of the message that announced the membership change. 645 * @param slave_key
646 * @param effective_since Message ID this membership change is in effect since. 646 * Public key of joining/leaving slave.
647 * @param did_join
648 * #GNUNET_YES on join, #GNUNET_NO on part.
649 * @param announced_at
650 * ID of the message that announced the membership change.
651 * @param effective_since
652 * Message ID this membership change is in effect since.
647 * For joins it is <= announced_at, for parts it is always 0. 653 * For joins it is <= announced_at, for parts it is always 0.
648 * @param group_generation In case of a part, the last group generation the 654 * @param group_generation
649 * slave has access to. It has relevance when a larger message have 655 * In case of a part, the last group generation the slave has access to.
650 * fragments with different group generations. 656 * It has relevance when a larger message have fragments with different
651 * @param rcb Callback to call with the result of the storage operation. 657 * group generations.
652 * @param rcb_cls Closure for the callback. 658 * @param rcb
659 * Callback to call with the result of the storage operation.
660 * @param rcb_cls
661 * Closure for the callback.
653 * 662 *
654 * @return Operation handle that can be used to cancel the operation. 663 * @return Operation handle that can be used to cancel the operation.
655 */ 664 */
@@ -667,6 +676,7 @@ GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
667 GNUNET_assert (NULL != h); 676 GNUNET_assert (NULL != h);
668 GNUNET_assert (NULL != channel_key); 677 GNUNET_assert (NULL != channel_key);
669 GNUNET_assert (NULL != slave_key); 678 GNUNET_assert (NULL != slave_key);
679 GNUNET_assert (GNUNET_YES == did_join || GNUNET_NO == did_join);
670 GNUNET_assert (did_join 680 GNUNET_assert (did_join
671 ? effective_since <= announced_at 681 ? effective_since <= announced_at
672 : effective_since == 0); 682 : effective_since == 0);
@@ -684,7 +694,7 @@ GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
684 req->header.size = htons (sizeof (*req)); 694 req->header.size = htons (sizeof (*req));
685 req->channel_key = *channel_key; 695 req->channel_key = *channel_key;
686 req->slave_key = *slave_key; 696 req->slave_key = *slave_key;
687 req->did_join = htonl (did_join); 697 req->did_join = did_join;
688 req->announced_at = GNUNET_htonll (announced_at); 698 req->announced_at = GNUNET_htonll (announced_at);
689 req->effective_since = GNUNET_htonll (effective_since); 699 req->effective_since = GNUNET_htonll (effective_since);
690 req->group_generation = GNUNET_htonll (group_generation); 700 req->group_generation = GNUNET_htonll (group_generation);
@@ -707,15 +717,22 @@ GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
707 * is also used when handling join requests to determine whether the slave is 717 * is also used when handling join requests to determine whether the slave is
708 * currently admitted to the channel. 718 * currently admitted to the channel.
709 * 719 *
710 * @param h Handle for the PSYCstore. 720 * @param h
711 * @param channel_key The channel we are interested in. 721 * Handle for the PSYCstore.
712 * @param slave_key Public key of slave whose membership to check. 722 * @param channel_key
713 * @param message_id Message ID for which to do the membership test. 723 * The channel we are interested in.
714 * @param group_generation Group generation of the fragment of the message to 724 * @param slave_key
715 * test. It has relevance if the message consists of multiple fragments 725 * Public key of slave whose membership to check.
716 * with different group generations. 726 * @param message_id
717 * @param rcb Callback to call with the test result. 727 * Message ID for which to do the membership test.
718 * @param rcb_cls Closure for the callback. 728 * @param group_generation
729 * Group generation of the fragment of the message to test.
730 * It has relevance if the message consists of multiple fragments with
731 * different group generations.
732 * @param rcb
733 * Callback to call with the test result.
734 * @param rcb_cls
735 * Closure for the callback.
719 * 736 *
720 * @return Operation handle that can be used to cancel the operation. 737 * @return Operation handle that can be used to cancel the operation.
721 */ 738 */