Mercurial > public > mercurial-scm > hg
annotate mercurial/thirdparty/selectors2.py @ 45892:06b64fabf91c
copies: cache the ancestor checking call when tracing copy
A good share of the time spent in this function is spent doing ancestors
checking. To avoid spending time in duplicated call, we cache the result of
calls.
In the slower case, this provide a quite significant performance boost. Below
are the result for a set of selected pairs (many of them pathological):
(And further down is another table that summarize the current state of filelog
based vs changeset base copy tracing)
The benchmark have been configured to be killed after 6 minutes of runtime,
which mean that any detect slower than 2 minutes will be marked as "killed".
This drop some useful information about how much slower these case are? but also
prevent 99% of the benchmark time to be spent on case that can be labelled "very
slow" anyway.
Repo Case Source-Rev Dest-Rev Old-Time New-Time Difference Factor
------------------------------------------------------------------------------------------------------------------------------------
mercurial x_revs_x_added_0_copies ad6b123de1c7 39cfcef4f463 : 0.000044 s, 0.000044 s, +0.000000 s, ? 1.0000
mercurial x_revs_x_added_x_copies 2b1c78674230 0c1d10351869 : 0.000138 s, 0.000138 s, +0.000000 s, ? 1.0000
mercurial x000_revs_x000_added_x_copies 81f8ff2a9bf2 dd3267698d84 : 0.005067 s, 0.005052 s, -0.000015 s, ? 0.9970
pypy x_revs_x_added_0_copies aed021ee8ae8 099ed31b181b : 0.000218 s, 0.000219 s, +0.000001 s, ? 1.0046
pypy x_revs_x000_added_0_copies 4aa4e1f8e19a 359343b9ac0e : 0.000053 s, 0.000055 s, +0.000002 s, ? 1.0377
pypy x_revs_x_added_x_copies ac52eb7bbbb0 72e022663155 : 0.000125 s, 0.000128 s, +0.000003 s, ? 1.0240
pypy x_revs_x00_added_x_copies c3b14617fbd7 ace7255d9a26 : 0.001098 s, 0.001089 s, -0.000009 s, ? 0.9918
pypy x_revs_x000_added_x000_copies df6f7a526b60 a83dc6a2d56f : 0.017546 s, 0.017407 s, -0.000139 s, ? 0.9921
pypy x000_revs_xx00_added_0_copies 89a76aede314 2f22446ff07e : 0.096723 s, 0.094175 s, -0.002548 s, ? 0.9737
pypy x000_revs_x000_added_x_copies 8a3b5bfd266e 2c68e87c3efe : 0.271796 s, 0.238009 s, -0.033787 s, ? 0.8757
pypy x000_revs_x000_added_x000_copies 89a76aede314 7b3dda341c84 : 0.128602 s, 0.125876 s, -0.002726 s, ? 0.9788
pypy x0000_revs_x_added_0_copies d1defd0dc478 c9cb1334cc78 : 7.086742 s, 3.581556 s, -3.505186 s, ? 0.5054
pypy x0000_revs_xx000_added_0_copies bf2c629d0071 4ffed77c095c : 0.016634 s, 0.016721 s, +0.000087 s, ? 1.0052
pypy x0000_revs_xx000_added_x000_copies 08ea3258278e d9fa043f30c0 : 0.254225 s, 0.242367 s, -0.011858 s, ? 0.9534
netbeans x_revs_x_added_0_copies fb0955ffcbcd a01e9239f9e7 : 0.000166 s, 0.000165 s, -0.000001 s, ? 0.9940
netbeans x_revs_x000_added_0_copies 6f360122949f 20eb231cc7d0 : 0.000118 s, 0.000114 s, -0.000004 s, ? 0.9661
netbeans x_revs_x_added_x_copies 1ada3faf6fb6 5a39d12eecf4 : 0.000296 s, 0.000296 s, +0.000000 s, ? 1.0000
netbeans x_revs_x00_added_x_copies 35be93ba1e2c 9eec5e90c05f : 0.001137 s, 0.001124 s, -0.000013 s, ? 0.9886
netbeans x000_revs_xx00_added_0_copies eac3045b4fdd 51d4ae7f1290 : 0.014133 s, 0.013060 s, -0.001073 s, ? 0.9241
netbeans x000_revs_x000_added_x_copies e2063d266acd 6081d72689dc : 0.016988 s, 0.017112 s, +0.000124 s, ? 1.0073
netbeans x000_revs_x000_added_x000_copies ff453e9fee32 411350406ec2 : 0.676361 s, 0.660350 s, -0.016011 s, ? 0.9763
netbeans x0000_revs_xx000_added_x000_copies 588c2d1ced70 1aad62e59ddd : 12.515149 s, 10.032499 s, -2.482650 s, ? 0.8016
mozilla-central x_revs_x_added_0_copies 3697f962bb7b 7015fcdd43a2 : 0.000186 s, 0.000189 s, +0.000003 s, ? 1.0161
mozilla-central x_revs_x000_added_0_copies dd390860c6c9 40d0c5bed75d : 0.000459 s, 0.000462 s, +0.000003 s, ? 1.0065
mozilla-central x_revs_x_added_x_copies 8d198483ae3b 14207ffc2b2f : 0.000273 s, 0.000270 s, -0.000003 s, ? 0.9890
mozilla-central x_revs_x00_added_x_copies 98cbc58cc6bc 446a150332c3 : 0.001503 s, 0.001474 s, -0.000029 s, ? 0.9807
mozilla-central x_revs_x000_added_x000_copies 3c684b4b8f68 0a5e72d1b479 : 0.004862 s, 0.004806 s, -0.000056 s, ? 0.9885
mozilla-central x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 0.088291 s, 0.085150 s, -0.003141 s, ? 0.9644
mozilla-central x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 0.007113 s, 0.007064 s, -0.000049 s, ? 0.9931
mozilla-central x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 0.004687 s, 0.004741 s, +0.000054 s, ? 1.0115
mozilla-central x000_revs_x000_added_x000_copies 7c97034feb78 4407bd0c6330 : 0.198710 s, 0.190133 s, -0.008577 s, ? 0.9568
mozilla-central x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 0.036068 s, 0.035651 s, -0.000417 s, ? 0.9884
mozilla-central x0000_revs_xx000_added_x000_copies f78c615a656c 96a38b690156 : 0.465362 s, 0.440694 s, -0.024668 s, ? 0.9470
mozilla-central x00000_revs_x0000_added_x0000_copies 6832ae71433c 4c222a1d9a00 : 24.519684 s, 18.454163 s, -6.065521 s, ? 0.7526
mozilla-central x00000_revs_x00000_added_x000_copies 76caed42cf7c 1daa622bbe42 : 42.711897 s, 31.562719 s, -11.149178 s, ? 0.7390
mozilla-try x_revs_x_added_0_copies aaf6dde0deb8 9790f499805a : 0.001201 s, 0.001189 s, -0.000012 s, ? 0.9900
mozilla-try x_revs_x000_added_0_copies d8d0222927b4 5bb8ce8c7450 : 0.001216 s, 0.001204 s, -0.000012 s, ? 0.9901
mozilla-try x_revs_x_added_x_copies 092fcca11bdb 936255a0384a : 0.000595 s, 0.000586 s, -0.000009 s, ? 0.9849
mozilla-try x_revs_x00_added_x_copies b53d2fadbdb5 017afae788ec : 0.001856 s, 0.001845 s, -0.000011 s, ? 0.9941
mozilla-try x_revs_x000_added_x000_copies 20408ad61ce5 6f0ee96e21ad : 0.064936 s, 0.063822 s, -0.001114 s, ? 0.9828
mozilla-try x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 0.090601 s, 0.088038 s, -0.002563 s, ? 0.9717
mozilla-try x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 0.007510 s, 0.007389 s, -0.000121 s, ? 0.9839
mozilla-try x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 0.004911 s, 0.004868 s, -0.000043 s, ? 0.9912
mozilla-try x000_revs_x000_added_x000_copies 1346fd0130e4 4c65cbdabc1f : 0.233231 s, 0.222450 s, -0.010781 s, ? 0.9538
mozilla-try x0000_revs_x_added_0_copies 63519bfd42ee a36a2a865d92 : 0.419989 s, 0.370675 s, -0.049314 s, ? 0.8826
mozilla-try x0000_revs_x_added_x_copies 9fe69ff0762d bcabf2a78927 : 0.401521 s, 0.358020 s, -0.043501 s, ? 0.8917
mozilla-try x0000_revs_xx000_added_x_copies 156f6e2674f2 4d0f2c178e66 : 0.179555 s, 0.145235 s, -0.034320 s, ? 0.8089
mozilla-try x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 0.038004 s, 0.037606 s, -0.000398 s, ? 0.9895
mozilla-try x0000_revs_xx000_added_x000_copies 89294cd501d9 7ccb2fc7ccb5 : 52.838482 s, 7.382439 s, -45.456043 s, ? 0.1397
mozilla-try x0000_revs_x0000_added_x0000_copies e928c65095ed e951f4ad123a : 8.705874 s, 7.273506 s, -1.432368 s, ? 0.8355
mozilla-try x00000_revs_x00000_added_0_copies dc8a3ca7010e d16fde900c9c : 1.126708 s, 1.074593 s, -0.052115 s, ? 0.9537
mozilla-try x00000_revs_x0000_added_x0000_copies 8d3fafa80d4b eb884023b810 : 83.854020 s, 27.746195 s, -56.107825 s, ? 0.3309
Below is a table comparing the runtime of the current "filelog centric"
algorithm, with the "changeset centric" one, we just modified.
The changeset centric algorithm is a significant win in many scenario, but they
are still various cases where it is quite slower. When many revision has to be
considered the cost of retrieving the copy information, creating new
dictionaries, merging dictionaries and checking if revision are ancestors of
each other can slow things down.
The rest of this series, will introduce a rust version of the copy tracing code
to deal with most of theses issues.
Repo Case Source-Rev Dest-Rev filelog sidedata Difference Factor
---------------------------------------------------------------------------------------------------------------------------------------
mercurial x_revs_x_added_0_copies ad6b123de1c7 39cfcef4f463 : 0.000914 s, 0.000044 s, - 0.000870 s, ? 0.048140
mercurial x_revs_x_added_x_copies 2b1c78674230 0c1d10351869 : 0.001812 s, 0.000138 s, - 0.001674 s, ? 0.076159
mercurial x000_revs_x000_added_x_copies 81f8ff2a9bf2 dd3267698d84 : 0.017954 s, 0.005052 s, - 0.012902 s, ? 0.281386
pypy x_revs_x_added_0_copies aed021ee8ae8 099ed31b181b : 0.001509 s, 0.000219 s, - 0.001290 s, ? 0.145129
pypy x_revs_x000_added_0_copies 4aa4e1f8e19a 359343b9ac0e : 0.206881 s, 0.000055 s, - 0.206826 s, ? 0.000266
pypy x_revs_x_added_x_copies ac52eb7bbbb0 72e022663155 : 0.016951 s, 0.000128 s, - 0.016823 s, ? 0.007551
pypy x_revs_x00_added_x_copies c3b14617fbd7 ace7255d9a26 : 0.019096 s, 0.001089 s, - 0.018007 s, ? 0.057028
pypy x_revs_x000_added_x000_copies df6f7a526b60 a83dc6a2d56f : 0.762506 s, 0.017407 s, - 0.745099 s, ? 0.022829
pypy x000_revs_xx00_added_0_copies 89a76aede314 2f22446ff07e : 1.179211 s, 0.094175 s, - 1.085036 s, ? 0.079863
pypy x000_revs_x000_added_x_copies 8a3b5bfd266e 2c68e87c3efe : 1.249058 s, 0.238009 s, - 1.011049 s, ? 0.190551
pypy x000_revs_x000_added_x000_copies 89a76aede314 7b3dda341c84 : 1.614107 s, 0.125876 s, - 1.488231 s, ? 0.077985
pypy x0000_revs_x_added_0_copies d1defd0dc478 c9cb1334cc78 : 0.001064 s, 3.581556 s, + 3.580492 s, ? 3366.124060
pypy x0000_revs_xx000_added_0_copies bf2c629d0071 4ffed77c095c : 1.061275 s, 0.016721 s, - 1.044554 s, ? 0.015756
pypy x0000_revs_xx000_added_x000_copies 08ea3258278e d9fa043f30c0 : 1.341119 s, 0.242367 s, - 1.098752 s, ? 0.180720
netbeans x_revs_x_added_0_copies fb0955ffcbcd a01e9239f9e7 : 0.027803 s, 0.000165 s, - 0.027638 s, ? 0.005935
netbeans x_revs_x000_added_0_copies 6f360122949f 20eb231cc7d0 : 0.130014 s, 0.000114 s, - 0.129900 s, ? 0.000877
netbeans x_revs_x_added_x_copies 1ada3faf6fb6 5a39d12eecf4 : 0.024990 s, 0.000296 s, - 0.024694 s, ? 0.011845
netbeans x_revs_x00_added_x_copies 35be93ba1e2c 9eec5e90c05f : 0.052201 s, 0.001124 s, - 0.051077 s, ? 0.021532
netbeans x000_revs_xx00_added_0_copies eac3045b4fdd 51d4ae7f1290 : 0.037642 s, 0.013060 s, - 0.024582 s, ? 0.346953
netbeans x000_revs_x000_added_x_copies e2063d266acd 6081d72689dc : 0.197086 s, 0.017112 s, - 0.179974 s, ? 0.086825
netbeans x000_revs_x000_added_x000_copies ff453e9fee32 411350406ec2 : 0.935148 s, 0.660350 s, - 0.274798 s, ? 0.706145
netbeans x0000_revs_xx000_added_x000_copies 588c2d1ced70 1aad62e59ddd : 3.920674 s, 10.032499 s, + 6.111825 s, ? 2.558871
mozilla-central x_revs_x_added_0_copies 3697f962bb7b 7015fcdd43a2 : 0.024232 s, 0.000189 s, - 0.024043 s, ? 0.007800
mozilla-central x_revs_x000_added_0_copies dd390860c6c9 40d0c5bed75d : 0.141483 s, 0.000462 s, - 0.141021 s, ? 0.003265
mozilla-central x_revs_x_added_x_copies 8d198483ae3b 14207ffc2b2f : 0.025775 s, 0.000270 s, - 0.025505 s, ? 0.010475
mozilla-central x_revs_x00_added_x_copies 98cbc58cc6bc 446a150332c3 : 0.084922 s, 0.001474 s, - 0.083448 s, ? 0.017357
mozilla-central x_revs_x000_added_x000_copies 3c684b4b8f68 0a5e72d1b479 : 0.194784 s, 0.004806 s, - 0.189978 s, ? 0.024673
mozilla-central x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 2.161103 s, 0.085150 s, - 2.075953 s, ? 0.039401
mozilla-central x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 0.089347 s, 0.007064 s, - 0.082283 s, ? 0.079063
mozilla-central x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 0.732171 s, 0.004741 s, - 0.727430 s, ? 0.006475
mozilla-central x000_revs_x000_added_x000_copies 7c97034feb78 4407bd0c6330 : 1.157287 s, 0.190133 s, - 0.967154 s, ? 0.164292
mozilla-central x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 6.726568 s, 0.035651 s, - 6.690917 s, ? 0.005300
mozilla-central x0000_revs_xx000_added_x000_copies f78c615a656c 96a38b690156 : 3.266229 s, 0.440694 s, - 2.825535 s, ? 0.134924
mozilla-central x00000_revs_x0000_added_x0000_copies 6832ae71433c 4c222a1d9a00 : 15.860534 s, 18.454163 s, + 2.593629 s, ? 1.163527
mozilla-central x00000_revs_x00000_added_x000_copies 76caed42cf7c 1daa622bbe42 : 20.450475 s, 31.562719 s, +11.112244 s, ? 1.543373
mozilla-try x_revs_x_added_0_copies aaf6dde0deb8 9790f499805a : 0.080442 s, 0.001189 s, - 0.079253 s, ? 0.014781
mozilla-try x_revs_x000_added_0_copies d8d0222927b4 5bb8ce8c7450 : 0.497672 s, 0.001204 s, - 0.496468 s, ? 0.002419
mozilla-try x_revs_x_added_x_copies 092fcca11bdb 936255a0384a : 0.021183 s, 0.000586 s, - 0.020597 s, ? 0.027664
mozilla-try x_revs_x00_added_x_copies b53d2fadbdb5 017afae788ec : 0.230991 s, 0.001845 s, - 0.229146 s, ? 0.007987
mozilla-try x_revs_x000_added_x000_copies 20408ad61ce5 6f0ee96e21ad : 1.118461 s, 0.063822 s, - 1.054639 s, ? 0.057062
mozilla-try x_revs_x0000_added_x0000_copies effb563bb7e5 c07a39dc4e80 : 2.206083 s, 0.088038 s, - 2.118045 s, ? 0.039907
mozilla-try x000_revs_xx00_added_0_copies 6100d773079a 04a55431795e : 0.089404 s, 0.007389 s, - 0.082015 s, ? 0.082647
mozilla-try x000_revs_x000_added_x_copies 9f17a6fc04f9 2d37b966abed : 0.733043 s, 0.004868 s, - 0.728175 s, ? 0.006641
mozilla-try x000_revs_x000_added_x000_copies 1346fd0130e4 4c65cbdabc1f : 1.163367 s, 0.222450 s, - 0.940917 s, ? 0.191212
mozilla-try x0000_revs_x_added_0_copies 63519bfd42ee a36a2a865d92 : 0.085456 s, 0.370675 s, + 0.285219 s, ? 4.337612
mozilla-try x0000_revs_x_added_x_copies 9fe69ff0762d bcabf2a78927 : 0.083601 s, 0.358020 s, + 0.274419 s, ? 4.282485
mozilla-try x0000_revs_xx000_added_x_copies 156f6e2674f2 4d0f2c178e66 : 7.366614 s, 0.145235 s, - 7.221379 s, ? 0.019715
mozilla-try x0000_revs_xx000_added_0_copies 9eec5917337d 67118cc6dcad : 6.664464 s, 0.037606 s, - 6.626858 s, ? 0.005643
mozilla-try x0000_revs_xx000_added_x000_copies 89294cd501d9 7ccb2fc7ccb5 : 7.467836 s, 7.382439 s, - 0.085397 s, ? 0.988565
mozilla-try x0000_revs_x0000_added_x0000_copies e928c65095ed e951f4ad123a : 9.801294 s, 7.273506 s, - 2.527788 s, ? 0.742097
mozilla-try x00000_revs_x_added_0_copies 6a320851d377 1ebb79acd503 : 0.091886 s, killed
mozilla-try x00000_revs_x00000_added_0_copies dc8a3ca7010e d16fde900c9c : 26.491140 s, 1.074593 s, -25.416547 s, ? 0.040564
mozilla-try x00000_revs_x_added_x_copies 5173c4b6f97c 95d83ee7242d : 0.092863 s, killed
mozilla-try x00000_revs_x000_added_x_copies 9126823d0e9c ca82787bb23c : 0.226823 s, killed
mozilla-try x00000_revs_x0000_added_x0000_copies 8d3fafa80d4b eb884023b810 : 18.914630 s, 27.746195 s, + 8.831565 s, ? 1.466917
mozilla-try x00000_revs_x00000_added_x0000_copies 1b661134e2ca 1ae03d022d6d : 21.198903 s, killed
mozilla-try x00000_revs_x00000_added_x000_copies 9b2a99adc05e 8e29777b48e6 : 24.952268 s, killed
Differential Revision: https://phab.mercurial-scm.org/D9296
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Mon, 02 Nov 2020 11:03:56 +0100 |
parents | d1bda397df73 |
children |
rev | line source |
---|---|
33502 | 1 """ Back-ported, durable, and portable selectors """ |
2 | |
3 # MIT License | |
4 # | |
5 # Copyright (c) 2017 Seth Michael Larson | |
6 # | |
7 # Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 # of this software and associated documentation files (the "Software"), to deal | |
9 # in the Software without restriction, including without limitation the rights | |
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 # copies of the Software, and to permit persons to whom the Software is | |
12 # furnished to do so, subject to the following conditions: | |
13 # | |
14 # The above copyright notice and this permission notice shall be included in all | |
15 # copies or substantial portions of the Software. | |
16 # | |
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 # SOFTWARE. | |
24 | |
25 from __future__ import absolute_import | |
26 | |
27 import collections | |
28 import errno | |
29 import math | |
30 import select | |
31 import socket | |
32 import sys | |
33 import time | |
34 | |
35245
414114a7c18f
thirdparty: move selectors2 module to where it should be
Yuya Nishihara <yuya@tcha.org>
parents:
34639
diff
changeset
|
35 from .. import pycompat |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
36 |
33502 | 37 namedtuple = collections.namedtuple |
38 Mapping = collections.Mapping | |
39 | |
40 try: | |
41 monotonic = time.monotonic | |
42 except AttributeError: | |
43 monotonic = time.time | |
44 | |
45 __author__ = 'Seth Michael Larson' | |
46 __email__ = 'sethmichaellarson@protonmail.com' | |
47 __version__ = '2.0.0' | |
48 __license__ = 'MIT' | |
49 __url__ = 'https://www.github.com/SethMichaelLarson/selectors2' | |
50 | |
51 __all__ = ['EVENT_READ', | |
52 'EVENT_WRITE', | |
53 'SelectorKey', | |
54 'DefaultSelector', | |
55 'BaseSelector'] | |
56 | |
57 EVENT_READ = (1 << 0) | |
58 EVENT_WRITE = (1 << 1) | |
59 _DEFAULT_SELECTOR = None | |
60 _SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None. | |
61 _ERROR_TYPES = (OSError, IOError, socket.error) | |
62 | |
63 | |
64 SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data']) | |
65 | |
66 | |
67 class _SelectorMapping(Mapping): | |
68 """ Mapping of file objects to selector keys """ | |
69 | |
70 def __init__(self, selector): | |
71 self._selector = selector | |
72 | |
73 def __len__(self): | |
74 return len(self._selector._fd_to_key) | |
75 | |
76 def __getitem__(self, fileobj): | |
77 try: | |
78 fd = self._selector._fileobj_lookup(fileobj) | |
79 return self._selector._fd_to_key[fd] | |
80 except KeyError: | |
81 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
82 | |
83 def __iter__(self): | |
84 return iter(self._selector._fd_to_key) | |
85 | |
86 | |
87 def _fileobj_to_fd(fileobj): | |
88 """ Return a file descriptor from a file object. If | |
89 given an integer will simply return that integer back. """ | |
90 if isinstance(fileobj, int): | |
91 fd = fileobj | |
92 else: | |
93 try: | |
94 fd = int(fileobj.fileno()) | |
95 except (AttributeError, TypeError, ValueError): | |
96 raise ValueError("Invalid file object: {0!r}".format(fileobj)) | |
97 if fd < 0: | |
98 raise ValueError("Invalid file descriptor: {0}".format(fd)) | |
99 return fd | |
100 | |
101 | |
102 class BaseSelector(object): | |
103 """ Abstract Selector class | |
104 | |
105 A selector supports registering file objects to be monitored | |
106 for specific I/O events. | |
107 | |
108 A file object is a file descriptor or any object with a | |
109 `fileno()` method. An arbitrary object can be attached to the | |
110 file object which can be used for example to store context info, | |
111 a callback, etc. | |
112 | |
113 A selector can use various implementations (select(), poll(), epoll(), | |
114 and kqueue()) depending on the platform. The 'DefaultSelector' class uses | |
115 the most efficient implementation for the current platform. | |
116 """ | |
117 def __init__(self): | |
118 # Maps file descriptors to keys. | |
119 self._fd_to_key = {} | |
120 | |
121 # Read-only mapping returned by get_map() | |
122 self._map = _SelectorMapping(self) | |
123 | |
124 def _fileobj_lookup(self, fileobj): | |
125 """ Return a file descriptor from a file object. | |
126 This wraps _fileobj_to_fd() to do an exhaustive | |
127 search in case the object is invalid but we still | |
128 have it in our map. Used by unregister() so we can | |
129 unregister an object that was previously registered | |
130 even if it is closed. It is also used by _SelectorMapping | |
131 """ | |
132 try: | |
133 return _fileobj_to_fd(fileobj) | |
134 except ValueError: | |
135 | |
136 # Search through all our mapped keys. | |
137 for key in self._fd_to_key.values(): | |
138 if key.fileobj is fileobj: | |
139 return key.fd | |
140 | |
141 # Raise ValueError after all. | |
142 raise | |
143 | |
144 def register(self, fileobj, events, data=None): | |
145 """ Register a file object for a set of events to monitor. """ | |
146 if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)): | |
147 raise ValueError("Invalid events: {0!r}".format(events)) | |
148 | |
149 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data) | |
150 | |
151 if key.fd in self._fd_to_key: | |
152 raise KeyError("{0!r} (FD {1}) is already registered" | |
153 .format(fileobj, key.fd)) | |
154 | |
155 self._fd_to_key[key.fd] = key | |
156 return key | |
157 | |
158 def unregister(self, fileobj): | |
159 """ Unregister a file object from being monitored. """ | |
160 try: | |
161 key = self._fd_to_key.pop(self._fileobj_lookup(fileobj)) | |
162 except KeyError: | |
163 raise KeyError("{0!r} is not registered".format(fileobj)) | |
164 | |
165 # Getting the fileno of a closed socket on Windows errors with EBADF. | |
166 except socket.error as err: | |
167 if err.errno != errno.EBADF: | |
168 raise | |
169 else: | |
170 for key in self._fd_to_key.values(): | |
171 if key.fileobj is fileobj: | |
172 self._fd_to_key.pop(key.fd) | |
173 break | |
174 else: | |
175 raise KeyError("{0!r} is not registered".format(fileobj)) | |
176 return key | |
177 | |
178 def modify(self, fileobj, events, data=None): | |
179 """ Change a registered file object monitored events and data. """ | |
180 # NOTE: Some subclasses optimize this operation even further. | |
181 try: | |
182 key = self._fd_to_key[self._fileobj_lookup(fileobj)] | |
183 except KeyError: | |
184 raise KeyError("{0!r} is not registered".format(fileobj)) | |
185 | |
186 if events != key.events: | |
187 self.unregister(fileobj) | |
188 key = self.register(fileobj, events, data) | |
189 | |
190 elif data != key.data: | |
191 # Use a shortcut to update the data. | |
192 key = key._replace(data=data) | |
193 self._fd_to_key[key.fd] = key | |
194 | |
195 return key | |
196 | |
197 def select(self, timeout=None): | |
198 """ Perform the actual selection until some monitored file objects | |
199 are ready or the timeout expires. """ | |
200 raise NotImplementedError() | |
201 | |
202 def close(self): | |
203 """ Close the selector. This must be called to ensure that all | |
204 underlying resources are freed. """ | |
205 self._fd_to_key.clear() | |
206 self._map = None | |
207 | |
208 def get_key(self, fileobj): | |
209 """ Return the key associated with a registered file object. """ | |
210 mapping = self.get_map() | |
211 if mapping is None: | |
212 raise RuntimeError("Selector is closed") | |
213 try: | |
214 return mapping[fileobj] | |
215 except KeyError: | |
216 raise KeyError("{0!r} is not registered".format(fileobj)) | |
217 | |
218 def get_map(self): | |
219 """ Return a mapping of file objects to selector keys """ | |
220 return self._map | |
221 | |
222 def _key_from_fd(self, fd): | |
223 """ Return the key associated to a given file descriptor | |
224 Return None if it is not found. """ | |
225 try: | |
226 return self._fd_to_key[fd] | |
227 except KeyError: | |
228 return None | |
229 | |
230 def __enter__(self): | |
231 return self | |
232 | |
233 def __exit__(self, *_): | |
234 self.close() | |
235 | |
236 | |
237 # Almost all platforms have select.select() | |
238 if hasattr(select, "select"): | |
239 class SelectSelector(BaseSelector): | |
240 """ Select-based selector. """ | |
241 def __init__(self): | |
242 super(SelectSelector, self).__init__() | |
243 self._readers = set() | |
244 self._writers = set() | |
245 | |
246 def register(self, fileobj, events, data=None): | |
247 key = super(SelectSelector, self).register(fileobj, events, data) | |
248 if events & EVENT_READ: | |
249 self._readers.add(key.fd) | |
250 if events & EVENT_WRITE: | |
251 self._writers.add(key.fd) | |
252 return key | |
253 | |
254 def unregister(self, fileobj): | |
255 key = super(SelectSelector, self).unregister(fileobj) | |
256 self._readers.discard(key.fd) | |
257 self._writers.discard(key.fd) | |
258 return key | |
259 | |
260 def select(self, timeout=None): | |
261 # Selecting on empty lists on Windows errors out. | |
262 if not len(self._readers) and not len(self._writers): | |
263 return [] | |
264 | |
265 timeout = None if timeout is None else max(timeout, 0.0) | |
266 ready = [] | |
267 r, w, _ = _syscall_wrapper(self._wrap_select, True, self._readers, | |
268 self._writers, timeout) | |
269 r = set(r) | |
270 w = set(w) | |
271 for fd in r | w: | |
272 events = 0 | |
273 if fd in r: | |
274 events |= EVENT_READ | |
275 if fd in w: | |
276 events |= EVENT_WRITE | |
277 | |
278 key = self._key_from_fd(fd) | |
279 if key: | |
280 ready.append((key, events & key.events)) | |
281 return ready | |
282 | |
283 def _wrap_select(self, r, w, timeout=None): | |
284 """ Wrapper for select.select because timeout is a positional arg """ | |
285 return select.select(r, w, [], timeout) | |
286 | |
287 __all__.append('SelectSelector') | |
288 | |
289 # Jython has a different implementation of .fileno() for socket objects. | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
290 if pycompat.isjython: |
33502 | 291 class _JythonSelectorMapping(object): |
292 """ This is an implementation of _SelectorMapping that is built | |
293 for use specifically with Jython, which does not provide a hashable | |
294 value from socket.socket.fileno(). """ | |
295 | |
296 def __init__(self, selector): | |
297 assert isinstance(selector, JythonSelectSelector) | |
298 self._selector = selector | |
299 | |
300 def __len__(self): | |
301 return len(self._selector._sockets) | |
302 | |
303 def __getitem__(self, fileobj): | |
304 for sock, key in self._selector._sockets: | |
305 if sock is fileobj: | |
306 return key | |
307 else: | |
308 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
309 | |
310 class JythonSelectSelector(SelectSelector): | |
311 """ This is an implementation of SelectSelector that is for Jython | |
312 which works around that Jython's socket.socket.fileno() does not | |
313 return an integer fd value. All SelectorKey.fd will be equal to -1 | |
314 and should not be used. This instead uses object id to compare fileobj | |
315 and will only use select.select as it's the only selector that allows | |
316 directly passing in socket objects rather than registering fds. | |
317 See: http://bugs.jython.org/issue1678 | |
318 https://wiki.python.org/jython/NewSocketModule#socket.fileno.28.29_does_not_return_an_integer | |
319 """ | |
320 | |
321 def __init__(self): | |
322 super(JythonSelectSelector, self).__init__() | |
323 | |
324 self._sockets = [] # Uses a list of tuples instead of dictionary. | |
325 self._map = _JythonSelectorMapping(self) | |
326 self._readers = [] | |
327 self._writers = [] | |
328 | |
329 # Jython has a select.cpython_compatible_select function in older versions. | |
330 self._select_func = getattr(select, 'cpython_compatible_select', select.select) | |
331 | |
332 def register(self, fileobj, events, data=None): | |
333 for sock, _ in self._sockets: | |
334 if sock is fileobj: | |
335 raise KeyError("{0!r} is already registered" | |
336 .format(fileobj, sock)) | |
337 | |
338 key = SelectorKey(fileobj, -1, events, data) | |
339 self._sockets.append((fileobj, key)) | |
340 | |
341 if events & EVENT_READ: | |
342 self._readers.append(fileobj) | |
343 if events & EVENT_WRITE: | |
344 self._writers.append(fileobj) | |
345 return key | |
346 | |
347 def unregister(self, fileobj): | |
348 for i, (sock, key) in enumerate(self._sockets): | |
349 if sock is fileobj: | |
350 break | |
351 else: | |
352 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
353 | |
354 if key.events & EVENT_READ: | |
355 self._readers.remove(fileobj) | |
356 if key.events & EVENT_WRITE: | |
357 self._writers.remove(fileobj) | |
358 | |
359 del self._sockets[i] | |
360 return key | |
361 | |
362 def _wrap_select(self, r, w, timeout=None): | |
363 """ Wrapper for select.select because timeout is a positional arg """ | |
364 return self._select_func(r, w, [], timeout) | |
365 | |
366 __all__.append('JythonSelectSelector') | |
367 SelectSelector = JythonSelectSelector # Override so the wrong selector isn't used. | |
368 | |
369 | |
370 if hasattr(select, "poll"): | |
371 class PollSelector(BaseSelector): | |
372 """ Poll-based selector """ | |
373 def __init__(self): | |
374 super(PollSelector, self).__init__() | |
375 self._poll = select.poll() | |
376 | |
377 def register(self, fileobj, events, data=None): | |
378 key = super(PollSelector, self).register(fileobj, events, data) | |
379 event_mask = 0 | |
380 if events & EVENT_READ: | |
381 event_mask |= select.POLLIN | |
382 if events & EVENT_WRITE: | |
383 event_mask |= select.POLLOUT | |
384 self._poll.register(key.fd, event_mask) | |
385 return key | |
386 | |
387 def unregister(self, fileobj): | |
388 key = super(PollSelector, self).unregister(fileobj) | |
389 self._poll.unregister(key.fd) | |
390 return key | |
391 | |
392 def _wrap_poll(self, timeout=None): | |
393 """ Wrapper function for select.poll.poll() so that | |
394 _syscall_wrapper can work with only seconds. """ | |
395 if timeout is not None: | |
396 if timeout <= 0: | |
397 timeout = 0 | |
398 else: | |
399 # select.poll.poll() has a resolution of 1 millisecond, | |
400 # round away from zero to wait *at least* timeout seconds. | |
401 timeout = math.ceil(timeout * 1000) | |
402 | |
403 result = self._poll.poll(timeout) | |
404 return result | |
405 | |
406 def select(self, timeout=None): | |
407 ready = [] | |
408 fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) | |
409 for fd, event_mask in fd_events: | |
410 events = 0 | |
411 if event_mask & ~select.POLLIN: | |
412 events |= EVENT_WRITE | |
413 if event_mask & ~select.POLLOUT: | |
414 events |= EVENT_READ | |
415 | |
416 key = self._key_from_fd(fd) | |
417 if key: | |
418 ready.append((key, events & key.events)) | |
419 | |
420 return ready | |
421 | |
422 __all__.append('PollSelector') | |
423 | |
424 if hasattr(select, "epoll"): | |
425 class EpollSelector(BaseSelector): | |
426 """ Epoll-based selector """ | |
427 def __init__(self): | |
428 super(EpollSelector, self).__init__() | |
429 self._epoll = select.epoll() | |
430 | |
431 def fileno(self): | |
432 return self._epoll.fileno() | |
433 | |
434 def register(self, fileobj, events, data=None): | |
435 key = super(EpollSelector, self).register(fileobj, events, data) | |
436 events_mask = 0 | |
437 if events & EVENT_READ: | |
438 events_mask |= select.EPOLLIN | |
439 if events & EVENT_WRITE: | |
440 events_mask |= select.EPOLLOUT | |
441 _syscall_wrapper(self._epoll.register, False, key.fd, events_mask) | |
442 return key | |
443 | |
444 def unregister(self, fileobj): | |
445 key = super(EpollSelector, self).unregister(fileobj) | |
446 try: | |
447 _syscall_wrapper(self._epoll.unregister, False, key.fd) | |
448 except _ERROR_TYPES: | |
449 # This can occur when the fd was closed since registry. | |
450 pass | |
451 return key | |
452 | |
453 def select(self, timeout=None): | |
454 if timeout is not None: | |
455 if timeout <= 0: | |
456 timeout = 0.0 | |
457 else: | |
458 # select.epoll.poll() has a resolution of 1 millisecond | |
459 # but luckily takes seconds so we don't need a wrapper | |
460 # like PollSelector. Just for better rounding. | |
461 timeout = math.ceil(timeout * 1000) * 0.001 | |
462 timeout = float(timeout) | |
463 else: | |
464 timeout = -1.0 # epoll.poll() must have a float. | |
465 | |
466 # We always want at least 1 to ensure that select can be called | |
467 # with no file descriptors registered. Otherwise will fail. | |
468 max_events = max(len(self._fd_to_key), 1) | |
469 | |
470 ready = [] | |
471 fd_events = _syscall_wrapper(self._epoll.poll, True, | |
472 timeout=timeout, | |
473 maxevents=max_events) | |
474 for fd, event_mask in fd_events: | |
475 events = 0 | |
476 if event_mask & ~select.EPOLLIN: | |
477 events |= EVENT_WRITE | |
478 if event_mask & ~select.EPOLLOUT: | |
479 events |= EVENT_READ | |
480 | |
481 key = self._key_from_fd(fd) | |
482 if key: | |
483 ready.append((key, events & key.events)) | |
484 return ready | |
485 | |
486 def close(self): | |
487 self._epoll.close() | |
488 super(EpollSelector, self).close() | |
489 | |
490 __all__.append('EpollSelector') | |
491 | |
492 | |
493 if hasattr(select, "devpoll"): | |
494 class DevpollSelector(BaseSelector): | |
495 """Solaris /dev/poll selector.""" | |
496 | |
497 def __init__(self): | |
498 super(DevpollSelector, self).__init__() | |
499 self._devpoll = select.devpoll() | |
500 | |
501 def fileno(self): | |
502 return self._devpoll.fileno() | |
503 | |
504 def register(self, fileobj, events, data=None): | |
505 key = super(DevpollSelector, self).register(fileobj, events, data) | |
506 poll_events = 0 | |
507 if events & EVENT_READ: | |
508 poll_events |= select.POLLIN | |
509 if events & EVENT_WRITE: | |
510 poll_events |= select.POLLOUT | |
511 self._devpoll.register(key.fd, poll_events) | |
512 return key | |
513 | |
514 def unregister(self, fileobj): | |
515 key = super(DevpollSelector, self).unregister(fileobj) | |
516 self._devpoll.unregister(key.fd) | |
517 return key | |
518 | |
519 def _wrap_poll(self, timeout=None): | |
520 """ Wrapper function for select.poll.poll() so that | |
521 _syscall_wrapper can work with only seconds. """ | |
522 if timeout is not None: | |
523 if timeout <= 0: | |
524 timeout = 0 | |
525 else: | |
526 # select.devpoll.poll() has a resolution of 1 millisecond, | |
527 # round away from zero to wait *at least* timeout seconds. | |
528 timeout = math.ceil(timeout * 1000) | |
529 | |
530 result = self._devpoll.poll(timeout) | |
531 return result | |
532 | |
533 def select(self, timeout=None): | |
534 ready = [] | |
535 fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) | |
536 for fd, event_mask in fd_events: | |
537 events = 0 | |
538 if event_mask & ~select.POLLIN: | |
539 events |= EVENT_WRITE | |
540 if event_mask & ~select.POLLOUT: | |
541 events |= EVENT_READ | |
542 | |
543 key = self._key_from_fd(fd) | |
544 if key: | |
545 ready.append((key, events & key.events)) | |
546 | |
547 return ready | |
548 | |
549 def close(self): | |
550 self._devpoll.close() | |
551 super(DevpollSelector, self).close() | |
552 | |
553 __all__.append('DevpollSelector') | |
554 | |
555 | |
556 if hasattr(select, "kqueue"): | |
557 class KqueueSelector(BaseSelector): | |
558 """ Kqueue / Kevent-based selector """ | |
559 def __init__(self): | |
560 super(KqueueSelector, self).__init__() | |
561 self._kqueue = select.kqueue() | |
562 | |
563 def fileno(self): | |
564 return self._kqueue.fileno() | |
565 | |
566 def register(self, fileobj, events, data=None): | |
567 key = super(KqueueSelector, self).register(fileobj, events, data) | |
568 if events & EVENT_READ: | |
569 kevent = select.kevent(key.fd, | |
570 select.KQ_FILTER_READ, | |
571 select.KQ_EV_ADD) | |
572 | |
573 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
574 | |
575 if events & EVENT_WRITE: | |
576 kevent = select.kevent(key.fd, | |
577 select.KQ_FILTER_WRITE, | |
578 select.KQ_EV_ADD) | |
579 | |
580 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
581 | |
582 return key | |
583 | |
584 def unregister(self, fileobj): | |
585 key = super(KqueueSelector, self).unregister(fileobj) | |
586 if key.events & EVENT_READ: | |
587 kevent = select.kevent(key.fd, | |
588 select.KQ_FILTER_READ, | |
589 select.KQ_EV_DELETE) | |
590 try: | |
591 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
592 except _ERROR_TYPES: | |
593 pass | |
594 if key.events & EVENT_WRITE: | |
595 kevent = select.kevent(key.fd, | |
596 select.KQ_FILTER_WRITE, | |
597 select.KQ_EV_DELETE) | |
598 try: | |
599 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
600 except _ERROR_TYPES: | |
601 pass | |
602 | |
603 return key | |
604 | |
605 def select(self, timeout=None): | |
606 if timeout is not None: | |
607 timeout = max(timeout, 0) | |
608 | |
609 max_events = len(self._fd_to_key) * 2 | |
610 ready_fds = {} | |
611 | |
612 kevent_list = _syscall_wrapper(self._kqueue.control, True, | |
613 None, max_events, timeout) | |
614 | |
615 for kevent in kevent_list: | |
616 fd = kevent.ident | |
617 event_mask = kevent.filter | |
618 events = 0 | |
619 if event_mask == select.KQ_FILTER_READ: | |
620 events |= EVENT_READ | |
621 if event_mask == select.KQ_FILTER_WRITE: | |
622 events |= EVENT_WRITE | |
623 | |
624 key = self._key_from_fd(fd) | |
625 if key: | |
626 if key.fd not in ready_fds: | |
627 ready_fds[key.fd] = (key, events & key.events) | |
628 else: | |
629 old_events = ready_fds[key.fd][1] | |
630 ready_fds[key.fd] = (key, (events | old_events) & key.events) | |
631 | |
632 return list(ready_fds.values()) | |
633 | |
634 def close(self): | |
635 self._kqueue.close() | |
636 super(KqueueSelector, self).close() | |
637 | |
638 __all__.append('KqueueSelector') | |
639 | |
640 | |
641 def _can_allocate(struct): | |
642 """ Checks that select structs can be allocated by the underlying | |
643 operating system, not just advertised by the select module. We don't | |
644 check select() because we'll be hopeful that most platforms that | |
645 don't have it available will not advertise it. (ie: GAE) """ | |
646 try: | |
647 # select.poll() objects won't fail until used. | |
648 if struct == 'poll': | |
649 p = select.poll() | |
650 p.poll(0) | |
651 | |
652 # All others will fail on allocation. | |
653 else: | |
654 getattr(select, struct)().close() | |
655 return True | |
656 except (OSError, AttributeError): | |
657 return False | |
658 | |
659 | |
660 # Python 3.5 uses a more direct route to wrap system calls to increase speed. | |
661 if sys.version_info >= (3, 5): | |
662 def _syscall_wrapper(func, _, *args, **kwargs): | |
663 """ This is the short-circuit version of the below logic | |
664 because in Python 3.5+ all selectors restart system calls. """ | |
665 return func(*args, **kwargs) | |
666 else: | |
667 def _syscall_wrapper(func, recalc_timeout, *args, **kwargs): | |
668 """ Wrapper function for syscalls that could fail due to EINTR. | |
669 All functions should be retried if there is time left in the timeout | |
670 in accordance with PEP 475. """ | |
671 timeout = kwargs.get("timeout", None) | |
672 if timeout is None: | |
673 expires = None | |
674 recalc_timeout = False | |
675 else: | |
676 timeout = float(timeout) | |
677 if timeout < 0.0: # Timeout less than 0 treated as no timeout. | |
678 expires = None | |
679 else: | |
680 expires = monotonic() + timeout | |
681 | |
682 args = list(args) | |
683 if recalc_timeout and "timeout" not in kwargs: | |
684 raise ValueError( | |
685 "Timeout must be in args or kwargs to be recalculated") | |
686 | |
687 result = _SYSCALL_SENTINEL | |
688 while result is _SYSCALL_SENTINEL: | |
689 try: | |
690 result = func(*args, **kwargs) | |
691 # OSError is thrown by select.select | |
692 # IOError is thrown by select.epoll.poll | |
693 # select.error is thrown by select.poll.poll | |
694 # Aren't we thankful for Python 3.x rework for exceptions? | |
695 except (OSError, IOError, select.error) as e: | |
696 # select.error wasn't a subclass of OSError in the past. | |
697 errcode = None | |
698 if hasattr(e, "errno"): | |
699 errcode = e.errno | |
700 elif hasattr(e, "args"): | |
701 errcode = e.args[0] | |
702 | |
703 # Also test for the Windows equivalent of EINTR. | |
704 is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and | |
705 errcode == errno.WSAEINTR)) | |
706 | |
707 if is_interrupt: | |
708 if expires is not None: | |
709 current_time = monotonic() | |
710 if current_time > expires: | |
40787
d1bda397df73
selectors2: backport minimal fix of timeout handling from 2.0.1
Yuya Nishihara <yuya@tcha.org>
parents:
35245
diff
changeset
|
711 raise OSError(errno.ETIMEDOUT, 'Connection timed out') |
33502 | 712 if recalc_timeout: |
713 if "timeout" in kwargs: | |
714 kwargs["timeout"] = expires - current_time | |
715 continue | |
716 raise | |
717 return result | |
718 | |
719 | |
720 # Choose the best implementation, roughly: | |
721 # kqueue == devpoll == epoll > poll > select | |
722 # select() also can't accept a FD > FD_SETSIZE (usually around 1024) | |
723 def DefaultSelector(): | |
724 """ This function serves as a first call for DefaultSelector to | |
725 detect if the select module is being monkey-patched incorrectly | |
726 by eventlet, greenlet, and preserve proper behavior. """ | |
727 global _DEFAULT_SELECTOR | |
728 if _DEFAULT_SELECTOR is None: | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
729 if pycompat.isjython: |
33502 | 730 _DEFAULT_SELECTOR = JythonSelectSelector |
731 elif _can_allocate('kqueue'): | |
732 _DEFAULT_SELECTOR = KqueueSelector | |
733 elif _can_allocate('devpoll'): | |
734 _DEFAULT_SELECTOR = DevpollSelector | |
735 elif _can_allocate('epoll'): | |
736 _DEFAULT_SELECTOR = EpollSelector | |
737 elif _can_allocate('poll'): | |
738 _DEFAULT_SELECTOR = PollSelector | |
739 elif hasattr(select, 'select'): | |
740 _DEFAULT_SELECTOR = SelectSelector | |
741 else: # Platform-specific: AppEngine | |
742 raise RuntimeError('Platform does not have a selector.') | |
743 return _DEFAULT_SELECTOR() |