diff options
-rw-r--r-- | src/cadet/gnunet-service-cadet-new.c | 41 | ||||
-rw-r--r-- | src/cadet/gnunet-service-cadet-new.h | 5 | ||||
-rw-r--r-- | src/cadet/gnunet-service-cadet-new_channel.c | 145 | ||||
-rw-r--r-- | src/cadet/gnunet-service-cadet-new_channel.h | 14 | ||||
-rw-r--r-- | src/cadet/gnunet-service-cadet-new_tunnels.c | 24 |
5 files changed, 155 insertions, 74 deletions
diff --git a/src/cadet/gnunet-service-cadet-new.c b/src/cadet/gnunet-service-cadet-new.c index bb251c307..c9a2fb437 100644 --- a/src/cadet/gnunet-service-cadet-new.c +++ b/src/cadet/gnunet-service-cadet-new.c | |||
@@ -131,7 +131,7 @@ struct GNUNET_PeerIdentity my_full_id; | |||
131 | struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; | 131 | struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; |
132 | 132 | ||
133 | /** | 133 | /** |
134 | * Signal that shutdown is happening: prevent recover measures. | 134 | * Signal that shutdown is happening: prevent recovery measures. |
135 | */ | 135 | */ |
136 | int shutting_down; | 136 | int shutting_down; |
137 | 137 | ||
@@ -356,17 +356,11 @@ destroy_paths_now (void *cls, | |||
356 | 356 | ||
357 | 357 | ||
358 | /** | 358 | /** |
359 | * Task run during shutdown. | 359 | * Shutdown everything once the clients have disconnected. |
360 | * | ||
361 | * @param cls unused | ||
362 | */ | 360 | */ |
363 | static void | 361 | static void |
364 | shutdown_task (void *cls) | 362 | shutdown_rest () |
365 | { | 363 | { |
366 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
367 | "Shutting down\n"); | ||
368 | shutting_down = GNUNET_YES; | ||
369 | GCO_shutdown (); | ||
370 | if (NULL != stats) | 364 | if (NULL != stats) |
371 | { | 365 | { |
372 | GNUNET_STATISTICS_destroy (stats, | 366 | GNUNET_STATISTICS_destroy (stats, |
@@ -414,6 +408,23 @@ shutdown_task (void *cls) | |||
414 | 408 | ||
415 | 409 | ||
416 | /** | 410 | /** |
411 | * Task run during shutdown. | ||
412 | * | ||
413 | * @param cls unused | ||
414 | */ | ||
415 | static void | ||
416 | shutdown_task (void *cls) | ||
417 | { | ||
418 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
419 | "Shutting down\n"); | ||
420 | shutting_down = GNUNET_YES; | ||
421 | GCO_shutdown (); | ||
422 | if (NULL == clients_head) | ||
423 | shutdown_rest (); | ||
424 | } | ||
425 | |||
426 | |||
427 | /** | ||
417 | * We had a remote connection @a value to port @a port before | 428 | * We had a remote connection @a value to port @a port before |
418 | * client @a cls opened port @a port. Bind them now. | 429 | * client @a cls opened port @a port. Bind them now. |
419 | * | 430 | * |
@@ -608,7 +619,8 @@ handle_channel_destroy (void *cls, | |||
608 | GNUNET_CONTAINER_multihashmap32_remove (c->channels, | 619 | GNUNET_CONTAINER_multihashmap32_remove (c->channels, |
609 | ntohl (msg->ccn.channel_of_client), | 620 | ntohl (msg->ccn.channel_of_client), |
610 | ch)); | 621 | ch)); |
611 | GCCH_channel_local_destroy (ch); | 622 | GCCH_channel_local_destroy (ch, |
623 | c); | ||
612 | GNUNET_SERVICE_client_continue (c->client); | 624 | GNUNET_SERVICE_client_continue (c->client); |
613 | } | 625 | } |
614 | 626 | ||
@@ -1196,10 +1208,8 @@ channel_destroy_iterator (void *cls, | |||
1196 | GNUNET_CONTAINER_multihashmap32_remove (c->channels, | 1208 | GNUNET_CONTAINER_multihashmap32_remove (c->channels, |
1197 | key, | 1209 | key, |
1198 | ch)); | 1210 | ch)); |
1199 | if (key < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) | 1211 | GCCH_channel_local_destroy (ch, |
1200 | GCCH_channel_local_destroy (ch); | 1212 | c); |
1201 | else | ||
1202 | GCCH_channel_incoming_destroy (ch); | ||
1203 | return GNUNET_OK; | 1213 | return GNUNET_OK; |
1204 | } | 1214 | } |
1205 | 1215 | ||
@@ -1275,6 +1285,9 @@ client_disconnect_cb (void *cls, | |||
1275 | -1, | 1285 | -1, |
1276 | GNUNET_NO); | 1286 | GNUNET_NO); |
1277 | GNUNET_free (c); | 1287 | GNUNET_free (c); |
1288 | if ( (NULL == clients_head) && | ||
1289 | (GNUNET_YES == shutting_down) ) | ||
1290 | shutdown_rest (); | ||
1278 | } | 1291 | } |
1279 | 1292 | ||
1280 | 1293 | ||
diff --git a/src/cadet/gnunet-service-cadet-new.h b/src/cadet/gnunet-service-cadet-new.h index cb289e9c3..4a76c578b 100644 --- a/src/cadet/gnunet-service-cadet-new.h +++ b/src/cadet/gnunet-service-cadet-new.h | |||
@@ -225,6 +225,11 @@ extern unsigned long long ratchet_messages; | |||
225 | */ | 225 | */ |
226 | extern struct GNUNET_TIME_Relative ratchet_time; | 226 | extern struct GNUNET_TIME_Relative ratchet_time; |
227 | 227 | ||
228 | /** | ||
229 | * Signal that shutdown is happening: prevent recovery measures. | ||
230 | */ | ||
231 | extern int shutting_down; | ||
232 | |||
228 | 233 | ||
229 | /** | 234 | /** |
230 | * Send a message to a client. | 235 | * Send a message to a client. |
diff --git a/src/cadet/gnunet-service-cadet-new_channel.c b/src/cadet/gnunet-service-cadet-new_channel.c index f45c5e5ce..870732192 100644 --- a/src/cadet/gnunet-service-cadet-new_channel.c +++ b/src/cadet/gnunet-service-cadet-new_channel.c | |||
@@ -302,6 +302,11 @@ struct CadetChannel | |||
302 | int out_of_order; | 302 | int out_of_order; |
303 | 303 | ||
304 | /** | 304 | /** |
305 | * Is this channel a loopback channel, where the destination is us again? | ||
306 | */ | ||
307 | int is_loopback; | ||
308 | |||
309 | /** | ||
305 | * Flag to signal the destruction of the channel. If this is set to | 310 | * Flag to signal the destruction of the channel. If this is set to |
306 | * #GNUNET_YES the channel will be destroyed once the queue is | 311 | * #GNUNET_YES the channel will be destroyed once the queue is |
307 | * empty. | 312 | * empty. |
@@ -523,11 +528,38 @@ GCCH_channel_local_new (struct CadetClient *owner, | |||
523 | ch->owner = owner; | 528 | ch->owner = owner; |
524 | ch->ccn = ccn; | 529 | ch->ccn = ccn; |
525 | ch->port = *port; | 530 | ch->port = *port; |
526 | ch->t = GCP_get_tunnel (destination, | 531 | if (0 == memcmp (&my_full_id, |
527 | GNUNET_YES); | 532 | GCP_get_id (destination), |
528 | ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; | 533 | sizeof (struct GNUNET_PeerIdentity))) |
529 | ch->ctn = GCT_add_channel (ch->t, | 534 | { |
530 | ch); | 535 | ch->is_loopback = GNUNET_YES; |
536 | ch->dest = GNUNET_CONTAINER_multihashmap_get (open_ports, | ||
537 | port); | ||
538 | if (NULL == ch->dest) | ||
539 | { | ||
540 | /* port closed, wait for it to possibly open */ | ||
541 | (void) GNUNET_CONTAINER_multihashmap_put (loose_channels, | ||
542 | port, | ||
543 | ch, | ||
544 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
545 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
546 | "Created loose incoming loopback channel to port %s\n", | ||
547 | GNUNET_h2s (&ch->port)); | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | GCCH_bind (ch, | ||
552 | ch->dest); | ||
553 | } | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | ch->t = GCP_get_tunnel (destination, | ||
558 | GNUNET_YES); | ||
559 | ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; | ||
560 | ch->ctn = GCT_add_channel (ch->t, | ||
561 | ch); | ||
562 | } | ||
531 | GNUNET_STATISTICS_update (stats, | 563 | GNUNET_STATISTICS_update (stats, |
532 | "# channels", | 564 | "# channels", |
533 | 1, | 565 | 1, |
@@ -537,7 +569,7 @@ GCCH_channel_local_new (struct CadetClient *owner, | |||
537 | GNUNET_h2s (port), | 569 | GNUNET_h2s (port), |
538 | GCP_2s (destination), | 570 | GCP_2s (destination), |
539 | GSC_2s (owner), | 571 | GSC_2s (owner), |
540 | GCT_2s (ch->t)); | 572 | (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t)); |
541 | return ch; | 573 | return ch; |
542 | } | 574 | } |
543 | 575 | ||
@@ -791,11 +823,18 @@ GCCH_bind (struct CadetChannel *ch, | |||
791 | &ch->port, | 823 | &ch->port, |
792 | options); | 824 | options); |
793 | ch->mid_recv.mid = htonl (1); /* The CONNECT counts as message 0! */ | 825 | ch->mid_recv.mid = htonl (1); /* The CONNECT counts as message 0! */ |
794 | 826 | if (GNUNET_YES == ch->is_loopback) | |
795 | /* notify other peer that we accepted the connection */ | 827 | { |
796 | ch->retry_control_task | 828 | ch->state = CADET_CHANNEL_OPEN_SENT; |
797 | = GNUNET_SCHEDULER_add_now (&send_open_ack, | 829 | GCCH_handle_channel_open_ack (ch); |
798 | ch); | 830 | } |
831 | else | ||
832 | { | ||
833 | /* notify other peer that we accepted the connection */ | ||
834 | ch->retry_control_task | ||
835 | = GNUNET_SCHEDULER_add_now (&send_open_ack, | ||
836 | ch); | ||
837 | } | ||
799 | /* give client it's initial supply of ACKs */ | 838 | /* give client it's initial supply of ACKs */ |
800 | env = GNUNET_MQ_msg (tcm, | 839 | env = GNUNET_MQ_msg (tcm, |
801 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE); | 840 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE); |
@@ -812,17 +851,27 @@ GCCH_bind (struct CadetChannel *ch, | |||
812 | 851 | ||
813 | 852 | ||
814 | /** | 853 | /** |
815 | * Destroy locally created channel. Called by the | 854 | * Destroy locally created channel. Called by the local client, so no |
816 | * local client, so no need to tell the client. | 855 | * need to tell the client. |
817 | * | 856 | * |
818 | * @param ch channel to destroy | 857 | * @param ch channel to destroy |
858 | * @param c client that caused the destruction | ||
819 | */ | 859 | */ |
820 | void | 860 | void |
821 | GCCH_channel_local_destroy (struct CadetChannel *ch) | 861 | GCCH_channel_local_destroy (struct CadetChannel *ch, |
862 | struct CadetClient *c) | ||
822 | { | 863 | { |
823 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 864 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
824 | "Local client asks for destruction of %s which it initiated\n", | 865 | "%s asks for destruction of %s\n", |
866 | GSC_2s (c), | ||
825 | GCCH_2s (ch)); | 867 | GCCH_2s (ch)); |
868 | GNUNET_assert (NULL != c); | ||
869 | if (c == ch->owner) | ||
870 | ch->owner = NULL; | ||
871 | else if (c == ch->dest) | ||
872 | ch->dest = NULL; | ||
873 | else | ||
874 | GNUNET_assert (0); | ||
826 | if (GNUNET_YES == ch->destroy) | 875 | if (GNUNET_YES == ch->destroy) |
827 | { | 876 | { |
828 | /* other end already destroyed, with the local client gone, no need | 877 | /* other end already destroyed, with the local client gone, no need |
@@ -830,9 +879,12 @@ GCCH_channel_local_destroy (struct CadetChannel *ch) | |||
830 | channel_destroy (ch); | 879 | channel_destroy (ch); |
831 | return; | 880 | return; |
832 | } | 881 | } |
833 | if (NULL != ch->head_sent) | 882 | if ( (NULL != ch->head_sent) || |
883 | (NULL != ch->owner) || | ||
884 | (NULL != ch->dest) ) | ||
834 | { | 885 | { |
835 | /* allow send queue to train first */ | 886 | /* Wait for other end to destroy us as well, |
887 | and otherwise allow send queue to be transmitted first */ | ||
836 | ch->destroy = GNUNET_YES; | 888 | ch->destroy = GNUNET_YES; |
837 | return; | 889 | return; |
838 | } | 890 | } |
@@ -840,39 +892,7 @@ GCCH_channel_local_destroy (struct CadetChannel *ch) | |||
840 | if (CADET_CHANNEL_NEW != ch->state) | 892 | if (CADET_CHANNEL_NEW != ch->state) |
841 | GCT_send_channel_destroy (ch->t, | 893 | GCT_send_channel_destroy (ch->t, |
842 | ch->ctn); | 894 | ch->ctn); |
843 | /* Now finish our clean up */ | ||
844 | channel_destroy (ch); | ||
845 | } | ||
846 | |||
847 | |||
848 | /** | ||
849 | * Destroy channel that was incoming. Called by the | ||
850 | * local client, so no need to tell the client. | ||
851 | * | ||
852 | * @param ch channel to destroy | ||
853 | */ | ||
854 | void | ||
855 | GCCH_channel_incoming_destroy (struct CadetChannel *ch) | ||
856 | { | ||
857 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
858 | "Local client asks for destruction of %s which it accepted\n", | ||
859 | GCCH_2s (ch)); | ||
860 | if (GNUNET_YES == ch->destroy) | ||
861 | { | ||
862 | /* other end already destroyed, with the remote client gone, no need | ||
863 | to finish transmissions, just destroy immediately. */ | ||
864 | channel_destroy (ch); | ||
865 | return; | ||
866 | } | ||
867 | if (NULL != ch->head_recv) | ||
868 | { | ||
869 | /* allow local client to see all data first */ | ||
870 | ch->destroy = GNUNET_YES; | ||
871 | return; | ||
872 | } | ||
873 | /* Nothing left to do, just finish destruction */ | 895 | /* Nothing left to do, just finish destruction */ |
874 | GCT_send_channel_destroy (ch->t, | ||
875 | ch->ctn); | ||
876 | channel_destroy (ch); | 896 | channel_destroy (ch); |
877 | } | 897 | } |
878 | 898 | ||
@@ -980,6 +1000,12 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, | |||
980 | "Receicved %u bytes of application data on %s\n", | 1000 | "Receicved %u bytes of application data on %s\n", |
981 | (unsigned int) payload_size, | 1001 | (unsigned int) payload_size, |
982 | GCCH_2s (ch)); | 1002 | GCCH_2s (ch)); |
1003 | if (GNUNET_YES == ch->is_loopback) | ||
1004 | { | ||
1005 | GNUNET_break (0); // FIXME: not implemented | ||
1006 | return; | ||
1007 | } | ||
1008 | |||
983 | env = GNUNET_MQ_msg_extra (ld, | 1009 | env = GNUNET_MQ_msg_extra (ld, |
984 | payload_size, | 1010 | payload_size, |
985 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); | 1011 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); |
@@ -1054,6 +1080,7 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, | |||
1054 | { | 1080 | { |
1055 | struct CadetReliableMessage *crm; | 1081 | struct CadetReliableMessage *crm; |
1056 | 1082 | ||
1083 | GNUNET_break (GNUNET_NO == ch->is_loopback); | ||
1057 | if (GNUNET_NO == ch->reliable) | 1084 | if (GNUNET_NO == ch->reliable) |
1058 | { | 1085 | { |
1059 | /* not expecting ACKs on unreliable channel, odd */ | 1086 | /* not expecting ACKs on unreliable channel, odd */ |
@@ -1104,9 +1131,23 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, | |||
1104 | void | 1131 | void |
1105 | GCCH_handle_remote_destroy (struct CadetChannel *ch) | 1132 | GCCH_handle_remote_destroy (struct CadetChannel *ch) |
1106 | { | 1133 | { |
1134 | GNUNET_break (GNUNET_NO == ch->is_loopback); | ||
1107 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 1135 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
1108 | "Received remote channel DESTROY for %s\n", | 1136 | "Received remote channel DESTROY for %s\n", |
1109 | GCCH_2s (ch)); | 1137 | GCCH_2s (ch)); |
1138 | if (GNUNET_YES == ch->destroy) | ||
1139 | { | ||
1140 | /* Local client already gone, this is instant-death. */ | ||
1141 | channel_destroy (ch); | ||
1142 | return; | ||
1143 | } | ||
1144 | if (NULL != ch->head_recv) | ||
1145 | { | ||
1146 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1147 | "Lost end of transmission due to remote shutdown on channel %s\n", | ||
1148 | GCCH_2s (ch)); | ||
1149 | /* FIXME: change API to notify client about truncated transmission! */ | ||
1150 | } | ||
1110 | ch->destroy = GNUNET_YES; | 1151 | ch->destroy = GNUNET_YES; |
1111 | GSC_handle_remote_channel_destroy ((NULL != ch->owner) ? ch->owner : ch->dest, | 1152 | GSC_handle_remote_channel_destroy ((NULL != ch->owner) ? ch->owner : ch->dest, |
1112 | ch->ccn, | 1153 | ch->ccn, |
@@ -1290,6 +1331,12 @@ GCCH_handle_local_data (struct CadetChannel *ch, | |||
1290 | } | 1331 | } |
1291 | ch->pending_messages++; | 1332 | ch->pending_messages++; |
1292 | 1333 | ||
1334 | if (GNUNET_YES == ch->is_loopback) | ||
1335 | { | ||
1336 | GNUNET_break (0); // fIXME: not implemented | ||
1337 | return GNUNET_SYSERR; | ||
1338 | } | ||
1339 | |||
1293 | /* Everything is correct, send the message. */ | 1340 | /* Everything is correct, send the message. */ |
1294 | crm = GNUNET_malloc (sizeof (*crm) + buf_len); | 1341 | crm = GNUNET_malloc (sizeof (*crm) + buf_len); |
1295 | crm->ch = ch; | 1342 | crm->ch = ch; |
diff --git a/src/cadet/gnunet-service-cadet-new_channel.h b/src/cadet/gnunet-service-cadet-new_channel.h index 3a931499e..a473fd3e4 100644 --- a/src/cadet/gnunet-service-cadet-new_channel.h +++ b/src/cadet/gnunet-service-cadet-new_channel.h | |||
@@ -113,9 +113,11 @@ GCCH_bind (struct CadetChannel *ch, | |||
113 | * local client, so no need to tell the client. | 113 | * local client, so no need to tell the client. |
114 | * | 114 | * |
115 | * @param ch channel to destroy | 115 | * @param ch channel to destroy |
116 | * @param c client that caused the destruction | ||
116 | */ | 117 | */ |
117 | void | 118 | void |
118 | GCCH_channel_local_destroy (struct CadetChannel *ch); | 119 | GCCH_channel_local_destroy (struct CadetChannel *ch, |
120 | struct CadetClient *c); | ||
119 | 121 | ||
120 | 122 | ||
121 | /** | 123 | /** |
@@ -150,16 +152,6 @@ GCCH_channel_incoming_new (struct CadetTunnel *t, | |||
150 | 152 | ||
151 | 153 | ||
152 | /** | 154 | /** |
153 | * Destroy channel that was incoming. Called by the | ||
154 | * local client, so no need to tell the client. | ||
155 | * | ||
156 | * @param ch channel to destroy | ||
157 | */ | ||
158 | void | ||
159 | GCCH_channel_incoming_destroy (struct CadetChannel *ch); | ||
160 | |||
161 | |||
162 | /** | ||
163 | * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for | 155 | * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for |
164 | * this channel. If the binding was successful, (re)transmit the | 156 | * this channel. If the binding was successful, (re)transmit the |
165 | * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. | 157 | * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. |
diff --git a/src/cadet/gnunet-service-cadet-new_tunnels.c b/src/cadet/gnunet-service-cadet-new_tunnels.c index 20ab8f4e9..b85f1b321 100644 --- a/src/cadet/gnunet-service-cadet-new_tunnels.c +++ b/src/cadet/gnunet-service-cadet-new_tunnels.c | |||
@@ -1624,6 +1624,26 @@ GCT_remove_channel (struct CadetTunnel *t, | |||
1624 | 1624 | ||
1625 | 1625 | ||
1626 | /** | 1626 | /** |
1627 | * Destroy remaining channels during shutdown. | ||
1628 | * | ||
1629 | * @param cls the `struct CadetTunnel` of the channel | ||
1630 | * @param key key of the channel | ||
1631 | * @param value the `struct CadetChannel` | ||
1632 | * @return #GNUNET_OK (continue to iterate) | ||
1633 | */ | ||
1634 | static int | ||
1635 | destroy_remaining_channels (void *cls, | ||
1636 | uint32_t key, | ||
1637 | void *value) | ||
1638 | { | ||
1639 | struct CadetChannel *ch = value; | ||
1640 | |||
1641 | GCCH_handle_remote_destroy (ch); | ||
1642 | return GNUNET_OK; | ||
1643 | } | ||
1644 | |||
1645 | |||
1646 | /** | ||
1627 | * Destroys the tunnel @a t now, without delay. Used during shutdown. | 1647 | * Destroys the tunnel @a t now, without delay. Used during shutdown. |
1628 | * | 1648 | * |
1629 | * @param t tunnel to destroy | 1649 | * @param t tunnel to destroy |
@@ -1631,6 +1651,10 @@ GCT_remove_channel (struct CadetTunnel *t, | |||
1631 | void | 1651 | void |
1632 | GCT_destroy_tunnel_now (struct CadetTunnel *t) | 1652 | GCT_destroy_tunnel_now (struct CadetTunnel *t) |
1633 | { | 1653 | { |
1654 | GNUNET_assert (GNUNET_YES == shutting_down); | ||
1655 | GNUNET_CONTAINER_multihashmap32_iterate (t->channels, | ||
1656 | &destroy_remaining_channels, | ||
1657 | t); | ||
1634 | GNUNET_assert (0 == | 1658 | GNUNET_assert (0 == |
1635 | GNUNET_CONTAINER_multihashmap32_size (t->channels)); | 1659 | GNUNET_CONTAINER_multihashmap32_size (t->channels)); |
1636 | if (NULL != t->destroy_task) | 1660 | if (NULL != t->destroy_task) |