1. 15 Apr, 2024 5 commits
    • Kirill Smelkov's avatar
      time: Redo timers properly · 90143b64
      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)
      90143b64
    • Kirill Smelkov's avatar
      time: Rearrange code a bit · 4a737cf8
      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.
      4a737cf8
    • Kirill Smelkov's avatar
      libgolang: Adjust and require runtimes to provide semaphores with timeout · 67936bdd
      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.
      67936bdd
    • Kirill Smelkov's avatar
      time: test: Add test for stop on func-based Timer · d0b9a2f9
      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.
      d0b9a2f9
    • Kirill Smelkov's avatar
      time: test: Explicitly release Timer/Ticker resources · 6d729f62
      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.
      6d729f62
  2. 17 Feb, 2024 4 commits
    • Kirill Smelkov's avatar
      tox += CPython 3.12 · 6dd420da
      Kirill Smelkov authored
      Tests now pass with that version.
      
      /reviewed-by @jerome
      /reviewed-on !23
      6dd420da
    • Kirill Smelkov's avatar
      gpython: Fix -V when underlying python is not exactly release · f8761f73
      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
      f8761f73
    • Kirill Smelkov's avatar
      golang: tests: Fix for Pytest ≥ 7.4 · 74a9838c
      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
      74a9838c
    • Kirill Smelkov's avatar
      *: Replace imp with importlib on py3 · 68f384a9
      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
      68f384a9
  3. 16 Aug, 2023 1 commit
  4. 03 May, 2023 1 commit
    • Kirill Smelkov's avatar
      pyx.build: v↑ setuptools_dso (2.7 -> 2.8) · 31fc6951
      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
      31fc6951
  5. 27 Apr, 2023 29 commits
    • Kirill Smelkov's avatar
      Windows support · 55d39d4d
      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.
      55d39d4d
    • Kirill Smelkov's avatar
      setup: Add classifiers to indicate Pygolang works on all Linux / macOS and Windows · d1e92fa2
      Kirill Smelkov authored
      Linux and macOS was working before. And we just added Windows support in
      the previous patches.
      d1e92fa2
    • Kirill Smelkov's avatar
      libgolang/gevent: Fix io_read deadlock and io_read/io_write potential data corruption · 3c10a0a3
      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
      3c10a0a3
    • Kirill Smelkov's avatar
      gpython: tests: Don't assume path delimiter is / · d9505256
      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.
      d9505256
    • Kirill Smelkov's avatar
      gpython: tests: Prepare expected repr properly · 161629e6
      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.
      161629e6
    • Kirill Smelkov's avatar
      gpython: Fix gevent activation on Windows · f23a9ef5
      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.
      f23a9ef5
    • Kirill Smelkov's avatar
      gpython: Fix startup on Windows when installed by pip · 377e44cb
      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.
      377e44cb
    • Kirill Smelkov's avatar
      os/signal: tests: Adjust exit code expectation for Windows · 2f632a3e
      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
      2f632a3e
    • Kirill Smelkov's avatar
      os/signal: Adjust signal_test_all.py to support Windows · c1d5b67b
      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.
      c1d5b67b
    • Kirill Smelkov's avatar
      os/signal: tests: use raise instead of kill(self) · ca4af748
      Kirill Smelkov authored
      Because on Windows os.kill unconditionally terminates target process,
      even if own self, instead of sending it any kind of signal.
      ca4af748
    • Kirill Smelkov's avatar
      os/signal: tests: Use SIGTERM/SIGINT on Windows · 75d40910
      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.
      75d40910
    • Kirill Smelkov's avatar
      *.py: Open files in binary mode and decode to UTF-8 explicitly if needed · a5349f5d
      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.
      a5349f5d
    • Kirill Smelkov's avatar
      golang: tests: Spawn subprocess python with $PYTHONIOENCODING set to encoding... · 8d723b34
      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"
      8d723b34
    • Kirill Smelkov's avatar
      golang: tests: Adjust golang_test_defer_excchain.txt-pytest golden to support Windows · ee55e19d
      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.
      ee55e19d
    • Kirill Smelkov's avatar
      golang: tests: Normalize \r\n to \n in doctests on windows · 17c1d2fa
      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
      17c1d2fa
    • Kirill Smelkov's avatar
      golang: tests: Normalize paths to use / delimiter in doctests's got · 06935819
      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
      06935819
    • Kirill Smelkov's avatar
      golang: Prepare path for libgolang.dll before importing _golang · a5ce8175
      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.
      a5ce8175
    • Kirill Smelkov's avatar
      .gitignore += *.lib *.exp · 0b97e0a8
      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.
      0b97e0a8
    • Kirill Smelkov's avatar
      setup: We need to link libpyxruntime DSO to py runtime · fbed01b0
      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.
      fbed01b0
    • Kirill Smelkov's avatar
      libgolang/gevent: Adjust to support Windows · 437bbd56
      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.
      437bbd56
    • Kirill Smelkov's avatar
      os/signal: Use windows-specific code to turn wakeup pipe into nonblocking mode · 45473979
      Kirill Smelkov authored
      There is no fcntl on windows. Named pipes can be set into nonblocking
      mode via SetNamedPipeHandleState(PIPE_NOWAIT).
      45473979
    • Kirill Smelkov's avatar
      os/signal: Internally emulate sigaction via signal · acc6c3f1
      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 .
      acc6c3f1
    • Kirill Smelkov's avatar
      os/signal: Factor call to sys::Sigaction into sys_sigaction internal helper · b15fa818
      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.
      b15fa818
    • Kirill Smelkov's avatar
      internal/syscall: Initial adjust to support MSVC · d3bf3b7d
      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.
      d3bf3b7d
    • Kirill Smelkov's avatar
      internal/syscall: Adjust Pipe to take flags · 63b5c1d9
      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.
      63b5c1d9
    • Kirill Smelkov's avatar
      internal/atomic: Adjust to support MSVC · 534033a5
      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
      534033a5
    • Kirill Smelkov's avatar
      fmt: Adjust to support MSVC · 5098decb
      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.
      5098decb
    • Kirill Smelkov's avatar
      os: Adjust to support MSVC · 22a597a0
      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.
      22a597a0
    • Kirill Smelkov's avatar
      libgolang: Use custom list_entry on MSVC · 715fa60b
      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
      715fa60b