- 15 Apr, 2024 5 commits
-
-
Kirill Smelkov authored
Background: in 2019 in 9c260fde (time: New package that mirrors Go's time) and b073f6df (time: Move/Port timers to C++/Pyx nogil) I've added basic timers - with proper API but with very dumb implementation that was spawning one thread per each timer. There were just a few timers in the users and this was working, surprisingly, relatively ok... ... until 2023 where I was working on XLTE that needs to organize 100Hz polling of Amarisoft eNodeB service to retrieve information about flows on Data Radio Bearers: xlte@2a016d48 https://lab.nexedi.com/kirr/xlte/-/blob/8e606c64/amari/drb.py There each request comes with its own deadline - to catch "no reply", and the deadlines are implemented via timers. So there are 100 threads created every second which adds visible overhead, consumes a lot of virtual address space and RSS for threads stacks, and should be all unnecessary. We was tolerating even that for some time, but recently Joanne approached me with reports that xamari program, that does the polling, is leaking memory. With that, and because it was hard to find what is actually leaking, I've started to remove uncertainties and there are a lot of uncertainty in what is going on when lots of threads are being created over and over. In the end the leak turned out to be likely a different thing (see nexedi/pygolang!24, still discovered while working on hereby patch), but all of the above was enough motivation to finally start redoing the timers properly. -------- So when it comes to do the timers properly more or less, there is usually queue of armed timers, and a loop that picks entries from that queue to fire them. I was initially trying to do the simple thing and use std::priority_queue for that, because priority_queue is internally heap, and heaps can provide O(log(n)) insertion and removal of arbitrary element, plus O(1) "pick top element to process". Exactly what would suit. However I quickly found that even in 2024, std::priority_queue does not provide removal operation at all, and there is no such thing as e.g. std::sift_heap, that would help to implement that manually. Which is surprising, because e.g. libevent implements all that just ok via sifting up/down upon removal in logarithmic complexity: https://github.com/libevent/libevent/blob/80e25c02/minheap-internal.h#L96-L115 the lack of efficient removal operation turned out to be a blocker to use std::priority_queue because most of the timers, that are armed for timeouts, are never expired and upon successful completion of covered operation, the timer is stopped. In other words the timer is removed from the timer queue and the removal is one of the most often operations. So, if std::priority_queue cannot work, we would need to either bring in another implementation of a heap, or, if we are to bring something, bring and use something else that is more suitable for implementing timers. That reminded me that in 2005 for my Navy project, I already implemented custom timer wheel to handle timeouts after reading https://lwn.net/Articles/152436/ . Contrary to heaps, such timer wheels provide O(1) insertion and removal of timers and work generally faster. But this time I did not want to delve into implementing all that myself again and tried to look around of what is available out there. There was an update to kernel timer-wheel implementation described at https://lwn.net/Articles/646950/ and from that a project called Timeout.c was also found that provides implementation for such a wheel for user space: https://25thandclement.com/~william/projects/timeout.c.html . However when we are to pick third-party code, we should be ready to understand it and fix bugs there on our own. So the audit of timeout.c did not went very smoothly - there are many platform-depended places, and the issue tracker shows signs that sometimes not everything is ok with the implementation. With that I've looked around a bit more and found more compact and more portable Ratas library with good structure and description and whose audit came more well: https://www.snellman.net/blog/archive/2016-07-27-ratas-hierarchical-timer-wheel https://github.com/jsnell/ratas Here, after going through the code, I feel to be capable to understand issues and fix bugs myself if that would become needed. And the benchmark comparison of Timeout.c and Ratas shows that they should be of the same order regarding performance: https://lab.nexedi.com/kirr/misc/-/blob/4f51fd6/bench/time-wheel/ratas-vs-timeout.pdf ratas@382321d2 timeout@d6f15744 which makes Ratas the winner for me. Having timer-wheel implementation, the rest is just technique to glue it all together. One implementation aspect deserves to be mentioned though: The timer loop uses Semaphore.acquire, recently modernized to also accept timeout, to organize sleep in between pauses with also being able to be simultaneously woken up if new timer is armed with earlier expiration time. Other than that the changes are mostly straightforward. Please see the patch itself for details. Regarding how the new implementation is more efficient for what we had before, there are added benchmarks to measure arming timers that do not fire, and, for symmetry, arming timers that do fire. We are most interested in the first benchmark, because it shows how cheap or expensive it is to use timers to implement timeouts, but the second one is also useful to have to see the overhead of the whole timers machinery. On my machine under py3.11 they go as after this patch: name time/op timer_arm_cancel 805ns ± 0% timer_arm_fire 9.63µs ± 0% and before the patch the benchmarks simply do not run till the end because they run out of memory due to huge number of threads being created. Still with the following test program we can measure the effect new timers implementation has: ---- 8< ---- from golang import time def main(): δt_rate = 1*time.millisecond tprev = time.now() tnext = tprev + δt_rate while 1: timer = time.Timer(5*time.second) _ = timer.stop() assert _ is True t = time.now() δtsleep = tnext - t #print('sleep %.3f ms' % (δtsleep/time.millisecond)) time.sleep(δtsleep) tprev = tnext tnext += δt_rate main() ---- 8< ---- This program creates/arms and cancels a timer 1000 times per second. Before hereby patch this program consumes ~ 30% of CPU, while after hereby patch this program consumes ~ 7-8% of CPU. For the reference just a sleep part of that program, with all code related to timers removed consumes ~5% of CPU, while the consumption of plain sleep(1ms) in C and directly using system calls ---- 8< ---- #include <unistd.h> int main() { while (1) { usleep(1000); } return 0; } ---- 8< ---- is ~ 3-4% of CPU on my machine. /cc @jerome /cc ORS team (@jhuge, @lu.xu, @tomo, @xavier_thompson, @Daetalus)
-
Kirill Smelkov authored
In the next patch we will add reworked implementation of timers - that will no longer use dumb approach to work via threads - and in that implementation it will make sense to regroup the organization of code a bit for better clarity. Prepare for that: - move Timer.reset to stay in between _new_timer and stop. This will be handy because Timer.reset will be interaction with both even loop (coming right before new) and stop. - move new_timer to the place where we commonly keep wrapper "create-timer or ticker" routines to improve signal/noise ration for the place where actual interaction in between code parts happen. For the reference my general approach to order things is to go from high level to down and group things by interaction along the way. This way things turns out to be the most easily readable and understandable.
-
Kirill Smelkov authored
Previously libgolang was specifying its runtime, among other primitives, to provide semaphore implementation with acquire and release methods. The release should be non-blocking operation, and the acquire should be blocking until the semaphore is acquired. However for efficient implementation of timers, we will need to have semaphore acquire that can also be instructed to time out. -> Adjust thread and gevent runtimes to provide that and adjust runtime interface specification to require that. This is generally backward incompatible change, but given that there is just a few libgolang runtimes, it, hopefully, should not do any real breakage. So I think it is ok to do it this way. For the reference - contrary to runtimes - the public user API of libgolang and pygolang - that most of the pygolang users actually use - is not changed at all. In other words there is no backward-compatibility issue for regular pygolang/libgolang users because for them pygolang stays 100% backward compatible.
-
Kirill Smelkov authored
I was working on Timer-related topics and started to suspect that stop might become panicking when draining timer channel if func != nil. That turned out to be not true - the code is correct as it is, but it generally helps to have tests covering questionable functionality.
-
Kirill Smelkov authored
In the light of discovered memory leaks (see nexedi/pygolang!24), it is better to explicitly make sure that resources allocated by every test are explicitly released. Even though timers are released automatically on their expiration, there is generally no guarantee that the tests will finish after all timers are expired. And even more so for Ticker - without explicit stop, the ticker continues to be active forever. So stop all created timers and tickers where we can in the tests.
-
- 17 Feb, 2024 4 commits
-
-
Kirill Smelkov authored
Tests now pass with that version. /reviewed-by @jerome /reviewed-on !23
-
Kirill Smelkov authored
When python is built from git checkout, not exactly on any tag state, it adds a "+" sign as suffix to its version, for example: (py312.venv) kirr@deca:~/src/tools/go/pygolang$ python -V Python 3.12.2+ (py312.venv) kirr@deca:~/src/tools/go/pygolang$ python Python 3.12.2+ (heads/3.12:0e4f73b8e45, Feb 15 2024, 10:52:08) [GCC 12.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> but our handler for -V was trying to construct the version from sys.version_info where there is no information about that "extra" part: In [2]: sys.version_info Out[2]: sys.version_info(major=3, minor=12, micro=2, releaselevel='final', serial=0) # no + In [4]: platform.python_version() Out[4]: '3.12.2+' as the result test_pymain_ver is failing: =================== FAILURES =================== ______________ test_pymain_ver[] _______________ runtime = '' @gpython_only def test_pymain_ver(runtime): from golang import b from gpython import _version_info_str as V import gevent vok = 'GPython %s' % golang.__version__ if runtime != 'threads': vok += ' [gevent %s]' % gevent.__version__ else: vok += ' [threads]' if is_cpython: vok += ' / CPython %s' % platform.python_version() elif is_pypy: vok += ' / PyPy %s / Python %s' % (V(sys.pypy_version_info), V(sys.version_info)) else: vok = sys.version vok += '\n' ret, out, err = _pyrun(['-V'], stdout=PIPE, stderr=PIPE, env=gpyenv(runtime)) > assert (ret, out, b(err)) == (0, b'', b(vok)) E AssertionError: assert (0, b'', b'GPython 0.1 [gevent 24.2.1] / CPython 3.12.2\n') == (0, b'', b'GPython 0.1 [gevent 24.2.1] / CPython 3.12.2+\n') E At index 2 diff: b'GPython 0.1 [gevent 24.2.1] / CPython 3.12.2\n' != b'GPython 0.1 [gevent 24.2.1] / CPython 3.12.2+\n' E Full diff: E - (0, b'', b'GPython 0.1 [gevent 24.2.1] / CPython 3.12.2+\n') E ? - E + (0, b'', b'GPython 0.1 [gevent 24.2.1] / CPython 3.12.2\n') gpython/gpython_test.py:341: AssertionError -> Fix it by handling -V with platform.python_version() directly. /reviewed-by @jerome /reviewed-on !23
-
Kirill Smelkov authored
Pytest 7.4 changed format of output tracebacks. Similarly to IPython adding test support for later versions would add maintenance cost on pygolang side, but testing this is actually not needed, since we activate Pytest-related patch only on py2 and for py2 the latest pytest version is pytest 4.6.11. -> So simply skip this test if we see we have Pytest ≥ 7.4. Skip it only on py3 just in case. For the reference here is how diff in between running golang_test_defer_excchain.py on Pytest 7.3 and Pytest 7.4 looks: diff --git a/a b/b index 45680b99..47c29cea 100644 --- a/a +++ b/b @@ -1,5 +1,5 @@ ============================= test session starts ============================== -platform linux -- Python 3.12.2+, pytest-7.3.2, pluggy-1.4.0 +platform linux -- Python 3.12.2+, pytest-7.4.0, pluggy-1.4.0 rootdir: /home/kirr/src/tools/go/pygolang-master collected 1 item @@ -7,22 +7,16 @@ golang/testprog/golang_test_defer_excchain.py F [100%] =================================== FAILURES =================================== _____________________________________ main _____________________________________ -golang/__init__.py:106: in _ - return f(*argv, **kw) golang/testprog/golang_test_defer_excchain.py:42: in main raise RuntimeError("err") E RuntimeError: err During handling of the above exception, another exception occurred: -golang/__init__.py:183: in __exit__ - d() golang/testprog/golang_test_defer_excchain.py:31: in d1 raise RuntimeError("d1: aaa") E RuntimeError: d1: aaa During handling of the above exception, another exception occurred: -golang/__init__.py:183: in __exit__ - d() golang/testprog/golang_test_defer_excchain.py:33: in d2 1/0 E ZeroDivisionError: division by zero /reviewed-by @jerome /reviewed-on !23
-
Kirill Smelkov authored
@Qubitium notes (https://github.com/navytux/pygolang/issues/1): Python 3.12 no longer support the imp module (fully deprecated) with note to use importlib as replacement. Please support python 3.12 by migrating from imp code to importlib. Thanks. and indeed, even trying to build pygolang fails on py3.12: (py312.venv) kirr@deca:~/src/tools/go/pygolang-master$ python setup.py build_ext -i Traceback (most recent call last): File "/home/kirr/src/tools/go/pygolang-master/setup.py", line 40, in <module> exec(readfile('trun'), trun) File "<string>", line 41, in <module> ModuleNotFoundError: No module named 'imp' -> Rework the code to use importlib instead, but keep using imp on py2 where there is practically no importlib functionality. /reported-by @Qubitium (github) /reviewed-by @jerome /reviewed-on !23
-
- 16 Aug, 2023 1 commit
-
-
Kirill Smelkov authored
Unfortunately the best Cython release ever[1] breaks build for Pygolang[2], and people on the net also report that there are other problems - both build failures and popped up performance problems due to changed semantic[3,4,...]. Fix it all on Pygolang side by, for now, not delving into being Cython 3 beta-testers and relying on field-proven Cython 2. /reported-by @levin.zimmermann [1] https://groups.google.com/g/cython-users/c/o41CrcRkVvo [2] wendelin.core!16 (comment 188623) [3] https://news.ycombinator.com/item?id=36778617 [4] https://github.com/cython/cython/issues/5540
-
- 03 May, 2023 1 commit
-
-
Kirill Smelkov authored
This fixes linking to editable pygolang install on Windows. See https://github.com/mdavidsaver/setuptools_dso/commit/fca6f29e and https://github.com/mdavidsaver/setuptools_dso/pull/25 for details. Without this fix e.g. test_pyx_build was also failing: platform win32 -- Python 3.10.11, pytest-7.3.1, pluggy-1.0.0 -- Z:\home\kirr\src\tools\go\pygo-win\1.wenv\Scripts\python.exe cachedir: .pytest_cache rootdir: Z:\home\kirr\src\tools\go\pygo-win\pygolang collecting 3 items collected 3 items golang/pyx/build_test.py::test_pyx_build running build_ext skipping 'pyxuser\test.cpp' Cython extension (up-to-date) building 'pyxuser.test' extension Z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\bin\Hostx64\x64\cl.exe /c /nologo /O2 /W3 /GL /DNDEBUG /MD -IZ:\home\kir r\src\tools\go\pygo-win\pygolang -IZ:\home\kirr\src\tools\go\pygo-win\pygolang\golang\_compat\windows -IZ:\home\kirr\src\tools\go\pygo-win\1.wen v\include\site\python3.10 -IZ:\home\kirr\src\tools\go\pygo-win\1.wenv\include "-IC:\Program Files\Python310\include" "-IC:\Program Files\Python3 10\Include" -Iz:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\include -Iz:\home\kirr\src\tools\go\pygo-win\BuildTools\ki ts\10\include\10.0.22000.0\shared -Iz:\home\kirr\src\tools\go\pygo-win\BuildTools\kits\10\include\10.0.22000.0\ucrt -Iz:\home\kirr\src\tools\go\ pygo-win\BuildTools\kits\10\include\10.0.22000.0\um -Iz:\home\kirr\src\tools\go\pygo-win\BuildTools\kits\10\include\10.0.22000.0\winrt /EHsc /Tp pyxuser\test.cpp /Fobuild\temp.win-amd64-cpython-310\Release\pyxuser\test.obj /std:c++20 /EHc- /EHsr cl : Command line warning D9025 : overriding '/EHc' with '/EHc-' test.cpp ... Z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\bin\Hostx64\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EM BED,ID=2 /MANIFESTUAC:NO /LIBPATH:Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\runtime "/LIBPATH:C:\Program Files\Python310\libs" "/LIBPAT H:C:\Program Files\Python310" /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\lib\x64 /LIBPATH:z:\home\kirr\src \tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\ucrt\x64 /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\ um\x64 libgolang.lib /EXPORT:PyInit_test build\temp.win-amd64-cpython-310\Release\pyxuser\test.obj /OUT:build\lib.win-amd64-cpython-310\pyxuser\ test.cp310-win_amd64.pyd /IMPLIB:build\temp.win-amd64-cpython-310\Release\pyxuser\test.cp310-win_amd64.lib LINK : fatal error LNK1181: cannot open input file 'libgolang.lib' <-- NOTE error: command 'Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\BuildTools\\vc\\tools\\msvc\\14.35.32215\\bin\\Hostx64\\x64\\link.exe' failed with exi t code 1181 FAILED ========================== FAILURES =========================== _______________________ test_pyx_build ________________________ def test_pyx_build(): pyxuser = testprog + "/golang_pyx_user" > pyrun(["setup.py", "build_ext", "-i"], cwd=pyxuser) golang\pyx\build_test.py:31: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ argv = ['setup.py', 'build_ext', '-i'], stdin = None, stdout = None, stderr = None kw = {'cwd': 'Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\pygolang\\golang\\pyx/testprog/golang_pyx_user'}, retcode = 1 def pyrun(argv, stdin=None, stdout=None, stderr=None, **kw): retcode, stdout, stderr = _pyrun(argv, stdin=stdin, stdout=stdout, stderr=stderr, **kw) if retcode: > raise RuntimeError(' '.join(argv) + '\n' + (stderr and str(stderr) or '(failed)')) E RuntimeError: setup.py build_ext -i E (failed) golang\golang_test.py:1771: RuntimeError
-
- 27 Apr, 2023 29 commits
-
-
Kirill Smelkov authored
Pygolang stopped to work on Windows in 2019 starting from 8fa3c15b (Start using Cython and providing Cython/nogil API). Restore it now.
-
Kirill Smelkov authored
Linux and macOS was working before. And we just added Windows support in the previous patches.
-
Kirill Smelkov authored
When underlying pygfobj is FileObjectThread its .readinto() leads to deadlock because it is no cooperative(*). This manifests as test_pyx_os_pipe_cpp hang when run by gpython on windows. -> Workaround this by reading first into intermediate buffer and then copying data to buffer that user provided. After this the deadlock is gone but test_pyx_os_pipe_cpp starts to fail and crash randomly. That's because similarly to channels we need to care and not access a buffer if it is located on stack and owning greenlet is inactive. Because when a greenlet is inactive, its stack is reused by another active greenlet and writing/reading to on-stack memory accesses that second greenlet stack, corrupting it on write. -> do the same what we do in chan operations: use intermediate on-heap buffer to protect original user's buffer to be accesses because it might be located on stack. That's what actually happens in test_pyx_os_pipe_cpp where two goroutines read and write to each other via pipe and using on-stack located buffers. And becuase on windows pipes, like regular files, are wrapped with FileObjectThread, when reading greenlet becomes suspended waiting for read reasul, it will be another greenlet to run on its stack, write to another end of a pipe, wakeup IO thread, which will write the data to requested buffer on G1 stack and oops - it was G2 there. (*) see https://github.com/gevent/gevent/pull/1948 for details
-
Kirill Smelkov authored
Because on Windows it is \ and using / for it to scan in path fails and leads to test_pymain_opt failure: E Full diff: E [ E 'sys.flags.debug: 0', E 'sys.flags.optimize: 0', E '__debug__: True', E 'assert: True', E 'docstrings: True', E 'import mod.py: ' E - 'C:\\users\\kirr\\Temp\\modpy_imports_fromvmgs8go4\\__pycache__/mod.cpython-310.pyc', E ? ^^ ---- E + 'C:\\users\\kirr\\Temp\\modpy_imports_from850gb81s\\__pycache__/mod.cpython-310.pyc', E ? ^^^ +++ E ] Here the difference is because gpython/testprog/print_opt.py failed to detect tmpd prefix and strip it.
-
Kirill Smelkov authored
In several places we were preparing repr of a string as '%s' which works ok most of the time but leads to failure if string contains '\' characters e.g. if it represents a path and we are running on windows: def test_pymain(): from golang import b .... # -m <module> _ = pyout(['-m', 'hello', 'abc', 'def'], cwd=testdata) # realpath rewrites e.g. `local/lib -> lib` if local/lib is symlink hellopy = realpath(join(testdata, 'hello.py')) > assert _ == b"hello\nworld\n['%s', 'abc', 'def']\n" % b(hellopy) E assert b"hello\nworld\n['Z:\\\\home\\\\kirr\\\\src\\\\tools\\\\go\\\\pygo-win\\\\pygolang\\\\gpython\\\\testdata\\\\hello.py', 'abc', 'def' ]\n" == b"hello\nworld\n['Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\pygolang\\gpython\\testdata\\hello.py', 'abc', 'def']\n" E At index 17 diff: b'\\' != b'h' E Full diff: E ( E - b"hello\nworld\n['Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\pygolang\\gpytho" E ? ------------ E + b"hello\nworld\n['Z:\\\\home\\\\kirr\\\\src\\\\tools\\\\go\\\\pygo-win\\\\pygo" E ? ++ ++ ++ ++ ++ ++ ++ E - b"n\\testdata\\hello.py', 'abc', 'def']\n", E + b"lang\\\\gpython\\\\testdata\\\\hello.py', 'abc', 'def']\n", E ? ++ ++++++++++++++ ++ E ) gpython\gpython_test.py:183: AssertionError -> Fix it by preparing repr via repr(...) properly.
-
Kirill Smelkov authored
On Windows if geventmp is present gpython startup fails: (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang>gpython Traceback (most recent call last): File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main return _run_code(code, main_globals, None, File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code exec(code, run_globals) File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\Scripts\gpython.exe\__main__.py", line 7, in <module> File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\gpython\__init__.py", line 450, in main pymain(argv, init) File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\gpython\__init__.py", line 223, in pymain init() File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\gpython\__init__.py", line 428, in init _ = monkey.patch_all(thread=patch_thread) # XXX sys=True ? File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\gevent\monkey.py", line 1255, in patch_all _notify_patch(events.GeventWillPatchAllEvent(modules_to_patch, kwargs), _warnings) File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\gevent\monkey.py", line 190, in _notify_patch notify_and_call_entry_points(event) File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\gevent\events.py", line 105, in notify_and_call_entry_points subscriber(event) File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\geventmp\monkey.py", line 160, in _patch_mp _patch_module("_mp.3._mp_util", _patch_module=True, _package_prefix='geventmp.') File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\geventmp\monkey.py", line 121, in _patch_module gevent_module = import_module(_package_prefix + name) File "C:\Program Files\Python310\lib\importlib\__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1050, in _gcd_import File "<frozen importlib._bootstrap>", line 1027, in _find_and_load File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 688, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 883, in exec_module File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\geventmp\_mp\3\_mp_util.py", line 16, in <module> from gevent.os import _watch_child ImportError: cannot import name '_watch_child' from 'gevent.os' (Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages\gevent\os.py) That happens because geventmp does not support windows actually. -> Fix it by requiring geventmp to be present only on non-windows. Adjust related comment as https://github.com/karellen/geventmp/pull/2 has been merged long ago.
-
Kirill Smelkov authored
On windows setuptools install gpython.exe and gpython-script.py while pip/distlib install gpython.exe with gpython-script sometimes embedded into gpython.exe itself with argv[0] pointing to 'gpython' without .exe suffix. This leads to gpython startup failure: (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang>gpython Traceback (most recent call last): File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main return _run_code(code, main_globals, None, File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code exec(code, run_globals) File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\Scripts\gpython.exe\__main__.py", line 7, in <module> File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\gpython\__init__.py", line 437, in main pymain(argv, init) File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\gpython\__init__.py", line 79, in pymain if not _is_buildout_script(exe): File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\gpython\__init__.py", line 442, in _is_buildout_script with open(path, 'rb') as f: FileNotFoundError: [Errno 2] No such file or directory: 'Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\1.wenv\\Scripts\\gpython' (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang>dir ../1.wenv/scripts/gpython* Directory of Z:\home\kirr\src\tools\go\pygo-win\1.wenv\scripts 26.04.2023 14:17 108,404 gpython.exe 1 file 108,404 bytes 0 directories 88,508,866,560 bytes free -> Adjust pymain to handle this case accordingly.
-
Kirill Smelkov authored
On windows upon terminating signal reception exict code of the process is always 3 instead of -signo. And so test_signal_all was failing as def test_signal_all(): retcode, out, _ = _pyrun([dir_testprog + "/signal_test_all.py"], stdout=PIPE) assert b"ok (notify)" in out assert b"ok (ignore)" in out assert b"terminating ..." in out > assert retcode == -syscall.SIGTERM.signo E assert 3 == -15 E + where 15 = os.Signal(15).signo E + where os.Signal(15) = syscall.SIGTERM
-
Kirill Smelkov authored
- There is no e.g. signal.SIGKILL on windows: golang/os/signal_test.py::test_signal_all Traceback (most recent call last): File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\os\testprog\signal_test_all.py", line 82, in <module> main() File "Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\os\testprog\signal_test_all.py", line 39, in main allsigv.remove(syscall.SIGKILL) # SIGKILL/SIGSTOP cannot be caught AttributeError: module 'golang.syscall' has no attribute 'SIGKILL'. Did you mean: 'SIGILL'? -> filter-out signals conditionally depending on their presence. - Need to also filter-out SIGILL/SIGABRT because upon reception MSVC runtime terminates process.
-
Kirill Smelkov authored
Because on Windows os.kill unconditionally terminates target process, even if own self, instead of sending it any kind of signal.
-
Kirill Smelkov authored
We use SIGUSR1/SIGUSR2 mainly in this test, but those signals are not available on Windows: @func def test_signal(): # Notify/Stop with wrong chan dtype -> panic _ = panics("pychan: channel type mismatch") > with _: signal.Notify(chan(2), syscall.SIGUSR1) E AttributeError: module 'golang.syscall' has no attribute 'SIGUSR1' -> Use SIGTERM/SIGINT if SIGUSR1/SIGUSR2 are not available. Don't want to use SIGTERM/SIGINT unconditionally because those signals are better to leave for the job control so that e.g. nxdtest can properly kill spawned pygolang tests.
-
Kirill Smelkov authored
On Windows in text mode files are opened with encoding=locale.getdefaultlocale() which is CP125X instead of UTF-8 even if $PYTHONIOENCODING=UTF-8. This way e.g. test_strings_print fail as: E Failed: not equal: E Expected: E print(qq(b)): "привет О±ОІОі b" E print(qq(u)): "привет О±ОІОі u" E Got: E print(qq(b)): "привет αβγ b" E print(qq(u)): "привет αβγ u" where "Expected" was read from golang/testprog/golang_test_str.txt and decoded wrongly. -> Fix it by always opening files for reading in binary mode and utf8-decoding manually, if needed, everywhere.
-
Kirill Smelkov authored
golang: tests: Spawn subprocess python with $PYTHONIOENCODING set to encoding of stdin/stdout/stderr We need to do it because on Windows `python x.py | ...` runs with stdio encoding set to cp125X even if just `python x.py` runs with stdio encoding=UTF-8. (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\testprog>python golang_test_str.py print(qq(b)): "привет b" print(qq(u)): "привет u" (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\testprog>python golang_test_str.py |more print(qq(b)): "яЁштхЄ b" print(qq(u)): "яЁштхЄ u" (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\testprog>python golang_test_str.py >aaa.txt (1.wenv) Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\testprog>type aaa.txt print(qq(b)): "яЁштхЄ b" print(qq(u)): "яЁштхЄ u" Which leads to the following test_strings_print failure: E Failed: not equal: E Expected: E print(qq(b)): "привет b" E print(qq(u)): "привет u" E Got: E print(qq(b)): "������ b" E print(qq(u)): "������ u" We also change golang_test_str.py to print not only russian and english letters, but also greek ones. This is to make sure that stdout IO encoding would not go cp125X unnoticed because then printing will fail with UnicodeEncodeError. Note: in the above example both got and expected are wrong. Via $PYTHONIOENCODING we only fix "got" and we will fix "expected" in the followup patch. After current patch test_strings_print result is E Failed: not equal: E Expected: E print(qq(b)): "привет О±ОІОі b" E print(qq(u)): "привет О±ОІОі u" E Got: E print(qq(b)): "привет αβγ b" E print(qq(u)): "привет αβγ u"
-
Kirill Smelkov authored
Apparently on Windows pytest detects terminal width differently than on Linux/macOS and the test fails as E Failed: not equal: E Differences (unified diff with -expected +actual): E @@ -1,5 +1,12 @@ E -... E -_____________________________________ main _____________________________________ E -../__init__.py:...: in _ E +============================= test session starts ============================= E +platform win32 -- Python 3.10.11, pytest-7.3.1, pluggy-1.0.0 E +rootdir: PYGOLANG E +collected 1 item E +<BLANKLINE> E +golang_test_defer_excchain.py F [100%] E +<BLANKLINE> E +================================== FAILURES =================================== E +____________________________________ main _____________________________________ E +../__init__.py:106: in _ E return f(*argv, **kw) E golang_test_defer_excchain.py:42: in main E @@ -8,6 +15,6 @@ E <BLANKLINE> E During handling of the above exception, another exception occurred: E -../__init__.py:...: in __exit__ E - ... E +../__init__.py:183: in __exit__ E + d() E golang_test_defer_excchain.py:31: in d1 E raise RuntimeError("d1: aaa") E @@ -15,9 +22,9 @@ E <BLANKLINE> E During handling of the above exception, another exception occurred: E -../__init__.py:...: in __exit__ E - ... E +../__init__.py:183: in __exit__ E + d() E golang_test_defer_excchain.py:33: in d2 E 1/0 E -E ZeroDivisionError: ... E +E ZeroDivisionError: division by zero E <BLANKLINE> E During handling of the above exception, another exception occurred: E @@ -25,3 +32,5 @@ E raise RuntimeError("d3: bbb") E E RuntimeError: d3: bbb E -=========================== ... E +=========================== short test summary info =========================== E +FAILED golang_test_defer_excchain.py::main - RuntimeError: d3: bbb E +============================== 1 failed in 1.45s ============================== -> Fix it by not requesting header to be of particular width.
-
Kirill Smelkov authored
On windows print emits \r\n instead of just \n. Here is how e.g. test_defer_excchain_dump fails without normalization: E Failed: not equal: E Differences (unified diff with -expected +actual): E @@ -1,42 +1,43 @@ E -Traceback (most recent call last): E - File "PYGOLANG/golang/__init__.py", line ..., in _ E - return f(*argv, **kw) E - File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 42, in main E - raise RuntimeError("err") E -RuntimeError: err E -<BLANKLINE> E -During handling of the above exception, another exception occurred: E -<BLANKLINE> E -Traceback (most recent call last): E - File "PYGOLANG/golang/__init__.py", line ..., in __exit__ E - ... E - File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 31, in d1 E - raise RuntimeError("d1: aaa") E -RuntimeError: d1: aaa E -<BLANKLINE> E -During handling of the above exception, another exception occurred: E -<BLANKLINE> E -Traceback (most recent call last): E - File "PYGOLANG/golang/__init__.py", line ..., in __exit__ E - ... E - File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 33, in d2 E - 1/0 E -ZeroDivisionError: ... E -<BLANKLINE> E -During handling of the above exception, another exception occurred: E -<BLANKLINE> E -Traceback (most recent call last): E - ... "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 45, in <module> E - main() E - ... E - File "PYGOLANG/golang/__init__.py", line ..., in _ E - with __goframe__: E - File "PYGOLANG/golang/__init__.py", line ..., in __exit__ E - ... E - File "PYGOLANG/golang/__init__.py", line ..., in __exit__ E - ... E - File "PYGOLANG/golang/__init__.py", line ..., in __exit__ E - ... E - File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 35, in d3 E - raise RuntimeError("d3: bbb") E -RuntimeError: d3: bbb E +Traceback (most recent call last): E + File "PYGOLANG/golang/__init__.py", line 106, in _ E + return f(*argv, **kw) E + File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 42, in main E + raise RuntimeError("err") E +RuntimeError: err E + E +During handling of the above exception, another exception occurred: E + E +Traceback (most recent call last): E + File "PYGOLANG/golang/__init__.py", line 183, in __exit__ E + d() E + File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 31, in d1 E + raise RuntimeError("d1: aaa") E +RuntimeError: d1: aaa E + E +During handling of the above exception, another exception occurred: E + E +Traceback (most recent call last): E + File "PYGOLANG/golang/__init__.py", line 183, in __exit__ E + d() E + File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 33, in d2 E + 1/0 E +ZeroDivisionError: division by zero E + E +During handling of the above exception, another exception occurred: E + E +Traceback (most recent call last): E + File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 45, in <module> E + main() E + File "Z:\home\kirr\src\tools\go\pygo-win\1.wenv\lib\site-packages/decorator.py", line 232, in fun E + return caller(func, *(extras + args), **kw) E + File "PYGOLANG/golang/__init__.py", line 105, in _ E + with __goframe__: E + File "PYGOLANG/golang/__init__.py", line 182, in __exit__ E + with __goframe__: E + File "PYGOLANG/golang/__init__.py", line 182, in __exit__ E + with __goframe__: E + File "PYGOLANG/golang/__init__.py", line 183, in __exit__ E + d() E + File "PYGOLANG/golang/testprog/golang_test_defer_excchain.py", line 35, in d3 E + raise RuntimeError("d3: bbb") E +RuntimeError: d3: bbb
-
Kirill Smelkov authored
Else on Windows e.g. test_defer_excchain_traceback fails as E Failed: not equal: E Differences (unified diff with -expected +actual): E @@ -1,8 +1,8 @@ E Traceback (most recent call last): E - File "PYGOLANG/golang/golang_test.py", line ..., in test_defer_excchain_traceback E + File "PYGOLANG\golang\golang_test.py", line 1524, in test_defer_excchain_traceback E alpha() E - File "PYGOLANG/golang/golang_test.py", line ..., in alpha E + File "PYGOLANG\golang\golang_test.py", line 1521, in alpha E beta() E - File "PYGOLANG/golang/golang_test.py", line ..., in beta E + File "PYGOLANG\golang\golang_test.py", line 1520, in beta E raise RuntimeError("gamma") E RuntimeError: gamma
-
Kirill Smelkov authored
Else it fails to import on Windows: collecting ... 05c8:err:module:import_dll Library libgolang.dll (which is needed by L"Z:\\home\\kirr\\src\\tools\\go\\pygo-win\\pygolang\\golang\\_golang.cp310-win_amd64.pyd") not found collected 0 items / 1 error =========================================================== ERRORS =========================================================== ___________________________________________ ERROR collecting golang/golang_test.py ___________________________________ ImportError while importing test module 'Z:\home\kirr\src\tools\go\pygo-win\pygolang\golang\golang_test.py'. Hint: make sure your test modules/packages have valid Python names. Traceback: C:\Program Files\Python310\lib\importlib\__init__.py:126: in import_module return _bootstrap._gcd_import(name[level:], package, level) golang\__init__.py:45: in <module> from golang._golang import _pysys_exc_clear as _sys_exc_clear E ImportError: DLL load failed while importing _golang: Модуль не найден. We need to increase required setuptools_dso version because dylink_prepare_dso and generation of *_dsoinfo.py modules was done after setuptools_dso 2.
-
Kirill Smelkov authored
On Windows this files are generated when linking alongside *.dll files if there are exported symbols. See also https://github.com/mdavidsaver/setuptools_dso/pull/25.
-
Kirill Smelkov authored
Libpyxruntime by definition contains python-specific code and we already compile it with include path having python includes in it. However on windows pyconfig.h contains #pragma comment(lib,"python3.lib") which instructs the linker to automatically link to that library. And without proper library path the link fails: Z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\bin\Hostx64\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:build\lib.win-amd64-cpython-31 0\golang\runtime /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\lib\x64 /LIBPATH:z:\home\kirr\src\tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\ucrt\x64 /LIBPATH:z:\h ome\kirr\src\tools\go\pygo-win\BuildTools\kits\10\lib\10.0.22000.0\um\x64 libgolang.lib build\temp.win-amd64-cpython-310\Release\golang/runtime/libpyxruntime.obj /OUT:build\lib.win-amd64-cpython-310\golang\ru ntime\libpyxruntime.dll /IMPLIB:build\lib.win-amd64-cpython-310\golang\runtime\libpyxruntime.lib LINK : fatal error LNK1104: cannot open file 'python310.lib' -> Fix it by providing proper library path for python.dll . We need to do it ourselves only for libpyxruntime dso because for py extensions this is automatically done by distutils out of the box.
-
Kirill Smelkov authored
On Windows rthere is no fcntl golang/runtime\_runtime_gevent.cpp(4811): error C3861: 'S_ISBLK': identifier not found golang/runtime\_runtime_gevent.cpp(4823): error C2039: 'Fcntl': is not a member of 'golang::internal::syscall' .\golang/runtime/internal/syscall.h(36): note: see declaration of 'golang::internal::syscall' golang/runtime\_runtime_gevent.cpp(4823): error C2065: 'F_GETFL': undeclared identifier golang/runtime\_runtime_gevent.cpp(4823): error C3861: 'Fcntl': identifier not found golang/runtime\_runtime_gevent.cpp(4870): error C2065: 'O_ACCMODE': undeclared identifier golang/runtime\_runtime_gevent.cpp(4889): error C2039: 'Fcntl': is not a member of 'golang::internal::syscall' .\golang/runtime/internal/syscall.h(36): note: see declaration of 'golang::internal::syscall' golang/runtime\_runtime_gevent.cpp(4889): error C2065: 'F_SETFL': undeclared identifier golang/runtime\_runtime_gevent.cpp(4889): error C2065: 'O_NONBLOCK': undeclared identifier golang/runtime\_runtime_gevent.cpp(4889): error C3861: 'Fcntl': identifier not found and even if there would be gevent does not provide cooperative version of FileObject for non-POSIX platforms. -> Always use FileObjectThread on windows even for pipes.
-
Kirill Smelkov authored
There is no fcntl on windows. Named pipes can be set into nonblocking mode via SetNamedPipeHandleState(PIPE_NOWAIT).
-
Kirill Smelkov authored
On MSVC there is no sigaction and we have to use signal. Fortunately our sigaction usage in os/signal can be practically emulated because we do not use all features of sigaction and because on MSVC there is SIG_GET signal extension to query current signal handler without resetting it. Because the emulation is limited we keep it in os/signal.cpp instead of adjusting syscall::Sigaction .
-
Kirill Smelkov authored
On MSVC we will need to use signal(2), because there is no sigaction, and sigaction will be partly emulated through that. Move signal setting code into common place as a preparatory step.
-
Kirill Smelkov authored
- there is no strerror_r, but strerror_s instead - fcntl is not supported - we need to explicitly tell open to open files in binary mode - pipes are created via _pipe and explicitly in binary mode - there is no sigaction.
-
Kirill Smelkov authored
This way e.g. O_CLOEXEC can be passed in at pipe creation time removing potential race in between pipe+fcntl vs exec in the middle. It will also help in windows porting in follow-up patches becuase there is no fcntl on Windows, but pipe with O_CLOEXEC semantic can be created directly with flags=_O_NOINHERIT.
-
Kirill Smelkov authored
There is no fork on windows and that we do not need to install atfork handler at all. golang/runtime/internal/atomic.cpp(23): fatal error C1083: Cannot open include file: 'pthread.h': No such file or directory
-
Kirill Smelkov authored
- there is no __attribute__ on that compiler - sadly va_start is rejected to work on reference: z:\home\kirr\src\tools\go\pygo-win\BuildTools\vc\tools\msvc\14.35.32215\include\vadefs.h(194): error C2338: static_assert failed: 'va_start argument must not have reference type and must not be parenthesized' golang/fmt.cpp(53): note: see reference to class template instantiation '__vcrt_assert_va_start_is_not_reference<const golang::string &>' being compiled -> change format from string& to be string in sprintf. Probably it should be ok in practice from performance point of view because string contains only pointer to data, not the data itself.
-
Kirill Smelkov authored
- There is no S_IRUSR & friends -> use _S_IREAD & co. - Tthere is no sys_siglist -> kind-of define it ourselves for all documented signals.
-
Kirill Smelkov authored
list_entry, as provided by linux/list.h, uses statement expressions(*) which are not supported by MSVC: #define list_entry(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) golang/runtime/libgolang.cpp(439): error C2059: syntax error: '{' golang/runtime/libgolang.cpp(439): error C2143: syntax error: missing ';' before '{' golang/runtime/libgolang.cpp(439): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int golang/runtime/libgolang.cpp(439): error C2440: 'initializing': cannot convert from 'list_head' to 'int' golang/runtime/libgolang.cpp(439): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called golang/runtime/libgolang.cpp(439): error C2143: syntax error: missing ';' before '*' golang/runtime/libgolang.cpp(439): error C2065: '__mptr': undeclared identifier golang/runtime/libgolang.cpp(439): error C2059: syntax error: ')' -> work it around by defining list_entry with C++ lambda. (*) https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
-