OLD | NEW |
---|---|
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:collection'; | 6 import 'dart:collection'; |
7 import 'dart:io' show Platform; | 7 import 'dart:io' show Platform; |
8 | 8 |
9 import 'package:analysis_server/src/plugin/notification_manager.dart'; | 9 import 'package:analysis_server/src/plugin/notification_manager.dart'; |
10 import 'package:analyzer/context/context_root.dart' as analyzer; | 10 import 'package:analyzer/context/context_root.dart' as analyzer; |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
228 currentSession.sendRequest(params); | 228 currentSession.sendRequest(params); |
229 } | 229 } |
230 } | 230 } |
231 } | 231 } |
232 | 232 |
233 /** | 233 /** |
234 * An object used to manage the currently running plugins. | 234 * An object used to manage the currently running plugins. |
235 */ | 235 */ |
236 class PluginManager { | 236 class PluginManager { |
237 /** | 237 /** |
238 * A table, keyed by both a plugin and a request method, to a list of the | |
239 * times that it took the plugin to return a response to requests with the | |
240 * method. | |
241 */ | |
242 static Map<PluginInfo, Map<String, List<int>>> pluginResponseTimes = | |
243 <PluginInfo, Map<String, List<int>>>{}; | |
244 | |
245 /** | |
238 * The resource provider used to access the file system. | 246 * The resource provider used to access the file system. |
239 */ | 247 */ |
240 final ResourceProvider resourceProvider; | 248 final ResourceProvider resourceProvider; |
241 | 249 |
242 /** | 250 /** |
243 * The absolute path of the directory containing the on-disk byte store, or | 251 * The absolute path of the directory containing the on-disk byte store, or |
244 * `null` if there is no on-disk store. | 252 * `null` if there is no on-disk store. |
245 */ | 253 */ |
246 final String byteStorePath; | 254 final String byteStorePath; |
247 | 255 |
(...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
560 return computePaths(executionFolder, runPub: true); | 568 return computePaths(executionFolder, runPub: true); |
561 } | 569 } |
562 | 570 |
563 /** | 571 /** |
564 * Return a hex-encoded MD5 signature of the given file [path]. | 572 * Return a hex-encoded MD5 signature of the given file [path]. |
565 */ | 573 */ |
566 String _uniqueDirectoryName(String path) { | 574 String _uniqueDirectoryName(String path) { |
567 List<int> bytes = md5.convert(path.codeUnits).bytes; | 575 List<int> bytes = md5.convert(path.codeUnits).bytes; |
568 return hex.encode(bytes); | 576 return hex.encode(bytes); |
569 } | 577 } |
578 | |
579 /** | |
580 * Record the fact that the given [plugin] responded to a request with the | |
581 * given [method] in the given [time]. | |
582 */ | |
583 static void recordResponseTime(PluginInfo plugin, String method, int time) { | |
584 pluginResponseTimes | |
585 .putIfAbsent(plugin, () => <String, List<int>>{}) | |
586 .putIfAbsent(method, () => <int>[]) | |
587 .add(time); | |
588 } | |
570 } | 589 } |
571 | 590 |
572 /** | 591 /** |
573 * Information about the execution a single plugin. | 592 * Information about the execution a single plugin. |
574 */ | 593 */ |
575 @visibleForTesting | 594 @visibleForTesting |
576 class PluginSession { | 595 class PluginSession { |
577 /** | 596 /** |
597 * The maximum number of milliseconds that server should wait for a response | |
598 * from a plugin before deciding that the plugin is hung. | |
599 */ | |
600 static const Duration MAXIMUM_RESPONSE_TIME = const Duration(minutes: 2); | |
601 | |
602 /** | |
603 * The length of time to wait after sending a 'plugin.shutdown' request before | |
604 * a failure to terminate will cause the isolate to be killed. | |
605 */ | |
606 static const Duration WAIT_FOR_SHUTDOWN_DURATION = | |
607 const Duration(seconds: 10); | |
608 | |
609 /** | |
578 * The information about the plugin being executed. | 610 * The information about the plugin being executed. |
579 */ | 611 */ |
580 final PluginInfo info; | 612 final PluginInfo info; |
581 | 613 |
582 /** | 614 /** |
583 * The completer used to signal when the plugin has stopped. | 615 * The completer used to signal when the plugin has stopped. |
584 */ | 616 */ |
585 Completer<Null> pluginStoppedCompleter = new Completer<Null>(); | 617 Completer<Null> pluginStoppedCompleter = new Completer<Null>(); |
586 | 618 |
587 /** | 619 /** |
588 * The channel used to communicate with the plugin. | 620 * The channel used to communicate with the plugin. |
589 */ | 621 */ |
590 ServerCommunicationChannel channel; | 622 ServerCommunicationChannel channel; |
591 | 623 |
592 /** | 624 /** |
593 * The index of the next request to be sent to the plugin. | 625 * The index of the next request to be sent to the plugin. |
594 */ | 626 */ |
595 int requestId = 0; | 627 int requestId = 0; |
596 | 628 |
597 /** | 629 /** |
598 * A table mapping the id's of requests to the functions used to handle the | 630 * A table mapping the id's of requests to the functions used to handle the |
599 * response to those requests. | 631 * response to those requests. |
600 */ | 632 */ |
601 Map<String, Completer<Response>> pendingRequests = | 633 Map<String, _PendingRequest> pendingRequests = <String, _PendingRequest>{}; |
602 <String, Completer<Response>>{}; | |
603 | 634 |
604 /** | 635 /** |
605 * A boolean indicating whether the plugin is compatible with the version of | 636 * A boolean indicating whether the plugin is compatible with the version of |
606 * the plugin API being used by this server. | 637 * the plugin API being used by this server. |
607 */ | 638 */ |
608 bool isCompatible = true; | 639 bool isCompatible = true; |
609 | 640 |
610 /** | 641 /** |
611 * The contact information to include when reporting problems related to the | 642 * The contact information to include when reporting problems related to the |
612 * plugin. | 643 * plugin. |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
682 // print(' $message'); | 713 // print(' $message'); |
683 // print(' ${new StackTrace.fromString(stackTrace)}'); | 714 // print(' ${new StackTrace.fromString(stackTrace)}'); |
684 // pluginStoppedCompleter.completeError(message, new StackTrace.fromString(st ackTrace)); | 715 // pluginStoppedCompleter.completeError(message, new StackTrace.fromString(st ackTrace)); |
685 } | 716 } |
686 | 717 |
687 /** | 718 /** |
688 * Handle a [response] from the plugin by completing the future that was | 719 * Handle a [response] from the plugin by completing the future that was |
689 * created when the request was sent. | 720 * created when the request was sent. |
690 */ | 721 */ |
691 void handleResponse(Response response) { | 722 void handleResponse(Response response) { |
692 Completer<Response> completer = pendingRequests.remove(response.id); | 723 _PendingRequest requestData = pendingRequests.remove(response.id); |
724 int responseTime = new DateTime.now().millisecondsSinceEpoch; | |
725 int duration = responseTime - requestData.requestTime; | |
726 PluginManager.recordResponseTime(info, requestData.method, duration); | |
727 Completer<Response> completer = requestData.completer; | |
693 if (completer != null) { | 728 if (completer != null) { |
694 completer.complete(response); | 729 completer.complete(response); |
695 } | 730 } |
696 } | 731 } |
697 | 732 |
698 /** | 733 /** |
734 * Return `true` if there are any requests that have not been responded to | |
735 * within the maximum allowed amount of time. | |
736 */ | |
737 bool isNonResponsive() { | |
738 // TODO(brianwilkerson) Figure out when to invoke this method in order to | |
739 // identify non-responsive plugins and kill them. | |
740 int cutOffTime = new DateTime.now().millisecondsSinceEpoch - | |
741 MAXIMUM_RESPONSE_TIME.inMilliseconds; | |
742 for (var requestData in pendingRequests.values) { | |
743 if (requestData.requestTime < cutOffTime) { | |
744 return true; | |
745 } | |
746 } | |
747 return false; | |
748 } | |
749 | |
750 /** | |
699 * Send a request, based on the given [parameters]. Return a future that will | 751 * Send a request, based on the given [parameters]. Return a future that will |
700 * complete when a response is received. | 752 * complete when a response is received. |
701 */ | 753 */ |
702 Future<Response> sendRequest(RequestParams parameters) { | 754 Future<Response> sendRequest(RequestParams parameters) { |
703 if (channel == null) { | 755 if (channel == null) { |
704 throw new StateError( | 756 throw new StateError( |
705 'Cannot send a request to a plugin that has stopped.'); | 757 'Cannot send a request to a plugin that has stopped.'); |
706 } | 758 } |
707 String id = nextRequestId; | 759 String id = nextRequestId; |
708 Completer<Response> completer = new Completer(); | 760 Completer<Response> completer = new Completer(); |
709 pendingRequests[id] = completer; | 761 int requestTime = new DateTime.now().millisecondsSinceEpoch; |
710 channel.sendRequest(parameters.toRequest(id)); | 762 Request request = parameters.toRequest(id); |
763 pendingRequests[id] = | |
764 new _PendingRequest(request.method, requestTime, completer); | |
765 channel.sendRequest(request); | |
711 return completer.future; | 766 return completer.future; |
712 } | 767 } |
713 | 768 |
714 /** | 769 /** |
715 * Start a new isolate that is running this plugin. The plugin will be sent | 770 * Start a new isolate that is running this plugin. The plugin will be sent |
716 * the given [byteStorePath]. Return `true` if the plugin is compatible and | 771 * the given [byteStorePath]. Return `true` if the plugin is compatible and |
717 * running. | 772 * running. |
718 */ | 773 */ |
719 Future<bool> start(String byteStorePath, String sdkPath) async { | 774 Future<bool> start(String byteStorePath, String sdkPath) async { |
720 if (channel != null) { | 775 if (channel != null) { |
(...skipping 29 matching lines...) Expand all Loading... | |
750 return true; | 805 return true; |
751 } | 806 } |
752 | 807 |
753 /** | 808 /** |
754 * Request that the plugin shutdown. | 809 * Request that the plugin shutdown. |
755 */ | 810 */ |
756 Future<Null> stop() { | 811 Future<Null> stop() { |
757 if (channel == null) { | 812 if (channel == null) { |
758 throw new StateError('Cannot stop a plugin that is not running.'); | 813 throw new StateError('Cannot stop a plugin that is not running.'); |
759 } | 814 } |
760 // TODO(brianwilkerson) Ensure that the isolate is killed if it does not | |
761 // terminate normally. | |
762 sendRequest(new PluginShutdownParams()); | 815 sendRequest(new PluginShutdownParams()); |
816 new Future.delayed(WAIT_FOR_SHUTDOWN_DURATION, () { | |
817 if (channel != null) { | |
818 channel.kill(); | |
819 channel = null; | |
820 } | |
821 }); | |
763 return pluginStoppedCompleter.future; | 822 return pluginStoppedCompleter.future; |
764 } | 823 } |
765 } | 824 } |
825 | |
826 /** | |
827 * Information about a request that has been sent but for which a response has | |
828 * not yet been received. | |
829 */ | |
830 class _PendingRequest { | |
831 /** | |
832 * The method of the request. | |
833 */ | |
834 final String method; | |
835 | |
836 /** | |
837 * The time at which the request was sent to the plugin. | |
838 */ | |
839 final int requestTime; | |
scheglov
2017/05/18 21:36:16
Or you could use new Stopwatch()..start() instead.
Brian Wilkerson
2017/05/18 21:51:39
I hadn't considered that, but now that I think abo
scheglov
2017/05/18 21:55:55
It's fine with my any way, just thought about othe
| |
840 | |
841 /** | |
842 * The completer that will be used to complete the future when the response is | |
843 * received from the plugin. | |
844 */ | |
845 final Completer<Response> completer; | |
846 | |
847 /** | |
848 * Initialize a pending request. | |
849 */ | |
850 _PendingRequest(this.method, this.requestTime, this.completer); | |
851 } | |
OLD | NEW |